faraday 1.5.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05b41e618d412fbb6199856f571098ff152bf81b42839139a2f0527d8f54e6d4
4
- data.tar.gz: 5bf7bfe979404c4b58c6030b2348181bfada78c7eca1e352e1eec271b2c490aa
3
+ metadata.gz: e0b66c2e6b13140f2093c73a7b8dab39dd25690b15b1e5eadaba90a787d2fdf5
4
+ data.tar.gz: af34783d38f124632f2e4edcfd7f6bf2d763bd5d6f36ad8b6de6cac675c9858d
5
5
  SHA512:
6
- metadata.gz: af7ce1cc0c2a34c6733e2cb655ba2b8fe66729300ee40d9ba0a9161d56ba7e4318ffa4fa754f66d80714732bafe895ec2d2eac717ce62115ee3e3e1979830672
7
- data.tar.gz: 3b92a28b2c1172da22f5892d2105e266d3873eb48d07a11090b2b52b5e5bb4a7bacfd8cd15e43b1094ea9df373014d9eb8a1a56226c808440c97489974b249b4
6
+ metadata.gz: dd547129c965998e13208a5a0accab40ddee955f7a001365f2a951c6a0e7d7fefc9e61c5d708188deff9e8e239c991627485e3f07e9686a87a3cd30938a8f05b
7
+ data.tar.gz: 81fdc31d08d17db983ea4a4c68951942c3673d5447096e1f51cd0f4eebac1289348b860a56e064131b65ef59e3ed6f63ce1fc004db435f7fb051460b98190b31
@@ -12,8 +12,8 @@ class Client
12
12
  @conn = conn
13
13
  end
14
14
 
15
- def sushi(jname)
16
- res = @conn.get("/#{jname}")
15
+ def sushi(jname, params: {})
16
+ res = @conn.get("/#{jname}", params)
17
17
  data = JSON.parse(res.body)
18
18
  data['name']
19
19
  end
@@ -62,4 +62,23 @@ RSpec.describe Client do
62
62
  expect { client.sushi('ebi') }.to raise_error(Faraday::ConnectionFailed)
63
63
  stubs.verify_stubbed_calls
64
64
  end
65
+
66
+ context 'When the test stub is run in strict_mode' do
67
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) }
68
+
69
+ it 'verifies the all parameter values are identical' do
70
+ stubs.get('/ebi?abc=123') do
71
+ [
72
+ 200,
73
+ { 'Content-Type': 'application/javascript' },
74
+ '{"name": "shrimp"}'
75
+ ]
76
+ end
77
+
78
+ # uncomment to raise Stubs::NotFound
79
+ # expect(client.sushi('ebi', params: { abc: 123, foo: 'Kappa' })).to eq('shrimp')
80
+ expect(client.sushi('ebi', params: { abc: 123 })).to eq('shrimp')
81
+ stubs.verify_stubbed_calls
82
+ end
83
+ end
65
84
  end
data/lib/faraday.rb CHANGED
@@ -36,6 +36,7 @@ require 'faraday/httpclient'
36
36
  require 'faraday/net_http'
37
37
  require 'faraday/net_http_persistent'
38
38
  require 'faraday/patron'
39
+ require 'faraday/rack'
39
40
 
40
41
  # This is the main namespace for Faraday.
41
42
  #
@@ -11,8 +11,7 @@ module Faraday
11
11
 
12
12
  register_middleware File.expand_path('adapter', __dir__),
13
13
  test: [:Test, 'test'],
14
- typhoeus: [:Typhoeus, 'typhoeus'],
15
- rack: [:Rack, 'rack']
14
+ typhoeus: [:Typhoeus, 'typhoeus']
16
15
 
17
16
  # This module marks an Adapter as supporting parallel requests.
18
17
  module Parallelism
@@ -25,6 +25,9 @@ module Faraday
25
25
  # "showing item: #{meta[:match_data][1]}"
26
26
  # ]
27
27
  # end
28
+ #
29
+ # # You can set strict_mode to exactly match the stubbed requests.
30
+ # stub.strict_mode = true
28
31
  # end
29
32
  # end
30
33
  #
@@ -47,10 +50,11 @@ module Faraday
47
50
  class NotFound < StandardError
48
51
  end
49
52
 
50
- def initialize
53
+ def initialize(strict_mode: false)
51
54
  # { get: [Stub, Stub] }
52
55
  @stack = {}
53
56
  @consumed = {}
57
+ @strict_mode = strict_mode
54
58
  yield(self) if block_given?
55
59
  end
56
60
 
@@ -115,6 +119,17 @@ module Faraday
115
119
  raise failed_stubs.join(' ') unless failed_stubs.empty?
116
120
  end
117
121
 
122
+ # Set strict_mode. If the value is true, this adapter tries to find matched requests strictly,
123
+ # which means that all of a path, parameters, and headers must be the same as an actual request.
124
+ def strict_mode=(value)
125
+ @strict_mode = value
126
+ @stack.each do |_method, stubs|
127
+ stubs.each do |stub|
128
+ stub.strict_mode = value
129
+ end
130
+ end
131
+ end
132
+
118
133
  protected
