faraday 1.10.6 → 2.0.0.alpha.pre.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -1
  3. data/README.md +16 -9
  4. data/examples/client_test.rb +1 -1
  5. data/lib/faraday/adapter/test.rb +2 -0
  6. data/lib/faraday/adapter.rb +0 -5
  7. data/lib/faraday/connection.rb +3 -86
  8. data/lib/faraday/encoders/nested_params_encoder.rb +3 -12
  9. data/lib/faraday/error.rb +7 -0
  10. data/lib/faraday/file_part.rb +122 -0
  11. data/lib/faraday/logging/formatter.rb +1 -0
  12. data/lib/faraday/middleware.rb +0 -1
  13. data/lib/faraday/middleware_registry.rb +15 -79
  14. data/lib/faraday/options.rb +3 -3
  15. data/lib/faraday/param_part.rb +53 -0
  16. data/lib/faraday/rack_builder.rb +1 -1
  17. data/lib/faraday/request/authorization.rb +26 -40
  18. data/lib/faraday/request/instrumentation.rb +2 -0
  19. data/lib/faraday/request/multipart.rb +108 -0
  20. data/lib/faraday/request/retry.rb +241 -0
  21. data/lib/faraday/request/url_encoded.rb +2 -0
  22. data/lib/faraday/request.rb +8 -24
  23. data/lib/faraday/response/json.rb +4 -4
  24. data/lib/faraday/response/logger.rb +2 -0
  25. data/lib/faraday/response/raise_error.rb +9 -1
  26. data/lib/faraday/response.rb +7 -20
  27. data/lib/faraday/utils/headers.rb +1 -1
  28. data/lib/faraday/utils.rb +9 -4
  29. data/lib/faraday/version.rb +1 -1
  30. data/lib/faraday.rb +6 -45
  31. data/spec/faraday/connection_spec.rb +78 -97
  32. data/spec/faraday/options/env_spec.rb +2 -2
  33. data/spec/faraday/params_encoders/nested_spec.rb +0 -30
  34. data/spec/faraday/rack_builder_spec.rb +5 -43
  35. data/spec/faraday/request/authorization_spec.rb +14 -36
  36. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  37. data/spec/faraday/request/multipart_spec.rb +302 -0
  38. data/spec/faraday/request/retry_spec.rb +254 -0
  39. data/spec/faraday/request_spec.rb +0 -11
  40. data/spec/faraday/response/json_spec.rb +4 -6
  41. data/spec/faraday/response/raise_error_spec.rb +7 -4
  42. data/spec/faraday/utils_spec.rb +1 -1
  43. data/spec/faraday_spec.rb +4 -8
  44. data/spec/spec_helper.rb +0 -2
  45. data/spec/support/fake_safe_buffer.rb +1 -1
  46. data/spec/support/shared_examples/request_method.rb +5 -5
  47. metadata +25 -156
  48. data/lib/faraday/adapter/typhoeus.rb +0 -15
  49. data/lib/faraday/autoload.rb +0 -89
  50. data/lib/faraday/dependency_loader.rb +0 -39
  51. data/lib/faraday/deprecate.rb +0 -110
  52. data/lib/faraday/request/basic_authentication.rb +0 -20
  53. data/lib/faraday/request/token_authentication.rb +0 -20
  54. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  55. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  56. data/spec/faraday/adapter/excon_spec.rb +0 -49
  57. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  58. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  59. data/spec/faraday/adapter/patron_spec.rb +0 -18
  60. data/spec/faraday/adapter/rack_spec.rb +0 -8
  61. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  62. data/spec/faraday/deprecate_spec.rb +0 -147
  63. data/spec/faraday/response/middleware_spec.rb +0 -68
  64. data/spec/support/webmock_rack_app.rb +0 -68
@@ -12,13 +12,11 @@ RSpec.describe Faraday::RackBuilder do
12
12
 
13
13
  class Apple < Handler
14
14
  end
15
+
15
16
  class Orange < Handler
16
17
  end
17
- class Banana < Handler
18
- end
19
18
 
20
- class Broken < Faraday::Middleware
21
- dependency 'zomg/i_dont/exist'
19
+ class Banana < Handler
22
20
  end
