faraday 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d04b0b5407943b47e2c55e93f2102cb1b0b268f2de5505eb5346410aec53ee8
4
- data.tar.gz: 7793d3c324d35bb183192e8ed0f43d1036f8590907491b979a7e5da2d0a09266
3
+ metadata.gz: cc98f5f6c55ba3ed19497f21d89c09658d62712bca56983558b0eb3ce3aec955
4
+ data.tar.gz: 4460cd29073e33bd00d3bc7485d38da232125134d7ad8a12ae65fb775a412ca7
5
5
  SHA512:
6
- metadata.gz: 308fd91172d26082a1de8401221507492a74a2e9a2ac8bad59e4e6f25f3d2b88f8c42258c12be65f0587ffb9d361e1c8f4c889d877dc3a401322283dc3106707
7
- data.tar.gz: c5b84b518095290e8ddbbf660b37f798a54849e604cc6550bf6ba9e6b4792b6a1de3994c41d2df182ec0cdafcdab62c15bafc9dc147b658aabc9e5059f7a2a52
6
+ metadata.gz: 4d86a269474244ea039af4c61fdd4c91f59fad6f1573b82ebe52b33d152f60fdb97ee2763d9419d1c59599a4a949e7ed527f5f45967ef084fcd9047bc2dfc3b0
7
+ data.tar.gz: 3d67a73d391d1c5d1c61aee31fd4ae2af73610b42e3559614c07cfd06faf735c1031200e2eb0138d8bfe507ee9ca97395b61e92ae6c9f7108fc537333960d4c7
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Faraday
6
+ class Request
7
+ # Request middleware that encodes the body as JSON.
8
+ #
9
+ # Processes only requests with matching Content-type or those without a type.
10
+ # If a request doesn't have a type but has a body, it sets the Content-type
11
+ # to JSON MIME-type.
12
+ #
13
+ # Doesn't try to encode bodies that already are in string form.
14
+ class Json < Middleware
15
+ MIME_TYPE = 'application/json'
16
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
17
+
18
+ def on_request(env)
19
+ match_content_type(env) do |data|
20
+ env[:body] = encode(data)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def encode(data)
27
+ ::JSON.generate(data)
28
+ end
29
+
30
+ def match_content_type(env)
31
+ return unless process_request?(env)
32
+
33
+ env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
34
+ yield env[:body] unless env[:body].respond_to?(:to_str)
35
+ end
36
+
37
+ def process_request?(env)
38
+ type = request_type(env)
39
+ body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX))
40
+ end
41
+
42
+ def body?(env)
43
+ (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
44
+ end
45
+
46
+ def request_type(env)
47
+ type = env[:request_headers][CONTENT_TYPE].to_s
48
+ type = type.split(';', 2).first if type.index(';')
49
+ type
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ Faraday::Request.register_middleware(json: Faraday::Request::Json)
@@ -35,8 +35,6 @@ module Faraday
35
35
 
36
36
  register_middleware File.expand_path('request', __dir__),
37
37
  url_encoded: [:UrlEncoded, 'url_encoded'],
38
- multipart: [:Multipart, 'multipart'],
39
- retry: [:Retry, 'retry'],
40
38
  authorization: [:Authorization, 'authorization'],
41
39
  basic_auth: [
42
40
  :BasicAuthentication,
@@ -46,7 +44,8 @@ module Faraday
46
44
  :TokenAuthentication,
47
45
  'token_authentication'
48
46
  ],
49
- instrumentation: [:Instrumentation, 'instrumentation']
47
+ instrumentation: [:Instrumentation, 'instrumentation'],
48
+ json: [:Json, 'json']
50
49
 
51
50
  # @param request_method [String]
52
51
  # @yield [request] for block customization, if block given
@@ -140,11 +139,11 @@ module Faraday
140
139
  # @param serialised [Hash] the serialised object.
141
140
  def marshal_load(serialised)
142
141
  self.http_method = serialised[:http_method]
143
- self.body = serialised[:body]
144
- self.headers = serialised[:headers]
145
- self.path = serialised[:path]
146
- self.params = serialised[:params]
147
- self.options = serialised[:options]
142
+ self.body = serialised[:body]
143
+ self.headers = serialised[:headers]
144
+ self.path = serialised[:path]
145
+ self.params = serialised[:params]
146
+ self.options = serialised[:options]
148
147
  end
149
148
 
150
149
  # @return [Env] the Env for this Request
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Faraday
6
+ class Response
7
+ # Parse response bodies as JSON.
8
+ class Json < Middleware
9
+ def initialize(app = nil, options = {})
10
+ super(app)
11
+ @parser_options = options[:parser_options]
12
+ @content_types = Array(options[:content_type] || /\bjson$/)
13
+ @preserve_raw = options[:preserve_raw]
14
+ end
15
+
16
+ def on_complete(env)
17
+ process_response(env) if parse_response?(env)
18
+ end
19
+
20
+ private
21
+
22
+ def process_response(env)
23
+ env[:raw_body] = env[:body] if @preserve_raw
24
+ env[:body] = parse(env[:body])
25
+ rescue StandardError, SyntaxError => e
26
+ raise Faraday::ParsingError.new(e, env[:response])
27
+ end
28
+
29
+ def parse(body)
30
+ ::JSON.parse(body, @parser_options || {}) unless body.strip.empty?
31
+ end
32
+
33
+ def parse_response?(env)
34
+ process_response_type?(env) &&
35
+ env[:body].respond_to?(:to_str)
36
+ end
37
+
38
+ def process_response_type?(env)
39
+ type = response_type(env)
40
+ @content_types.empty? || @content_types.any? do |pattern|
41
+ pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern
42
+ end
43
+ end
44
+
45
+ def response_type(env)
46
+ type = env[:response_headers][CONTENT_TYPE].to_s
47
+ type = type.split(';', 2).first if type.index(';')
48
+ type
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ Faraday::Response.register_middleware(json: Faraday::Response::Json)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require 'logger'
4
5
  require 'faraday/logging/formatter'
5
6
 
6
7
  module Faraday
@@ -11,10 +12,7 @@ module Faraday
11
12
  class Logger < Middleware
12
13
  def initialize(app, logger = nil, options = {})
13
14
  super(app)
14
- logger ||= begin
15
- require 'logger'
16
- ::Logger.new($stdout)
17
- end
15
+ logger ||= ::Logger.new($stdout)
18
16
  formatter_class = options.delete(:formatter) || Logging::Formatter
19
17
  @formatter = formatter_class.new(logger: logger, options: options)
20
18
  yield @formatter if block_given?
@@ -22,7 +22,8 @@ module Faraday
22
22
 
23
23
  register_middleware File.expand_path('response', __dir__),
24
24
  raise_error: [:RaiseError, 'raise_error'],
25
- logger: [:Logger, 'logger']
25
+ logger: [:Logger, 'logger'],
26
+ json: [:Json, 'json']
26
27
 
27
28
  def initialize(env = nil)
28
29
  @env = Env.from(env) if env
@@ -42,6 +43,7 @@ module Faraday
42
43
  def headers
43
44
  finished? ? env.response_headers : {}
44
45
  end
46
+
45
47
  def_delegator :headers, :[]
46
48
 
47
49
  def body
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '1.9.0'
4
+ VERSION = '1.10.0'
5
5
  end
data/lib/faraday.rb CHANGED
@@ -26,9 +26,11 @@ require 'faraday/response'
26
26
  require 'faraday/error'
27
27
  require 'faraday/request/url_encoded' # needed by multipart
28
28
 
29
- # External Middleware gems
29
+ # External Middleware gems and their aliases
30
30
  require 'faraday/multipart'
31
31
  require 'faraday/retry'
32
+ Faraday::Request::Multipart = Faraday::Multipart::Middleware
33
+ Faraday::Request::Retry = Faraday::Retry::Middleware
32
34
 
33
35
  # External Adapters gems
34
36
  unless defined?(JRUBY_VERSION)
@@ -55,6 +57,8 @@ require 'faraday/rack'
55
57
  # conn.get '/'
56
58
  #
57
59
  module Faraday
60
+ CONTENT_TYPE = 'Content-Type'
61
+
58
62
  class << self
59
63
  # The root path that Faraday is being loaded from.
60
64
  #
@@ -30,11 +30,13 @@ 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
- res = options.instrumenter
34
- rescue NameError => e
35
- expect(e.to_s).to match('ActiveSupport')
36
- else
37
- expect(res).to eq(ActiveSupport::Notifications)
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
38
40
  end
39
41
 
40
42
  it 'instruments with default name' do
@@ -0,0 +1,111 @@
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 'object body with json type' do
77
+ let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') }
78
+
79
+ it 'encodes body' do
80
+ expect(result_body).to eq('{"a":1}')
81
+ end
82
+
83
+ it "doesn't change content type" do
84
+ expect(result_type).to eq('application/json; charset=utf-8')
85
+ end
86
+ end
87
+
88
+ context 'object body with vendor json type' do
89
+ let(:result) { process({ a: 1 }, 'application/vnd.myapp.v1+json; charset=utf-8') }
90
+
91
+ it 'encodes body' do
92
+ expect(result_body).to eq('{"a":1}')
93
+ end
94
+
95
+ it "doesn't change content type" do
96
+ expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8')
97
+ end
98
+ end
99
+
100
+ context 'object body with incompatible type' do
101
+ let(:result) { process({ a: 1 }, 'application/xml; charset=utf-8') }
102
+
103
+ it "doesn't change 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/xml; charset=utf-8')
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,119 @@
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
+ begin
80
+ process('{') { |env| env[:response] = Faraday::Response.new }
81
+ raise 'Parsing should have failed.'
82
+ rescue Faraday::ParsingError => e
83
+ expect(e.response).to be_a(Faraday::Response)
84
+ end
85
+ end
86
+
87
+ context 'HEAD responses' do
88
+ it "nullifies the body if it's only one space" do
89
+ response = process(' ')
90
+ expect(response.body).to be_nil
91
+ end
92
+
93
+ it "nullifies the body if it's two spaces" do
94
+ response = process(' ')
95
+ expect(response.body).to be_nil
96
+ end
97
+ end
98
+
99
+ context 'JSON options' do
100
+ let(:body) { '{"a": 1}' }
101
+ let(:result) { { a: 1 } }
102
+ let(:options) do
103
+ {
104
+ parser_options: {
105
+ symbolize_names: true
106
+ }
107
+ }
108
+ end
109
+
110
+ it 'passes relevant options to JSON parse' do
111
+ expect(::JSON).to receive(:parse)
112
+ .with(body, options[:parser_options])
113
+ .and_return(result)
114
+
115
+ response = process(body)
116
+ expect(response.body).to eq(result)
117
+ end
118
+ end
119
+ end
@@ -41,7 +41,8 @@ class WebmockRackApp
41
41
 
42
42
  def req_headers(env)
43
43
  http_headers = env.select { |k, _| k.start_with?('HTTP_') }
44
- .transform_keys { |k| k[5..] }
44
+ .map { |k, v| [k[5..-1], v] }
45
+ .to_h
45
46
 
46
47
  special_headers = Faraday::Adapter::Rack::SPECIAL_HEADERS
47
48
  http_headers.merge(env.select { |k, _| special_headers.include?(k) })
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.9.0
4
+ version: 1.10.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: 2022-01-06 00:00:00.000000000 Z
13
+ date: 2022-02-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday-em_http
@@ -58,16 +58,16 @@ dependencies:
58
58
  name: faraday-httpclient
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - "<"
61
+ - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '3'
63
+ version: '1.0'
64
64
  type: :runtime
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
- - - "<"
68
+ - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '3'
70
+ version: '1.0'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: faraday-multipart
73
73
  requirement: !ruby/object:Gem::Requirement
@@ -86,44 +86,44 @@ dependencies:
86
86
  name: faraday-net_http
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
- - - "<"
89
+ - - "~>"
90
90
  - !ruby/object:Gem::Version
91
- version: '3'
91
+ version: '1.0'
92
92
  type: :runtime
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - "<"
96
+ - - "~>"
97
97
  - !ruby/object:Gem::Version
98
- version: '3'
98
+ version: '1.0'
99
99
  - !ruby/object:Gem::Dependency
100
100
  name: faraday-net_http_persistent
101
101
  requirement: !ruby/object:Gem::Requirement
102
102
  requirements:
103
- - - "<"
103
+ - - "~>"
104
104
  - !ruby/object:Gem::Version
105
- version: '3'
105
+ version: '1.0'
106
106
  type: :runtime
107
107
  prerelease: false
108
108
  version_requirements: !ruby/object:Gem::Requirement
109
109
  requirements:
110
- - - "<"
110
+ - - "~>"
111
111
  - !ruby/object:Gem::Version
112
- version: '3'
112
+ version: '1.0'
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: faraday-patron
115
115
  requirement: !ruby/object:Gem::Requirement
116
116
  requirements:
117
- - - "<"
117
+ - - "~>"
118
118
  - !ruby/object:Gem::Version
119
- version: '3'
119
+ version: '1.0'
120
120
  type: :runtime
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
- - - "<"
124
+ - - "~>"
125
125
  - !ruby/object:Gem::Version
126
- version: '3'
126
+ version: '1.0'
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: faraday-rack
129
129
  requirement: !ruby/object:Gem::Requirement
@@ -205,9 +205,11 @@ files:
205
205
  - lib/faraday/request/authorization.rb
206
206
  - lib/faraday/request/basic_authentication.rb
207
207
  - lib/faraday/request/instrumentation.rb
208
+ - lib/faraday/request/json.rb
208
209
  - lib/faraday/request/token_authentication.rb
209
210
  - lib/faraday/request/url_encoded.rb
210
211
  - lib/faraday/response.rb
212
+ - lib/faraday/response/json.rb
211
213
  - lib/faraday/response/logger.rb
212
214
  - lib/faraday/response/raise_error.rb
213
215
  - lib/faraday/utils.rb
@@ -239,8 +241,10 @@ files:
239
241
  - spec/faraday/rack_builder_spec.rb
240
242
  - spec/faraday/request/authorization_spec.rb
241
243
  - spec/faraday/request/instrumentation_spec.rb
244
+ - spec/faraday/request/json_spec.rb
242
245
  - spec/faraday/request/url_encoded_spec.rb
243
246
  - spec/faraday/request_spec.rb
247
+ - spec/faraday/response/json_spec.rb
244
248
  - spec/faraday/response/logger_spec.rb
245
249
  - spec/faraday/response/middleware_spec.rb
246
250
  - spec/faraday/response/raise_error_spec.rb
@@ -262,7 +266,7 @@ licenses:
262
266
  - MIT
263
267
  metadata:
264
268
  homepage_uri: https://lostisland.github.io/faraday
265
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.9.0
269
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.10.0
266
270
  source_code_uri: https://github.com/lostisland/faraday
267
271
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
268
272
  post_install_message:
@@ -274,7 +278,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
274
278
  requirements:
275
279
  - - ">="
276
280
  - !ruby/object:Gem::Version
277
- version: '2.6'
281
+ version: '2.4'
278
282
  required_rubygems_version: !ruby/object:Gem::Requirement
279
283
  requirements:
280
284
  - - ">="