119
134
 
120
135
  def new_stub(request_method, path, headers = {}, body = nil, &block)
@@ -128,7 +143,8 @@ module Faraday
128
143
  ]
129
144
  end
130
145
 
131
- stub = Stub.new(host, normalized_path, headers, body, block)
146
+ headers = Utils::Headers.new(headers)
147
+ stub = Stub.new(host, normalized_path, headers, body, @strict_mode, block)
132
148
  (@stack[request_method] ||= []) << stub
133
149
  end
134
150
 
@@ -143,9 +159,9 @@ module Faraday
143
159
 
144
160
  # Stub request
145
161
  # rubocop:disable Style/StructInheritance
146
- class Stub < Struct.new(:host, :path, :params, :headers, :body, :block)
162
+ class Stub < Struct.new(:host, :path, :params, :headers, :body, :strict_mode, :block)
147
163
  # rubocop:enable Style/StructInheritance
148
- def initialize(host, full, headers, body, block)
164
+ def initialize(host, full, headers, body, strict_mode, block) # rubocop:disable Metrics/ParameterLists
149
165
  path, query = full.respond_to?(:split) ? full.split('?') : full
150
166
  params =
151
167
  if query
@@ -154,7 +170,7 @@ module Faraday
154
170
  {}
155
171
  end
156
172
 
157
- super(host, path, params, headers, body, block)
173
+ super(host, path, params, headers, body, strict_mode, block)
158
174
  end
159
175
 
160
176
  def matches?(request_host, request_uri, request_headers, request_body)
@@ -184,12 +200,25 @@ module Faraday
184
200
  end
185
201
 
186
202
  def params_match?(request_params)
203
+ if strict_mode
204
+ return Set.new(params) == Set.new(request_params)
205
+ end
206
+
187
207
  params.keys.all? do |key|
188
208
  request_params[key] == params[key]
189
209
  end
190
210
  end
191
211
 
192
212
  def headers_match?(request_headers)
213
+ if strict_mode
214
+ headers_with_user_agent = headers.dup.tap do |hs|
215
+ # NOTE: Set User-Agent in case it's not set when creating Stubs.
216
+ # Users would not want to set Faraday's User-Agent explicitly.
217
+ hs[:user_agent] ||= Connection::USER_AGENT
218
+ end
219
+ return Set.new(headers_with_user_agent) == Set.new(request_headers)
220
+ end
221
+
193
222
  headers.keys.all? do |key|
194
223
  request_headers[key] == headers[key]
195
224
  end
@@ -59,8 +59,7 @@ module Faraday
59
59
  extend AutoloadHelper
60
60
  autoload_all 'faraday/adapter',
61
61
  Typhoeus: 'typhoeus',
62
- Test: 'test',
63
- Rack: 'rack'
62
+ Test: 'test'
64
63
  end
65
64
 
66
65
  # Request represents a single HTTP request for a Faraday adapter to make.
@@ -15,6 +15,7 @@ module Faraday
15
15
  class Connection
16
16
  # A Set of allowed HTTP verbs.
17
17
  METHODS = Set.new %i[get post put delete head patch options trace]
18
+ USER_AGENT = "Faraday v#{VERSION}"
18
19
 
19
20
  # @return [Hash] URI query unencoded key/value pairs.
20
21
  attr_reader :params
@@ -89,7 +90,7 @@ module Faraday
89
90
 
90
91
  yield(self) if block_given?
91
92
 
92
- @headers[:user_agent] ||= "Faraday v#{VERSION}"
93
+ @headers[:user_agent] ||= USER_AGENT
93
94
  end
94
95
 
95
96
  def initialize_proxy(url, options)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '1.5.1'
4
+ VERSION = '1.7.0'
5
5
  end
@@ -257,4 +257,89 @@ RSpec.describe Faraday::Adapter::Test do
257
257
  it { expect { request }.to raise_error described_class::Stubs::NotFound }
258
258
  end
259
259
  end