23
21
 
24
22
  subject { conn.builder }
@@ -127,24 +125,6 @@ RSpec.describe Faraday::RackBuilder do
127
125
  subject.use(:apple)
128
126
  expect(subject.handlers).to eq([Apple])
129
127
  end
130
-
131
- it 'allows to register with symbol' do
132
- Faraday::Middleware.register_middleware(apple: :Apple)
133
- subject.use(:apple)
134
- expect(subject.handlers).to eq([Apple])
135
- end
136
-
137
- it 'allows to register with string' do
138
- Faraday::Middleware.register_middleware(apple: 'Apple')
139
- subject.use(:apple)
140
- expect(subject.handlers).to eq([Apple])
141
- end
142
-
143
- it 'allows to register with Proc' do
144
- Faraday::Middleware.register_middleware(apple: -> { Apple })
145
- subject.use(:apple)
146
- expect(subject.handlers).to eq([Apple])
147
- end
148
128
  end
149
129
 
150
130
  context 'when having two handlers' do
@@ -176,24 +156,6 @@ RSpec.describe Faraday::RackBuilder do
176
156
  end
177
157
  end
178
158
 
179
- context 'when having a handler with broken dependency' do
180
- let(:conn) do
181
- Faraday::Connection.new do |builder|
182
- builder.adapter :test do |stub|
183
- stub.get('/') { |_| [200, {}, ''] }
184
- end
185
- end
186
- end
187
-
188
- before { subject.use(Broken) }
189
-
190
- it 'raises an error while making a request' do
191
- expect { conn.get('/') }.to raise_error(RuntimeError) do |err|
192
- expect(err.message).to match(%r{missing dependency for Broken: .+ -- zomg/i_dont/exist})
193
- end
194
- end
195
- end
196
-
197
159
  context 'when middleware is added with named arguments' do
198
160
  let(:conn) { Faraday::Connection.new {} }
199
161
 
@@ -220,7 +182,7 @@ RSpec.describe Faraday::RackBuilder do
220
182
  end
221
183
  end
222
184
 
223
- context 'when a request adapter is added with named arguments' do
185
+ context 'when a middleware is added with named arguments' do
224
186
  let(:conn) { Faraday::Connection.new {} }
225
187
 
226
188
  let(:cat_request) do
@@ -247,11 +209,11 @@ RSpec.describe Faraday::RackBuilder do
247
209
  end
248
210
  end
249
211
 
250
- context 'when a response adapter is added with named arguments' do
212
+ context 'when a middleware is added with named arguments' do
251
213
  let(:conn) { Faraday::Connection.new {} }
252
214
 
253
215
  let(:fish_response) do
254
- Class.new(Faraday::Response::Middleware) do
216
+ Class.new(Faraday::Middleware) do
255
217
  attr_accessor :name
256
218
 
257
219
  def initialize(app, name:)
@@ -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: 'Token token="bar"') }
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('Token token="bar"')
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) { :basic_auth }
28
+ let(:auth_type) { :basic }
29
29
 
30
30
  context 'when passed correct params' do
31
31
  let(:auth_config) { %w[aladdin opensesame] }
@@ -44,51 +44,29 @@ RSpec.describe Faraday::Request::Authorization do
44
44
  end
45
45
  end
46
46
 
47
- describe 'token_auth' do
48
- let(:auth_type) { :token_auth }
49
-
50
- context 'when passed correct params' do
51
- let(:auth_config) { 'quux' }
52
-
53
- it { expect(response.body).to eq('Token token="quux"') }
54
-
55
- include_examples 'does not interfere with existing authentication'
56
- end
57
-
58
- context 'when other values are provided' do
59
- let(:auth_config) { ['baz', { foo: 42 }] }
60
-
61
- it { expect(response.body).to match(/^Token /) }
62
- it { expect(response.body).to match(/token="baz"/) }
63
- it { expect(response.body).to match(/foo="42"/) }
64
-
65
- include_examples 'does not interfere with existing authentication'
66
- end
67
- end
68
-
69
47
  describe 'authorization' do
70
- let(:auth_type) { :authorization }
48
+ let(:auth_type) { :Bearer }
71
49
 
