faraday 0.9.1 → 1.8.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.
Files changed (146) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +360 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +28 -195
  5. data/Rakefile +4 -68
  6. data/examples/client_spec.rb +97 -0
  7. data/examples/client_test.rb +118 -0
  8. data/lib/faraday/adapter/test.rb +158 -58
  9. data/lib/faraday/adapter/typhoeus.rb +7 -115
  10. data/lib/faraday/adapter.rb +79 -20
  11. data/lib/faraday/adapter_registry.rb +30 -0
  12. data/lib/faraday/autoload.rb +39 -36
  13. data/lib/faraday/connection.rb +378 -168
  14. data/lib/faraday/dependency_loader.rb +37 -0
  15. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  16. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  17. data/lib/faraday/error.rb +128 -29
  18. data/lib/faraday/file_part.rb +128 -0
  19. data/lib/faraday/logging/formatter.rb +105 -0
  20. data/lib/faraday/methods.rb +6 -0
  21. data/lib/faraday/middleware.rb +19 -25
  22. data/lib/faraday/middleware_registry.rb +129 -0
  23. data/lib/faraday/options/connection_options.rb +22 -0
  24. data/lib/faraday/options/env.rb +181 -0
  25. data/lib/faraday/options/proxy_options.rb +32 -0
  26. data/lib/faraday/options/request_options.rb +22 -0
  27. data/lib/faraday/options/ssl_options.rb +59 -0
  28. data/lib/faraday/options.rb +61 -193
  29. data/lib/faraday/param_part.rb +53 -0
  30. data/lib/faraday/parameters.rb +4 -180
  31. data/lib/faraday/rack_builder.rb +84 -47
  32. data/lib/faraday/request/authorization.rb +51 -31
  33. data/lib/faraday/request/basic_authentication.rb +14 -7
  34. data/lib/faraday/request/instrumentation.rb +45 -27
  35. data/lib/faraday/request/multipart.rb +88 -45
  36. data/lib/faraday/request/retry.rb +212 -121
  37. data/lib/faraday/request/token_authentication.rb +15 -10
  38. data/lib/faraday/request/url_encoded.rb +43 -23
  39. data/lib/faraday/request.rb +96 -32
  40. data/lib/faraday/response/logger.rb +22 -48
  41. data/lib/faraday/response/raise_error.rb +49 -14
  42. data/lib/faraday/response.rb +33 -25
  43. data/lib/faraday/utils/headers.rb +139 -0
  44. data/lib/faraday/utils/params_hash.rb +61 -0
  45. data/lib/faraday/utils.rb +38 -218
  46. data/lib/faraday/version.rb +5 -0
  47. data/lib/faraday.rb +130 -213
  48. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  49. data/spec/faraday/adapter/em_http_spec.rb +49 -0
  50. data/spec/faraday/adapter/em_synchrony_spec.rb +18 -0
  51. data/spec/faraday/adapter/excon_spec.rb +49 -0
  52. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  53. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  54. data/spec/faraday/adapter/patron_spec.rb +18 -0
  55. data/spec/faraday/adapter/rack_spec.rb +8 -0
  56. data/spec/faraday/adapter/test_spec.rb +377 -0
  57. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  58. data/spec/faraday/adapter_registry_spec.rb +28 -0
  59. data/spec/faraday/adapter_spec.rb +55 -0
  60. data/spec/faraday/composite_read_io_spec.rb +80 -0
  61. data/spec/faraday/connection_spec.rb +736 -0
  62. data/spec/faraday/error_spec.rb +60 -0
  63. data/spec/faraday/middleware_spec.rb +52 -0
  64. data/spec/faraday/options/env_spec.rb +70 -0
  65. data/spec/faraday/options/options_spec.rb +297 -0
  66. data/spec/faraday/options/proxy_options_spec.rb +44 -0
  67. data/spec/faraday/options/request_options_spec.rb +19 -0
  68. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  69. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  70. data/spec/faraday/rack_builder_spec.rb +345 -0
  71. data/spec/faraday/request/authorization_spec.rb +96 -0
  72. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  73. data/spec/faraday/request/multipart_spec.rb +302 -0
  74. data/spec/faraday/request/retry_spec.rb +242 -0
  75. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  76. data/spec/faraday/request_spec.rb +120 -0
  77. data/spec/faraday/response/logger_spec.rb +220 -0
  78. data/spec/faraday/response/middleware_spec.rb +68 -0
  79. data/spec/faraday/response/raise_error_spec.rb +169 -0
  80. data/spec/faraday/response_spec.rb +75 -0
  81. data/spec/faraday/utils/headers_spec.rb +82 -0
  82. data/spec/faraday/utils_spec.rb +56 -0
  83. data/spec/faraday_spec.rb +37 -0
  84. data/spec/spec_helper.rb +132 -0
  85. data/spec/support/disabling_stub.rb +14 -0
  86. data/spec/support/fake_safe_buffer.rb +15 -0
  87. data/spec/support/helper_methods.rb +133 -0
  88. data/spec/support/shared_examples/adapter.rb +105 -0
  89. data/spec/support/shared_examples/params_encoder.rb +18 -0
  90. data/spec/support/shared_examples/request_method.rb +262 -0
  91. data/spec/support/streaming_response_checker.rb +35 -0
  92. data/spec/support/webmock_rack_app.rb +68 -0
  93. metadata +199 -98
  94. data/.document +0 -6
  95. data/CONTRIBUTING.md +0 -36
  96. data/Gemfile +0 -25
  97. data/faraday.gemspec +0 -34
  98. data/lib/faraday/adapter/em_http.rb +0 -237
  99. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
  100. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
  101. data/lib/faraday/adapter/em_synchrony.rb +0 -92
  102. data/lib/faraday/adapter/excon.rb +0 -80
  103. data/lib/faraday/adapter/httpclient.rb +0 -106
  104. data/lib/faraday/adapter/net_http.rb +0 -130
  105. data/lib/faraday/adapter/net_http_persistent.rb +0 -48
  106. data/lib/faraday/adapter/patron.rb +0 -72
  107. data/lib/faraday/adapter/rack.rb +0 -58
  108. data/lib/faraday/upload_io.rb +0 -67
  109. data/script/cached-bundle +0 -46
  110. data/script/console +0 -7
  111. data/script/generate_certs +0 -42
  112. data/script/package +0 -7
  113. data/script/proxy-server +0 -42
  114. data/script/release +0 -17
  115. data/script/s3-put +0 -71
  116. data/script/server +0 -36
  117. data/script/test +0 -172
  118. data/test/adapters/default_test.rb +0 -14
  119. data/test/adapters/em_http_test.rb +0 -20
  120. data/test/adapters/em_synchrony_test.rb +0 -20
  121. data/test/adapters/excon_test.rb +0 -20
  122. data/test/adapters/httpclient_test.rb +0 -21
  123. data/test/adapters/integration.rb +0 -254
  124. data/test/adapters/logger_test.rb +0 -82
  125. data/test/adapters/net_http_persistent_test.rb +0 -20
  126. data/test/adapters/net_http_test.rb +0 -14
  127. data/test/adapters/patron_test.rb +0 -20
  128. data/test/adapters/rack_test.rb +0 -31
  129. data/test/adapters/test_middleware_test.rb +0 -114
  130. data/test/adapters/typhoeus_test.rb +0 -28
  131. data/test/authentication_middleware_test.rb +0 -65
  132. data/test/composite_read_io_test.rb +0 -111
  133. data/test/connection_test.rb +0 -522
  134. data/test/env_test.rb +0 -218
  135. data/test/helper.rb +0 -81
  136. data/test/live_server.rb +0 -67
  137. data/test/middleware/instrumentation_test.rb +0 -88
  138. data/test/middleware/retry_test.rb +0 -177
  139. data/test/middleware_stack_test.rb +0 -173
  140. data/test/multibyte.txt +0 -1
  141. data/test/options_test.rb +0 -252
  142. data/test/parameters_test.rb +0 -64
  143. data/test/request_middleware_test.rb +0 -142
  144. data/test/response_middleware_test.rb +0 -72
  145. data/test/strawberry.rb +0 -2
  146. data/test/utils_test.rb +0 -58
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Request do
4
+ let(:conn) do
5
+ Faraday.new(url: 'http://sushi.com/api',
6
+ headers: { 'Mime-Version' => '1.0' },
7
+ request: { oauth: { consumer_key: 'anonymous' } })
8
+ end
9
+ let(:http_method) { :get }
10
+ let(:block) { nil }
11
+
12
+ subject { conn.build_request(http_method, &block) }
13
+
14
+ context 'when nothing particular is configured' do
15
+ it { expect(subject.http_method).to eq(:get) }
16
+ it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
17
+ end
18
+
19
+ context 'when HTTP method is post' do
20
+ let(:http_method) { :post }
21
+
22
+ it { expect(subject.http_method).to eq(:post) }
23
+ end
24
+
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
+ context 'when setting the url on setup with a URI' do
37
+ let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } }
38
+
39
+ it { expect(subject.path).to eq(URI.parse('foo.json')) }
40
+ it { expect(subject.params).to eq('a' => '1') }
41
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') }
42
+ end
43
+
44
+ context 'when setting the url on setup with a string path and params' do
45
+ let(:block) { proc { |req| req.url 'foo.json', 'a' => 1 } }
46
+
47
+ it { expect(subject.path).to eq('foo.json') }
48
+ it { expect(subject.params).to eq('a' => 1) }
49
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') }
50
+ end
51
+
52
+ context 'when setting the url on setup with a path including params' do
53
+ let(:block) { proc { |req| req.url 'foo.json?b=2&a=1#qqq' } }
54
+
55
+ it { expect(subject.path).to eq('foo.json') }
56
+ it { expect(subject.params).to eq('a' => '1', 'b' => '2') }
57
+ it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1&b=2') }
58
+ end
59
+
60
+ context 'when setting a header on setup with []= syntax' do
61
+ let(:block) { proc { |req| req['Server'] = 'Faraday' } }
62
+ let(:headers) { subject.to_env(conn).request_headers }
63
+
64
+ it { expect(subject.headers['Server']).to eq('Faraday') }
65
+ it { expect(headers['mime-version']).to eq('1.0') }
66
+ it { expect(headers['server']).to eq('Faraday') }
67
+ end
68
+
69
+ context 'when setting the body on setup' do
70
+ let(:block) { proc { |req| req.body = 'hi' } }
71
+
72
+ it { expect(subject.body).to eq('hi') }
73
+ it { expect(subject.to_env(conn).body).to eq('hi') }
74
+ end
75
+
76
+ context 'with global request options set' do
77
+ let(:env_request) { subject.to_env(conn).request }
78
+
79
+ before do
80
+ conn.options.timeout = 3
81
+ conn.options.open_timeout = 5
82
+ conn.ssl.verify = false
83
+ conn.proxy = 'http://proxy.com'
84
+ end
85
+
86
+ it { expect(subject.options.timeout).to eq(3) }
87
+ it { expect(subject.options.open_timeout).to eq(5) }
88
+ it { expect(env_request.timeout).to eq(3) }
89
+ it { expect(env_request.open_timeout).to eq(5) }
90
+
91
+ context 'and per-request options set' do
92
+ let(:block) do
93
+ proc do |req|
94
+ req.options.timeout = 10
95
+ req.options.boundary = 'boo'
96
+ req.options.oauth[:consumer_secret] = 'xyz'
97
+ req.options.context = {
98
+ foo: 'foo',
99
+ bar: 'bar'
100
+ }
101
+ end
102
+ end
103
+
104
+ it { expect(subject.options.timeout).to eq(10) }
105
+ it { expect(subject.options.open_timeout).to eq(5) }
106
+ it { expect(env_request.timeout).to eq(10) }
107
+ it { expect(env_request.open_timeout).to eq(5) }
108
+ it { expect(env_request.boundary).to eq('boo') }
109
+ it { expect(env_request.context).to eq(foo: 'foo', bar: 'bar') }
110
+ it do
111
+ oauth_expected = { consumer_secret: 'xyz', consumer_key: 'anonymous' }
112
+ expect(env_request.oauth).to eq(oauth_expected)
113
+ end
114
+ end
115
+ end
116
+
117
+ it 'supports marshal serialization' do
118
+ expect(Marshal.load(Marshal.dump(subject))).to eq(subject)
119
+ end
120
+ end
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require 'logger'
5
+
6
+ RSpec.describe Faraday::Response::Logger do
7
+ let(:string_io) { StringIO.new }
8
+ let(:logger) { Logger.new(string_io) }
9
+ let(:logger_options) { {} }
10
+ let(:conn) do
11
+ rubbles = ['Barney', 'Betty', 'Bam Bam']
12
+
13
+ Faraday.new do |b|
14
+ b.response :logger, logger, logger_options do |logger|
15
+ logger.filter(/(soylent green is) (.+)/, '\1 tasty')
16
+ logger.filter(/(api_key:).*"(.+)."/, '\1[API_KEY]')
17
+ logger.filter(/(password)=(.+)/, '\1=[HIDDEN]')
18
+ end
19
+ b.adapter :test do |stubs|
20
+ stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] }
21
+ stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] }
22
+ stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] }
23
+ stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] }
24
+ stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] }
25
+ stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] }
26
+ stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] }
27
+ stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] }
28
+ end
29
+ end
30
+ end
31
+
32
+ before do
33
+ logger.level = Logger::DEBUG
34
+ end
35
+
36
+ it 'still returns output' do
37
+ resp = conn.get '/hello', nil, accept: 'text/html'
38
+ expect(resp.body).to eq('hello')
39
+ end
40
+
41
+ context 'without configuration' do
42
+ let(:conn) do
43
+ Faraday.new do |b|
44
+ b.response :logger
45
+ b.adapter :test do |stubs|
46
+ stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] }
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'defaults to stdout' do
52
+ expect(Logger).to receive(:new).with($stdout).and_return(Logger.new(nil))
53
+ conn.get('/hello')
54
+ end
55
+ end
56
+
57
+ context 'with default formatter' do
58
+ let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
59
+
60
+ before { allow(Faraday::Logging::Formatter).to receive(:new).and_return(formatter) }
61
+
62
+ it 'delegates logging to the formatter' do
63
+ expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env))
64
+ expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env))
65
+ conn.get '/hello'
66
+ end
67
+ end
68
+
69
+ context 'with custom formatter' do
70
+ let(:formatter_class) do
71
+ Class.new(Faraday::Logging::Formatter) do
72
+ def request(_env)
73
+ info 'Custom log formatter request'
74
+ end
75
+
76
+ def response(_env)
77
+ info 'Custom log formatter response'
78
+ end
79
+ end
80
+ end
81
+
82
+ let(:logger_options) { { formatter: formatter_class } }
83
+
84
+ it 'logs with custom formatter' do
85
+ conn.get '/hello'
86
+
87
+ expect(string_io.string).to match('Custom log formatter request')
88
+ expect(string_io.string).to match('Custom log formatter response')
89
+ end
90
+ end
91
+
92
+ it 'logs method and url' do
93
+ conn.get '/hello', nil, accept: 'text/html'
94
+ expect(string_io.string).to match('GET http:/hello')
95
+ end
96
+
97
+ it 'logs request headers by default' do
98
+ conn.get '/hello', nil, accept: 'text/html'
99
+ expect(string_io.string).to match(%(Accept: "text/html))
100
+ end
101
+
102
+ it 'logs response headers by default' do
103
+ conn.get '/hello', nil, accept: 'text/html'
104
+ expect(string_io.string).to match(%(Content-Type: "text/html))
105
+ end
106
+
107
+ it 'does not log request body by default' do
108
+ conn.post '/ohai', 'name=Unagi', accept: 'text/html'
109
+ expect(string_io.string).not_to match(%(name=Unagi))
110
+ end
111
+
112
+ it 'does not log response body by default' do
113
+ conn.post '/ohai', 'name=Toro', accept: 'text/html'
114
+ expect(string_io.string).not_to match(%(fred))
115
+ end
116
+
117
+ it 'logs filter headers' do
118
+ conn.headers = { 'api_key' => 'ABC123' }
119
+ conn.get '/filtered_headers', nil, accept: 'text/html'
120
+ expect(string_io.string).to match(%(api_key:))
121
+ expect(string_io.string).to match(%([API_KEY]))
122
+ expect(string_io.string).not_to match(%(ABC123))
123
+ end
124
+
125
+ it 'logs filter url' do
126
+ conn.get '/filtered_url?password=hunter2', nil, accept: 'text/html'
127
+ expect(string_io.string).to match(%([HIDDEN]))
128
+ expect(string_io.string).not_to match(%(hunter2))
129
+ end
130
+
131
+ context 'when not logging request headers' do
132
+ let(:logger_options) { { headers: { request: false } } }
133
+
134
+ it 'does not log request headers if option is false' do
135
+ conn.get '/hello', nil, accept: 'text/html'
136
+ expect(string_io.string).not_to match(%(Accept: "text/html))
137
+ end
138
+ end
139
+
140
+ context 'when not logging response headers' do
141
+ let(:logger_options) { { headers: { response: false } } }
142
+
143
+ it 'does not log response headers if option is false' do
144
+ conn.get '/hello', nil, accept: 'text/html'
145
+ expect(string_io.string).not_to match(%(Content-Type: "text/html))
146
+ end
147
+ end
148
+
149
+ context 'when logging request body' do
150
+ let(:logger_options) { { bodies: { request: true } } }
151
+
152
+ it 'log only request body' do
153
+ conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
154
+ expect(string_io.string).to match(%(name=Tamago))
155
+ expect(string_io.string).not_to match(%(pebbles))
156
+ end
157
+ end
158
+
159
+ context 'when logging response body' do
160
+ let(:logger_options) { { bodies: { response: true } } }
161
+
162
+ it 'log only response body' do
163
+ conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
164
+ expect(string_io.string).to match(%(pebbles))
165
+ expect(string_io.string).not_to match(%(name=Hamachi))
166
+ end
167
+ end
168
+
169
+ context 'when logging request and response bodies' do
170
+ let(:logger_options) { { bodies: true } }
171
+
172
+ it 'log request and response body' do
173
+ conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
174
+ expect(string_io.string).to match(%(name=Ebi))
175
+ expect(string_io.string).to match(%(pebbles))
176
+ end
177
+
178
+ it 'log response body object' do
179
+ conn.get '/rubbles', nil, accept: 'text/html'
180
+ expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
181
+ end
182
+
183
+ it 'logs filter body' do
184
+ conn.get '/filtered_body', nil, accept: 'text/html'
185
+ expect(string_io.string).to match(%(soylent green is))
186
+ expect(string_io.string).to match(%(tasty))
187
+ expect(string_io.string).not_to match(%(people))
188
+ end
189
+ end
190
+
191
+ context 'when using log_level' do
192
+ let(:logger_options) { { bodies: true, log_level: :debug } }
193
+
194
+ it 'logs request/request body on the specified level (debug)' do
195
+ logger.level = Logger::DEBUG
196
+ conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
197
+ expect(string_io.string).to match(%(name=Ebi))
198
+ expect(string_io.string).to match(%(pebbles))
199
+ end
200
+
201
+ it 'logs headers on the debug level' do
202
+ logger.level = Logger::DEBUG
203
+ conn.get '/hello', nil, accept: 'text/html'
204
+ expect(string_io.string).to match(%(Content-Type: "text/html))
205
+ end
206
+
207
+ it 'does not log request/response body on the info level' do
208
+ logger.level = Logger::INFO
209
+ conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
210
+ expect(string_io.string).not_to match(%(name=Ebi))
211
+ expect(string_io.string).not_to match(%(pebbles))
212
+ end
213
+
214
+ it 'does not log headers on the info level' do
215
+ logger.level = Logger::INFO
216
+ conn.get '/hello', nil, accept: 'text/html'
217
+ expect(string_io.string).not_to match(%(Content-Type: "text/html))
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Response::Middleware do
4
+ let(:conn) do
5
+ Faraday.new do |b|
6
+ b.use custom_middleware
7
+ b.adapter :test do |stub|
8
+ stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, '<body></body>'] }
9
+ stub.get('not_modified') { [304, nil, nil] }
10
+ stub.get('no_content') { [204, nil, nil] }
11
+ end
12
+ end
13
+ end
14
+
15
+ context 'with a custom ResponseMiddleware' do
16
+ let(:custom_middleware) do
17
+ Class.new(Faraday::Response::Middleware) do
18
+ def parse(body)
19
+ body.upcase
20
+ end
21
+ end
22
+ end
23
+
24
+ it 'parses the response' do
25
+ expect(conn.get('ok').body).to eq('<BODY></BODY>')
26
+ end
27
+ end
28
+
29
+ context 'with a custom ResponseMiddleware and private parse' do
30
+ let(:custom_middleware) do
31
+ Class.new(Faraday::Response::Middleware) do
32
+ private
33
+
34
+ def parse(body)
35
+ body.upcase
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'parses the response' do
41
+ expect(conn.get('ok').body).to eq('<BODY></BODY>')
42
+ end
43
+ end
44
+
45
+ context 'with a custom ResponseMiddleware but empty response' do
46
+ let(:custom_middleware) do
47
+ Class.new(Faraday::Response::Middleware) do
48
+ def parse(_body)
49
+ raise 'this should not be called'
50
+ end
51
+ end
52
+ end
53
+
54
+ it 'raises exception for 200 responses' do
55
+ expect { conn.get('ok') }.to raise_error(StandardError)
56
+ end
57
+
58
+ it 'doesn\'t call the middleware for 204 responses' do
59
+ expect_any_instance_of(custom_middleware).not_to receive(:parse)
60
+ expect(conn.get('no_content').body).to be_nil
61
+ end
62
+
63
+ it 'doesn\'t call the middleware for 304 responses' do
64
+ expect_any_instance_of(custom_middleware).not_to receive(:parse)
65
+ expect(conn.get('not_modified').body).to be_nil
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Response::RaiseError do
4
+ let(:conn) do
5
+ Faraday.new do |b|
6
+ b.response :raise_error
7
+ b.adapter :test do |stub|
8
+ stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, '<body></body>'] }
9
+ stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
10
+ stub.get('unauthorized') { [401, { 'X-Reason' => 'because' }, 'keep looking'] }
11
+ stub.get('forbidden') { [403, { 'X-Reason' => 'because' }, 'keep looking'] }
12
+ stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
13
+ stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
14
+ stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
15
+ stub.get('unprocessable-entity') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
16
+ stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
17
+ stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] }
18
+ stub.get('server-error') { [500, { 'X-Error' => 'bailout' }, 'fail'] }
19
+ end
20
+ end
21
+ end
22
+
23
+ it 'raises no exception for 200 responses' do
24
+ expect { conn.get('ok') }.not_to raise_error
25
+ end
26
+
27
+ it 'raises Faraday::BadRequestError for 400 responses' do
28
+ expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
29
+ expect(ex.message).to eq('the server responded with status 400')
30
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
31
+ expect(ex.response[:status]).to eq(400)
32
+ expect(ex.response_status).to eq(400)
33
+ expect(ex.response_body).to eq('keep looking')
34
+ expect(ex.response_headers['X-Reason']).to eq('because')
35
+ end
36
+ end
37
+
38
+ it 'raises Faraday::UnauthorizedError for 401 responses' do
39
+ expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
40
+ expect(ex.message).to eq('the server responded with status 401')
41
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
42
+ expect(ex.response[:status]).to eq(401)
43
+ expect(ex.response_status).to eq(401)
44
+ expect(ex.response_body).to eq('keep looking')
45
+ expect(ex.response_headers['X-Reason']).to eq('because')
46
+ end
47
+ end
48
+
49
+ it 'raises Faraday::ForbiddenError for 403 responses' do
50
+ expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
51
+ expect(ex.message).to eq('the server responded with status 403')
52
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
53
+ expect(ex.response[:status]).to eq(403)
54
+ expect(ex.response_status).to eq(403)
55
+ expect(ex.response_body).to eq('keep looking')
56
+ expect(ex.response_headers['X-Reason']).to eq('because')
57
+ end
58
+ end
59
+
60
+ it 'raises Faraday::ResourceNotFound for 404 responses' do
61
+ expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
62
+ expect(ex.message).to eq('the server responded with status 404')
63
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
64
+ expect(ex.response[:status]).to eq(404)
65
+ expect(ex.response_status).to eq(404)
66
+ expect(ex.response_body).to eq('keep looking')
67
+ expect(ex.response_headers['X-Reason']).to eq('because')
68
+ end
69
+ end
70
+
71
+ it 'raises Faraday::ProxyAuthError for 407 responses' do
72
+ expect { conn.get('proxy-error') }.to raise_error(Faraday::ProxyAuthError) do |ex|
73
+ expect(ex.message).to eq('407 "Proxy Authentication Required"')
74
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
75
+ expect(ex.response[:status]).to eq(407)
76
+ expect(ex.response_status).to eq(407)
77
+ expect(ex.response_body).to eq('keep looking')
78
+ expect(ex.response_headers['X-Reason']).to eq('because')
79
+ end
80
+ end
81
+
82
+ it 'raises Faraday::ConflictError for 409 responses' do
83
+ expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
84
+ expect(ex.message).to eq('the server responded with status 409')
85
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
86
+ expect(ex.response[:status]).to eq(409)
87
+ expect(ex.response_status).to eq(409)
88
+ expect(ex.response_body).to eq('keep looking')
89
+ expect(ex.response_headers['X-Reason']).to eq('because')
90
+ end
91
+ end
92
+
93
+ it 'raises Faraday::UnprocessableEntityError for 422 responses' do
94
+ expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
95
+ expect(ex.message).to eq('the server responded with status 422')
96
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
97
+ expect(ex.response[:status]).to eq(422)
98
+ expect(ex.response_status).to eq(422)
99
+ expect(ex.response_body).to eq('keep looking')
100
+ expect(ex.response_headers['X-Reason']).to eq('because')
101
+ end
102
+ end
103
+
104
+ it 'raises Faraday::NilStatusError for nil status in response' do
105
+ expect { conn.get('nil-status') }.to raise_error(Faraday::NilStatusError) do |ex|
106
+ expect(ex.message).to eq('http status could not be derived from the server response')
107
+ expect(ex.response[:headers]['X-Reason']).to eq('nil')
108
+ expect(ex.response[:status]).to be_nil
109
+ expect(ex.response_status).to be_nil
110
+ expect(ex.response_body).to eq('fail')
111
+ expect(ex.response_headers['X-Reason']).to eq('nil')
112
+ end
113
+ end
114
+
115
+ it 'raises Faraday::ClientError for other 4xx responses' do
116
+ expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
117
+ expect(ex.message).to eq('the server responded with status 499')
118
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
119
+ expect(ex.response[:status]).to eq(499)
120
+ expect(ex.response_status).to eq(499)
121
+ expect(ex.response_body).to eq('keep looking')
122
+ expect(ex.response_headers['X-Reason']).to eq('because')
123
+ end
124
+ end
125
+
126
+ it 'raises Faraday::ServerError for 500 responses' do
127
+ expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
128
+ expect(ex.message).to eq('the server responded with status 500')
129
+ expect(ex.response[:headers]['X-Error']).to eq('bailout')
130
+ expect(ex.response[:status]).to eq(500)
131
+ expect(ex.response_status).to eq(500)
132
+ expect(ex.response_body).to eq('fail')
133
+ expect(ex.response_headers['X-Error']).to eq('bailout')
134
+ end
135
+ end
136
+
137
+ describe 'request info' do
138
+ let(:conn) do
139
+ Faraday.new do |b|
140
+ b.response :raise_error
141
+ b.adapter :test do |stub|
142
+ stub.post('request?full=true', request_body, request_headers) do
143
+ [400, { 'X-Reason' => 'because' }, 'keep looking']
144
+ end
145
+ end
146
+ end
147
+ end
148
+ let(:request_body) { JSON.generate({ 'item' => 'sth' }) }
149
+ let(:request_headers) { { 'Authorization' => 'Basic 123' } }
150
+
151
+ subject(:perform_request) do
152
+ conn.post 'request' do |req|
153
+ req.headers['Authorization'] = 'Basic 123'
154
+ req.params[:full] = true
155
+ req.body = request_body
156
+ end
157
+ end
158
+
159
+ it 'returns the request info in the exception' do
160
+ expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
161
+ expect(ex.response[:request][:method]).to eq(:post)
162
+ expect(ex.response[:request][:url_path]).to eq('/request')
163
+ expect(ex.response[:request][:params]).to eq({ 'full' => 'true' })
164
+ expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers))
165
+ expect(ex.response[:request][:body]).to eq(request_body)
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Response do
4
+ subject { Faraday::Response.new(env) }
5
+
6
+ let(:env) do
7
+ Faraday::Env.from(status: 404, body: 'yikes',
8
+ response_headers: { 'Content-Type' => 'text/plain' })
9
+ end
10
+
11
+ it { expect(subject.finished?).to be_truthy }
12
+ it { expect { subject.finish({}) }.to raise_error(RuntimeError) }
13
+ it { expect(subject.success?).to be_falsey }
14
+ it { expect(subject.status).to eq(404) }
15
+ it { expect(subject.body).to eq('yikes') }
16
+ it { expect(subject.headers['Content-Type']).to eq('text/plain') }
17
+ it { expect(subject['content-type']).to eq('text/plain') }
18
+
19
+ describe '#apply_request' do
20
+ before { subject.apply_request(body: 'a=b', method: :post) }
21
+
22
+ it { expect(subject.body).to eq('yikes') }
23
+ it { expect(subject.env[:method]).to eq(:post) }
24
+ end
25
+
26
+ describe '#to_hash' do
27
+ let(:hash) { subject.to_hash }
28
+
29
+ it { expect(hash).to be_a(Hash) }
30
+ it { expect(hash[:status]).to eq(subject.status) }
31
+ it { expect(hash[:response_headers]).to eq(subject.headers) }
32
+ it { expect(hash[:body]).to eq(subject.body) }
33
+ end
34
+
35
+ describe 'marshal serialization support' do
36
+ subject { Faraday::Response.new }
37
+ let(:loaded) { Marshal.load(Marshal.dump(subject)) }
38
+
39
+ before do
40
+ subject.on_complete {}
41
+ subject.finish(env.merge(params: 'moo'))
42
+ end
43
+
44
+ it { expect(loaded.env[:params]).to be_nil }
45
+ it { expect(loaded.env[:body]).to eq(env[:body]) }
46
+ it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) }
47
+ it { expect(loaded.env[:status]).to eq(env[:status]) }
48
+ end
49
+
50
+ describe '#on_complete' do
51
+ subject { Faraday::Response.new }
52
+
53
+ it 'parse body on finish' do
54
+ subject.on_complete { |env| env[:body] = env[:body].upcase }
55
+ subject.finish(env)
56
+
57
+ expect(subject.body).to eq('YIKES')
58
+ end
59
+
60
+ it 'can access response body in on_complete callback' do
61
+ subject.on_complete { |env| env[:body] = subject.body.upcase }
62
+ subject.finish(env)
63
+
64
+ expect(subject.body).to eq('YIKES')
65
+ end
66
+
67
+ it 'can access response body in on_complete callback' do
68
+ callback_env = nil
69
+ subject.on_complete { |env| callback_env = env }
70
+ subject.finish({})
71
+
72
+ expect(subject.env).to eq(callback_env)
73
+ end
74
+ end
75
+ end