260
+
261
+ describe 'strict_mode' do
262
+ let(:stubs) do
263
+ described_class::Stubs.new(strict_mode: true) do |stubs|
264
+ stubs.get('/strict?a=12&b=xy', 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello') { [200, {}, 'a'] }
265
+ stubs.get('/with_user_agent?a=12&b=xy', authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent') { [200, {}, 'a'] }
266
+ end
267
+ end
268
+
269
+ context 'when params and headers are exactly set' do
270
+ subject(:request) { connection.get('/strict', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', x_c: 'hello' }) }
271
+
272
+ it { expect(request.status).to eq 200 }
273
+ end
274
+
275
+ context 'when params and headers are exactly set with a custom user agent' do
276
+ subject(:request) { connection.get('/with_user_agent', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent' }) }
277
+
278
+ it { expect(request.status).to eq 200 }
279
+ end
280
+
281
+ shared_examples 'raise NotFound when params do not satisfy the strict check' do |params|
282
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
283
+
284
+ context "with #{params.inspect}" do
285
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
286
+ end
287
+ end
288
+
289
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12' }
290
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { b: 'xy' }
291
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '123', b: 'xy' }
292
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xyz' }
293
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
294
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
295
+
296
+ shared_examples 'raise NotFound when headers do not satisfy the strict check' do |path, headers|
297
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
298
+
299
+ context "with #{headers.inspect}" do
300
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
301
+ end
302
+ end
303
+
304
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck' }
305
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { 'X-C' => 'hello' }
306
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'Hi' }
307
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Basic m_ck', 'x-c': 'hello' }
308
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
309
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck' }
310
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'Unknown' }
311
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
312
+
313
+ context 'when strict_mode is disabled' do
314
+ before do
315
+ stubs.strict_mode = false
316
+ end
317
+
318
+ shared_examples 'does not raise NotFound even when params do not satisfy the strict check' do |params|
319
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
320
+
321
+ context "with #{params.inspect}" do
322
+ it { expect(request.status).to eq 200 }
323
+ end
324
+ end
325
+
326
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy' }
327
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
328
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
329
+
330
+ shared_examples 'does not raise NotFound even when headers do not satisfy the strict check' do |path, headers|
331
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
332
+
333
+ context "with #{headers.inspect}" do
334
+ it { expect(request.status).to eq 200 }
335
+ end
336
+ end
337
+
338
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello' }
339
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
340
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', user_agent: 'Special Agent' }
341
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' }
342
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
343
+ end
344
+ end
260
345
  end
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: 1.5.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "@technoweenie"
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-07-11 00:00:00.000000000 Z
13
+ date: 2021-08-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday-em_http
@@ -110,6 +110,20 @@ dependencies:
110
110
  - - "~>"
111
111
  - !ruby/object:Gem::Version
112
112
  version: '1.0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: faraday-rack
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '1.0'
120
+ type: :runtime
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '1.0'
113
127
  - !ruby/object:Gem::Dependency
114
128
  name: multipart-post
115
129
  requirement: !ruby/object:Gem::Requirement
@@ -158,7 +172,6 @@ files:
158
172
  - examples/client_test.rb
159
173
  - lib/faraday.rb
160
174
  - lib/faraday/adapter.rb
161
- - lib/faraday/adapter/rack.rb
162
175
  - lib/faraday/adapter/test.rb
163
176
  - lib/faraday/adapter/typhoeus.rb
164
177
  - lib/faraday/adapter_registry.rb
@@ -247,7 +260,7 @@ licenses:
247
260
  - MIT
248
261
  metadata:
249
262
  homepage_uri: https://lostisland.github.io/faraday
250
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.5.1
263
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.7.0
251
264
  source_code_uri: https://github.com/lostisland/faraday
252
265
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
253
266
  post_install_message:
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Faraday
4
- class Adapter
5
- # Sends requests to a Rack app.
6
- #
7
- # @example
8
- #
9
- # class MyRackApp
10
- # def call(env)
11
- # [200, {'Content-Type' => 'text/html'}, ["hello world"]]
12
- # end
13
- # end
14
- #
15
- # Faraday.new do |conn|
16
- # conn.adapter :rack, MyRackApp.new
17
- # end
18
- class Rack < Faraday::Adapter
19
- dependency 'rack/test'
20
-
21
- # not prefixed with "HTTP_"
22
- SPECIAL_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze
23
-
24
- def initialize(faraday_app, rack_app)
25
- super(faraday_app)
26
- mock_session = ::Rack::MockSession.new(rack_app)
27
- @session = ::Rack::Test::Session.new(mock_session)
28
- end
29
-
30
- def call(env)
31
- super
32
- rack_env = build_rack_env(env)
33
-
34
- env[:request_headers]&.each do |name, value|
35
- name = name.upcase.tr('-', '_')
36
- name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name
37
- rack_env[name] = value
38
- end
39
-
40
- timeout = request_timeout(:open, env[:request])
41
- timeout ||= request_timeout(:read, env[:request])
42
- response = if timeout
43
- Timer.timeout(timeout, Faraday::TimeoutError) do
44
- execute_request(env, rack_env)
45
- end
46
- else
47
- execute_request(env, rack_env)
48
- end
49
-
50
- if (req = env[:request]).stream_response?
51
- warn "Streaming downloads for #{self.class.name} " \
52
- 'are not yet implemented.'
53
- req.on_data.call(response.body, response.body.bytesize)
54
- end
55
-
56
- save_response(env, response.status, response.body, response.headers)
57
- @app.call env
58
- end
59
-
60
- private
61
-
62
- def execute_request(env, rack_env)
63
- @session.request(env[:url].to_s, rack_env)
64
- end
65
-
66
- def build_rack_env(env)
67
- {
68
- method: env[:method],
69
- input: env[:body].respond_to?(:read) ? env[:body].read : env[:body],
70
- 'rack.url_scheme' => env[:url].scheme
71
- }
72
- end
73
- end
74
- end
75
- end