72
- context 'when passed two strings' do
73
- let(:auth_config) { ['custom', 'abc def'] }
50
+ context 'when passed a string' do
51
+ let(:auth_config) { ['custom'] }
74
52
 
75
- it { expect(response.body).to eq('custom abc def') }
53
+ it { expect(response.body).to eq('Bearer custom') }
76
54
 
77
55
  include_examples 'does not interfere with existing authentication'
78
56
  end
79
57
 
80
- context 'when passed a string and a hash' do
81
- let(:auth_config) { ['baz', { foo: 42 }] }
58
+ context 'when passed a proc' do
59
+ let(:auth_config) { [-> { 'custom_from_proc' }] }
82
60
 
83
- it { expect(response.body).to eq('baz foo="42"') }
61
+ it { expect(response.body).to eq('Bearer custom_from_proc') }
84
62
 
85
63
  include_examples 'does not interfere with existing authentication'
86
64
  end
87
65
 
88
- context 'when passed a string and a proc' do
89
- let(:auth_config) { ['Bearer', -> { 'custom_from_proc' }] }
66
+ context 'when passed too many arguments' do
67
+ let(:auth_config) { %w[baz foo] }
90
68
 
91
- it { expect(response.body).to eq('Bearer custom_from_proc') }
69
+ it { expect { response }.to raise_error(ArgumentError) }
92
70
 
93
71
  include_examples 'does not interfere with existing authentication'
94
72
  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
- begin
34
- res = options.instrumenter
35
- rescue NameError => e
36
- expect(e.to_s).to match('ActiveSupport')
37
- else
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,302 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Request::Multipart do
4
+ let(:options) { {} }
5
+ let(:conn) do
6
+ Faraday.new do |b|
7
+ b.request :multipart, options
8
+ b.request :url_encoded
9
+ b.adapter :test do |stub|
10
+ stub.post('/echo') do |env|
11
+ posted_as = env[:request_headers]['Content-Type']
12
+ expect(env[:body]).to be_a_kind_of(Faraday::CompositeReadIO)
13
+ [200, { 'Content-Type' => posted_as }, env[:body].read]
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ shared_examples 'a multipart request' do
20
+ it 'generates a unique boundary for each request' do
21
+ response1 = conn.post('/echo', payload)
22
+ response2 = conn.post('/echo', payload)
23
+
24
+ b1 = parse_multipart_boundary(response1.headers['Content-Type'])
25
+ b2 = parse_multipart_boundary(response2.headers['Content-Type'])
26
+ expect(b1).to_not eq(b2)
27
+ end
28
+ end
29
+
30
+ context 'FilePart: when multipart objects in param' do
31
+ let(:payload) do
32
+ {
33
+ a: 1,
34
+ b: {
35
+ c: Faraday::FilePart.new(__FILE__, 'text/x-ruby', nil,
36
+ 'Content-Disposition' => 'form-data; foo=1'),
37
+ d: 2
38
+ }
39
+ }
40
+ end
41
+ it_behaves_like 'a multipart request'
42
+
43
+ it 'forms a multipart request' do
44
+ response = conn.post('/echo', payload)
45
+
46
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
47
+ result = parse_multipart(boundary, response.body)
48
+ expect(result[:errors]).to be_empty
49
+
50
+ part_a, body_a = result.part('a')
51
+ expect(part_a).to_not be_nil
52
+ expect(part_a.filename).to be_nil
53
+ expect(body_a).to eq('1')
54
+
55
+ part_bc, body_bc = result.part('b[c]')
56
+ expect(part_bc).to_not be_nil
57
+ expect(part_bc.filename).to eq('multipart_spec.rb')
58
+ expect(part_bc.headers['content-disposition'])
59
+ .to eq(
60
+ 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
61
+ )
62
+ expect(part_bc.headers['content-type']).to eq('text/x-ruby')
63
+ expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
64
+ expect(body_bc).to eq(File.read(__FILE__))
65
+
66
+ part_bd, body_bd = result.part('b[d]')
67
+ expect(part_bd).to_not be_nil
68
+ expect(part_bd.filename).to be_nil
69
+ expect(body_bd).to eq('2')
70
+ end
71
+ end
72
+
73
+ context 'FilePart: when providing json and IO content in the same payload' do
74
+ let(:io) { StringIO.new('io-content') }
75
+ let(:json) do
76
+ {
77
+ b: 1,
78
+ c: 2
79
+ }.to_json
80
+ end
81
+
82
+ let(:payload) do
83
+ {
84
+ json: Faraday::ParamPart.new(json, 'application/json'),
85
+ io: Faraday::FilePart.new(io, 'application/pdf')
86
+ }
87
+ end
88
+
89
+ it_behaves_like 'a multipart request'
90
+
91
+ it 'forms a multipart request' do
92
+ response = conn.post('/echo', payload)
93
+
94
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
95
+ result = parse_multipart(boundary, response.body)
96
+ expect(result[:errors]).to be_empty
97
+
98
+ part_json, body_json = result.part('json')
99
+ expect(part_json).to_not be_nil
100
+ expect(part_json.mime).to eq('application/json')
101
+ expect(part_json.filename).to be_nil
102
+ expect(body_json).to eq(json)
103
+
104
+ part_io, body_io = result.part('io')
105
+ expect(part_io).to_not be_nil
106
+ expect(part_io.mime).to eq('application/pdf')
107
+ expect(part_io.filename).to eq('local.path')
108
+ expect(body_io).to eq(io.string)
109
+ end
110
+ end
111
+
112
+ context 'FilePart: when multipart objects in array param' do
113
+ let(:payload) do
114
+ {
115
+ a: 1,
116
+ b: [{
117
+ c: Faraday::FilePart.new(__FILE__, 'text/x-ruby'),
118
+ d: 2
119
+ }]
120
+ }
121
+ end
122
+
123
+ it_behaves_like 'a multipart request'
124
+
125
+ it 'forms a multipart request' do
126
+ response = conn.post('/echo', payload)
127
+
128
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
129
+ result = parse_multipart(boundary, response.body)
130
+ expect(result[:errors]).to be_empty
131
+
132
+ part_a, body_a = result.part('a')
133
+ expect(part_a).to_not be_nil
134
+ expect(part_a.filename).to be_nil
135
+ expect(body_a).to eq('1')
136
+
137
+ part_bc, body_bc = result.part('b[][c]')
138
+ expect(part_bc).to_not be_nil
139
+ expect(part_bc.filename).to eq('multipart_spec.rb')
140
+ expect(part_bc.headers['content-disposition'])
141
+ .to eq(
142
+ 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
143
+ )
144
+ expect(part_bc.headers['content-type']).to eq('text/x-ruby')
145
+ expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
146
+ expect(body_bc).to eq(File.read(__FILE__))
147
+
148
+ part_bd, body_bd = result.part('b[][d]')
149
+ expect(part_bd).to_not be_nil
150
+ expect(part_bd.filename).to be_nil
151
+ expect(body_bd).to eq('2')
152
+ end
153
+ end
154
+
155
+ context 'UploadIO: when multipart objects in param' do
156
+ let(:payload) do
157
+ {
158
+ a: 1,
159
+ b: {
160
+ c: Faraday::FilePart.new(__FILE__, 'text/x-ruby', nil,
161
+ 'Content-Disposition' => 'form-data; foo=1'),
162
+ d: 2
163
+ }
164
+ }
165
+ end
166
+ it_behaves_like 'a multipart request'
167
+
168
+ it 'forms a multipart request' do
169
+ response = conn.post('/echo', payload)
170
+
171
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
172
+ result = parse_multipart(boundary, response.body)
173
+ expect(result[:errors]).to be_empty
174
+
175
+ part_a, body_a = result.part('a')
176
+ expect(part_a).to_not be_nil
177
+ expect(part_a.filename).to be_nil
178
+ expect(body_a).to eq('1')
179
+
180
+ part_bc, body_bc = result.part('b[c]')
181
+ expect(part_bc).to_not be_nil
182
+ expect(part_bc.filename).to eq('multipart_spec.rb')
183
+ expect(part_bc.headers['content-disposition'])
184
+ .to eq(
185
+ 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
186
+ )
187
+ expect(part_bc.headers['content-type']).to eq('text/x-ruby')
188
+ expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
189
+ expect(body_bc).to eq(File.read(__FILE__))
190
+
191
+ part_bd, body_bd = result.part('b[d]')
192
+ expect(part_bd).to_not be_nil
193
+ expect(part_bd.filename).to be_nil
194
+ expect(body_bd).to eq('2')
195
+ end
196
+ end
197
+
198
+ context 'UploadIO: when providing json and IO content in the same payload' do
199
+ let(:io) { StringIO.new('io-content') }
200
+ let(:json) do
201
+ {
202
+ b: 1,
203
+ c: 2
204
+ }.to_json
205
+ end
206
+
207
+ let(:payload) do
208
+ {
209
+ json: Faraday::ParamPart.new(json, 'application/json'),
210
+ io: Faraday::FilePart.new(io, 'application/pdf')
211
+ }
212
+ end
213
+
214
+ it_behaves_like 'a multipart request'
215
+
216
+ it 'forms a multipart request' do
217
+ response = conn.post('/echo', payload)
218
+
219
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
220
+ result = parse_multipart(boundary, response.body)
221
+ expect(result[:errors]).to be_empty
222
+
223
+ part_json, body_json = result.part('json')
224
+ expect(part_json).to_not be_nil
225
+ expect(part_json.mime).to eq('application/json')
226
+ expect(part_json.filename).to be_nil
227
+ expect(body_json).to eq(json)
228
+
229
+ part_io, body_io = result.part('io')
230
+ expect(part_io).to_not be_nil
231
+ expect(part_io.mime).to eq('application/pdf')
232
+ expect(part_io.filename).to eq('local.path')
233
+ expect(body_io).to eq(io.string)
234
+ end
235
+ end
236
+
237
+ context 'UploadIO: when multipart objects in array param' do
238
+ let(:payload) do
239
+ {
240
+ a: 1,
241
+ b: [{
242
+ c: Faraday::FilePart.new(__FILE__, 'text/x-ruby'),
243
+ d: 2
244
+ }]
245
+ }
246
+ end
247
+
248
+ it_behaves_like 'a multipart request'
249
+
250
+ it 'forms a multipart request' do
251
+ response = conn.post('/echo', payload)
252
+
253
+ boundary = parse_multipart_boundary(response.headers['Content-Type'])
254
+ result = parse_multipart(boundary, response.body)
255
+ expect(result[:errors]).to be_empty
256
+
257
+ part_a, body_a = result.part('a')
258
+ expect(part_a).to_not be_nil
259
+ expect(part_a.filename).to be_nil
260
+ expect(body_a).to eq('1')
261
+
262
+ part_bc, body_bc = result.part('b[][c]')
263
+ expect(part_bc).to_not be_nil
264
+ expect(part_bc.filename).to eq('multipart_spec.rb')
265
+ expect(part_bc.headers['content-disposition'])
266
+ .to eq(
267
+ 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
268
+ )
269
+ expect(part_bc.headers['content-type']).to eq('text/x-ruby')
270
+ expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
271
+ expect(body_bc).to eq(File.read(__FILE__))
272
+
273
+ part_bd, body_bd = result.part('b[][d]')
274
+ expect(part_bd).to_not be_nil
275
+ expect(part_bd.filename).to be_nil
276
+ expect(body_bd).to eq('2')
277
+ end
278
+ end
279
+
280
+ context 'when passing flat_encode=true option' do
281
+ let(:options) { { flat_encode: true } }
282
+ let(:io) { StringIO.new('io-content') }
283
+ let(:payload) do
284
+ {
285
+ a: 1,
286
+ b: [
287
+ Faraday::FilePart.new(io, 'application/pdf'),
288
+ Faraday::FilePart.new(io, 'application/pdf')
289
+ ]
290
+ }
291
+ end
292
+
293
+ it_behaves_like 'a multipart request'
294
+
295
+ it 'encode params using flat encoder' do
296
+ response = conn.post('/echo', payload)
297
+
298
+ expect(response.body).to include('name="b"')
299
+ expect(response.body).not_to include('name="b[]"')
300
+ end
301
+ end
302
+ end