faraday 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +3 -4
  4. data/examples/client_spec.rb +1 -1
  5. data/lib/faraday/adapter/em_http.rb +3 -2
  6. data/lib/faraday/adapter/excon.rb +2 -2
  7. data/lib/faraday/adapter/httpclient.rb +2 -1
  8. data/lib/faraday/adapter/net_http.rb +18 -8
  9. data/lib/faraday/adapter/typhoeus.rb +1 -1
  10. data/lib/faraday/adapter.rb +1 -0
  11. data/lib/faraday/adapter_registry.rb +3 -1
  12. data/lib/faraday/autoload.rb +1 -1
  13. data/lib/faraday/connection.rb +3 -3
  14. data/lib/faraday/dependency_loader.rb +3 -1
  15. data/lib/faraday/encoders/flat_params_encoder.rb +9 -2
  16. data/lib/faraday/encoders/nested_params_encoder.rb +7 -2
  17. data/lib/faraday/error.rb +8 -0
  18. data/lib/faraday/options.rb +4 -8
  19. data/lib/faraday/rack_builder.rb +13 -12
  20. data/lib/faraday/request/authorization.rb +3 -1
  21. data/lib/faraday/request/multipart.rb +10 -3
  22. data/lib/faraday/request/url_encoded.rb +3 -1
  23. data/lib/faraday/request.rb +20 -10
  24. data/lib/faraday/response/raise_error.rb +12 -1
  25. data/lib/faraday/response.rb +4 -1
  26. data/lib/faraday/utils/headers.rb +2 -2
  27. data/lib/faraday/utils.rb +11 -3
  28. data/lib/faraday.rb +2 -2
  29. data/spec/faraday/adapter/em_http_spec.rb +1 -1
  30. data/spec/faraday/adapter/em_synchrony_spec.rb +1 -1
  31. data/spec/faraday/adapter/patron_spec.rb +1 -1
  32. data/spec/faraday/adapter/test_spec.rb +260 -0
  33. data/spec/faraday/params_encoders/flat_spec.rb +8 -0
  34. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  35. data/spec/faraday/rack_builder_spec.rb +150 -1
  36. data/spec/faraday/request/authorization_spec.rb +2 -2
  37. data/spec/faraday/request/multipart_spec.rb +41 -13
  38. data/spec/faraday/request/url_encoded_spec.rb +13 -0
  39. data/spec/faraday/request_spec.rb +16 -5
  40. data/spec/faraday/response/middleware_spec.rb +16 -0
  41. data/spec/faraday/response/raise_error_spec.rb +33 -0
  42. data/spec/support/shared_examples/request_method.rb +3 -3
  43. metadata +21 -6
  44. data/UPGRADING.md +0 -55
@@ -31,4 +31,12 @@ RSpec.describe Faraday::FlatParamsEncoder do
31
31
  params = { a: [] }
32
32
  expect(subject.encode(params)).to eq('a=')
33
33
  end
34
+
35
+ it 'encodes unsorted when asked' do
36
+ params = { b: false, a: true }
37
+ expect(subject.encode(params)).to eq('a=true&b=false')
38
+ Faraday::FlatParamsEncoder.sort_params = false
39
+ expect(subject.encode(params)).to eq('b=false&a=true')
40
+ Faraday::FlatParamsEncoder.sort_params = true
41
+ end
34
42
  end
@@ -94,6 +94,14 @@ RSpec.describe Faraday::NestedParamsEncoder do
94
94
  expect(subject.encode(params)).to eq('a%5B%5D=true&a%5B%5D=false')
95
95
  end
96
96
 
97
+ it 'encodes unsorted when asked' do
98
+ params = { b: false, a: true }
99
+ expect(subject.encode(params)).to eq('a=true&b=false')
100
+ Faraday::NestedParamsEncoder.sort_params = false
101
+ expect(subject.encode(params)).to eq('b=false&a=true')
102
+ Faraday::NestedParamsEncoder.sort_params = true
103
+ end
104
+
97
105
  shared_examples 'a wrong decoding' do
98
106
  it do
99
107
  expect { subject.decode(query) }.to raise_error(TypeError) do |e|
@@ -189,8 +189,157 @@ RSpec.describe Faraday::RackBuilder do
189
189
 
190
190
  it 'raises an error while making a request' do
191
191
  expect { conn.get('/') }.to raise_error(RuntimeError) do |err|
192
- expect(err.message).to eq('missing dependency for Broken: cannot load such file -- zomg/i_dont/exist')
192
+ expect(err.message).to match(%r{missing dependency for Broken: .+ -- zomg/i_dont/exist})
193
193
  end
194
194
  end
195
195
  end
196
+
197
+ context 'when middleware is added with named arguments' do
198
+ let(:conn) { Faraday::Connection.new {} }
199
+
200
+ let(:dog_middleware) do
201
+ Class.new(Faraday::Middleware) do
202
+ attr_accessor :name
203
+
204
+ def initialize(app, name:)
205
+ super(app)
206
+ @name = name
207
+ end
208
+ end
209
+ end
210
+ let(:dog) do
211
+ subject.handlers.find { |handler| handler == dog_middleware }.build
212
+ end
213
+
214
+ it 'adds a handler to construct middleware with options passed to use' do
215
+ subject.use dog_middleware, name: 'Rex'
216
+ expect { dog }.to_not output(
217
+ /warning: Using the last argument as keyword parameters is deprecated/
218
+ ).to_stderr
219
+ expect(dog.name).to eq('Rex')
220
+ end
221
+ end
222
+
223
+ context 'when a request adapter is added with named arguments' do
224
+ let(:conn) { Faraday::Connection.new {} }
225
+
226
+ let(:cat_request) do
227
+ Class.new(Faraday::Middleware) do
228
+ attr_accessor :name
229
+
230
+ def initialize(app, name:)
231
+ super(app)
232
+ @name = name
233
+ end
234
+ end
235
+ end
236
+ let(:cat) do
237
+ subject.handlers.find { |handler| handler == cat_request }.build
238
+ end
239
+
240
+ it 'adds a handler to construct request adapter with options passed to request' do
241
+ Faraday::Request.register_middleware cat_request: cat_request
242
+ subject.request :cat_request, name: 'Felix'
243
+ expect { cat }.to_not output(
244
+ /warning: Using the last argument as keyword parameters is deprecated/
245
+ ).to_stderr
246
+ expect(cat.name).to eq('Felix')
247
+ end
248
+ end
249
+
250
+ context 'when a response adapter is added with named arguments' do
251
+ let(:conn) { Faraday::Connection.new {} }
252
+
253
+ let(:fish_response) do
254
+ Class.new(Faraday::Response::Middleware) do
255
+ attr_accessor :name
256
+
257
+ def initialize(app, name:)
258
+ super(app)
259
+ @name = name
260
+ end
261
+ end
262
+ end
263
+ let(:fish) do
264
+ subject.handlers.find { |handler| handler == fish_response }.build
265
+ end
266
+
267
+ it 'adds a handler to construct response adapter with options passed to response' do
268
+ Faraday::Response.register_middleware fish_response: fish_response
269
+ subject.response :fish_response, name: 'Bubbles'
270
+ expect { fish }.to_not output(
271
+ /warning: Using the last argument as keyword parameters is deprecated/
272
+ ).to_stderr
273
+ expect(fish.name).to eq('Bubbles')
274
+ end
275
+ end
276
+
277
+ context 'when a plain adapter is added with named arguments' do
278
+ let(:conn) { Faraday::Connection.new {} }
279
+
280
+ let(:rabbit_adapter) do
281
+ Class.new(Faraday::Adapter) do
282
+ attr_accessor :name
283
+
284
+ def initialize(app, name:)
285
+ super(app)
286
+ @name = name
287
+ end
288
+ end
289
+ end
290
+ let(:rabbit) do
291
+ subject.adapter.build
292
+ end
293
+
294
+ it 'adds a handler to construct adapter with options passed to adapter' do
295
+ Faraday::Adapter.register_middleware rabbit_adapter: rabbit_adapter
296
+ subject.adapter :rabbit_adapter, name: 'Thumper'
297
+ expect { rabbit }.to_not output(
298
+ /warning: Using the last argument as keyword parameters is deprecated/
299
+ ).to_stderr
300
+ expect(rabbit.name).to eq('Thumper')
301
+ end
302
+ end
303
+
304
+ context 'when handlers are directly added or updated' do
305
+ let(:conn) { Faraday::Connection.new {} }
306
+
307
+ let(:rock_handler) do
308
+ Class.new do
309
+ attr_accessor :name
310
+
311
+ def initialize(_app, name:)
312
+ @name = name
313
+ end
314
+ end
315
+ end
316
+ let(:rock) do
317
+ subject.handlers.find { |handler| handler == rock_handler }.build
318
+ end
319
+
320
+ it 'adds a handler to construct adapter with options passed to insert' do
321
+ subject.insert 0, rock_handler, name: 'Stony'
322
+ expect { rock }.to_not output(
323
+ /warning: Using the last argument as keyword parameters is deprecated/
324
+ ).to_stderr
325
+ expect(rock.name).to eq('Stony')
326
+ end
327
+
328
+ it 'adds a handler with options passed to insert_after' do
329
+ subject.insert_after 0, rock_handler, name: 'Rocky'
330
+ expect { rock }.to_not output(
331
+ /warning: Using the last argument as keyword parameters is deprecated/
332
+ ).to_stderr
333
+ expect(rock.name).to eq('Rocky')
334
+ end
335
+
336
+ it 'adds a handler with options passed to swap' do
337
+ subject.insert 0, rock_handler, name: 'Flint'
338
+ subject.swap 0, rock_handler, name: 'Chert'
339
+ expect { rock }.to_not output(
340
+ /warning: Using the last argument as keyword parameters is deprecated/
341
+ ).to_stderr
342
+ expect(rock.name).to eq('Chert')
343
+ end
344
+ end
196
345
  end
@@ -56,7 +56,7 @@ RSpec.describe Faraday::Request::Authorization do
56
56
  end
57
57
 
58
58
  context 'when other values are provided' do
59
- let(:auth_config) { ['baz', foo: 42] }
59
+ let(:auth_config) { ['baz', { foo: 42 }] }
60
60
 
61
61
  it { expect(response.body).to match(/^Token /) }
62
62
  it { expect(response.body).to match(/token="baz"/) }
@@ -78,7 +78,7 @@ RSpec.describe Faraday::Request::Authorization do
78
78
  end
79
79
 
80
80
  context 'when passed a string and a hash' do
81
- let(:auth_config) { ['baz', foo: 42] }
81
+ let(:auth_config) { ['baz', { foo: 42 }] }
82
82
 
83
83
  it { expect(response.body).to eq('baz foo="42"') }
84
84
 
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Faraday::Request::Multipart do
4
+ let(:options) { {} }
4
5
  let(:conn) do
5
6
  Faraday.new do |b|
6
- b.request :multipart
7
+ b.request :multipart, options
7
8
  b.request :url_encoded
8
9
  b.adapter :test do |stub|
9
10
  stub.post('/echo') do |env|
@@ -54,9 +55,10 @@ RSpec.describe Faraday::Request::Multipart do
54
55
  part_bc, body_bc = result.part('b[c]')
55
56
  expect(part_bc).to_not be_nil
56
57
  expect(part_bc.filename).to eq('multipart_spec.rb')
57
- expect(part_bc.headers['content-disposition']).to eq(
58
- 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
59
- )
58
+ expect(part_bc.headers['content-disposition'])
59
+ .to eq(
60
+ 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
61
+ )
60
62
  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
61
63
  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
62
64
  expect(body_bc).to eq(File.read(__FILE__))
@@ -135,9 +137,10 @@ RSpec.describe Faraday::Request::Multipart do
135
137
  part_bc, body_bc = result.part('b[][c]')
136
138
  expect(part_bc).to_not be_nil
137
139
  expect(part_bc.filename).to eq('multipart_spec.rb')
138
- expect(part_bc.headers['content-disposition']).to eq(
139
- 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
140
- )
140
+ expect(part_bc.headers['content-disposition'])
141
+ .to eq(
142
+ 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
143
+ )
141
144
  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
142
145
  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
143
146
  expect(body_bc).to eq(File.read(__FILE__))
@@ -177,9 +180,10 @@ RSpec.describe Faraday::Request::Multipart do
177
180
  part_bc, body_bc = result.part('b[c]')
178
181
  expect(part_bc).to_not be_nil
179
182
  expect(part_bc.filename).to eq('multipart_spec.rb')
180
- expect(part_bc.headers['content-disposition']).to eq(
181
- 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
182
- )
183
+ expect(part_bc.headers['content-disposition'])
184
+ .to eq(
185
+ 'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
186
+ )
183
187
  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
184
188
  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
185
189
  expect(body_bc).to eq(File.read(__FILE__))
@@ -258,9 +262,10 @@ RSpec.describe Faraday::Request::Multipart do
258
262
  part_bc, body_bc = result.part('b[][c]')
259
263
  expect(part_bc).to_not be_nil
260
264
  expect(part_bc.filename).to eq('multipart_spec.rb')
261
- expect(part_bc.headers['content-disposition']).to eq(
262
- 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
263
- )
265
+ expect(part_bc.headers['content-disposition'])
266
+ .to eq(
267
+ 'form-data; name="b[][c]"; filename="multipart_spec.rb"'
268
+ )
264
269
  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
265
270
  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
266
271
  expect(body_bc).to eq(File.read(__FILE__))
@@ -271,4 +276,27 @@ RSpec.describe Faraday::Request::Multipart do
271
276
  expect(body_bd).to eq('2')
272
277
  end
273
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::UploadIO.new(io, 'application/pdf'),
288
+ Faraday::UploadIO.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
274
302
  end
@@ -67,4 +67,17 @@ RSpec.describe Faraday::Request::UrlEncoded do
67
67
  response = conn.post('/echo', 'a' => { 'b' => { 'c' => ['d'] } })
68
68
  expect(response.body).to eq('a%5Bb%5D%5Bc%5D%5B%5D=d')
69
69
  end
70
+
71
+ context 'customising default_space_encoding' do
72
+ around do |example|
73
+ Faraday::Utils.default_space_encoding = '%20'
74
+ example.run
75
+ Faraday::Utils.default_space_encoding = nil
76
+ end
77
+
78
+ it 'uses the custom character to encode spaces' do
79
+ response = conn.post('/echo', str: 'apple banana')
80
+ expect(response.body).to eq('str=apple%20banana')
81
+ end
82
+ end
70
83
  end
@@ -6,20 +6,31 @@ RSpec.describe Faraday::Request do
6
6
  headers: { 'Mime-Version' => '1.0' },
7
7
  request: { oauth: { consumer_key: 'anonymous' } })
8
8
  end
9
- let(:method) { :get }
9
+ let(:http_method) { :get }
10
10
  let(:block) { nil }
11
11
 
12
- subject { conn.build_request(method, &block) }
12
+ subject { conn.build_request(http_method, &block) }
13
13
 
14
14
  context 'when nothing particular is configured' do
15
- it { expect(subject.method).to eq(:get) }
15
+ it { expect(subject.http_method).to eq(:get) }
16
16
  it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
17
17
  end
18
18
 
19
- context 'when method is post' do
20
- let(:method) { :post }
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
21
30
 
22
31
  it { expect(subject.method).to eq(:post) }
32
+
33
+ it { expect { subject.method }.to output(expected_warning).to_stderr }
23
34
  end
24
35
 
25
36
  context 'when setting the url on setup with a URI' do
@@ -26,6 +26,22 @@ RSpec.describe Faraday::Response::Middleware do
26
26
  end
27
27
  end
28
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
+
29
45
  context 'with a custom ResponseMiddleware but empty response' do
30
46
  let(:custom_middleware) do
31
47
  Class.new(Faraday::Response::Middleware) do
@@ -103,4 +103,37 @@ RSpec.describe Faraday::Response::RaiseError do
103
103
  expect(ex.response[:status]).to eq(500)
104
104
  end
105
105
  end
106
+
107
+ describe 'request info' do
108
+ let(:conn) do
109
+ Faraday.new do |b|
110
+ b.response :raise_error
111
+ b.adapter :test do |stub|
112
+ stub.post('request?full=true', request_body, request_headers) do
113
+ [400, { 'X-Reason' => 'because' }, 'keep looking']
114
+ end
115
+ end
116
+ end
117
+ end
118
+ let(:request_body) { JSON.generate({ 'item' => 'sth' }) }
119
+ let(:request_headers) { { 'Authorization' => 'Basic 123' } }
120
+
121
+ subject(:perform_request) do
122
+ conn.post 'request' do |req|
123
+ req.headers['Authorization'] = 'Basic 123'
124
+ req.params[:full] = true
125
+ req.body = request_body
126
+ end
127
+ end
128
+
129
+ it 'returns the request info in the exception' do
130
+ expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
131
+ expect(ex.response[:request][:method]).to eq(:post)
132
+ expect(ex.response[:request][:url_path]).to eq('/request')
133
+ expect(ex.response[:request][:params]).to eq({ 'full' => 'true' })
134
+ expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers))
135
+ expect(ex.response[:request][:body]).to eq(request_body)
136
+ end
137
+ end
138
+ end
106
139
  end
@@ -13,8 +13,8 @@ shared_examples 'a request method' do |http_method|
13
13
  end
14
14
 
15
15
  it 'handles headers with multiple values' do
16
- request_stub.to_return(headers: { 'Set-Cookie' => 'one, two' })
17
- expect(response.headers['set-cookie']).to eq('one, two')
16
+ request_stub.to_return(headers: { 'Set-Cookie' => 'name=value' })
17
+ expect(response.headers['set-cookie']).to eq('name=value')
18
18
  end
19
19
 
20
20
  it 'retrieves the response headers' do
@@ -119,7 +119,7 @@ shared_examples 'a request method' do |http_method|
119
119
  request_stub.with(headers: { 'Content-Type' => %r{\Amultipart/form-data} }) do |request|
120
120
  # WebMock does not support matching body for multipart/form-data requests yet :(
121
121
  # https://github.com/bblimke/webmock/issues/623
122
- request.body =~ /RubyMultipartPost/
122
+ request.body.include?('RubyMultipartPost')
123
123
  end
124
124
  conn.public_send(http_method, '/', payload)
125
125
  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.0.0
4
+ version: 1.1.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: 2020-01-01 00:00:00.000000000 Z
13
+ date: 2020-10-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: multipart-post
@@ -32,6 +32,20 @@ dependencies:
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '3'
35
+ - !ruby/object:Gem::Dependency
36
+ name: ruby2_keywords
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
35
49
  description:
36
50
  email: technoweenie@gmail.com
37
51
  executables: []
@@ -42,7 +56,6 @@ files:
42
56
  - LICENSE.md
43
57
  - README.md
44
58
  - Rakefile
45
- - UPGRADING.md
46
59
  - examples/client_spec.rb
47
60
  - examples/client_test.rb
48
61
  - lib/faraday.rb
@@ -102,6 +115,7 @@ files:
102
115
  - spec/faraday/adapter/net_http_spec.rb
103
116
  - spec/faraday/adapter/patron_spec.rb
104
117
  - spec/faraday/adapter/rack_spec.rb
118
+ - spec/faraday/adapter/test_spec.rb
105
119
  - spec/faraday/adapter/typhoeus_spec.rb
106
120
  - spec/faraday/adapter_registry_spec.rb
107
121
  - spec/faraday/adapter_spec.rb
@@ -143,25 +157,26 @@ licenses:
143
157
  - MIT
144
158
  metadata:
145
159
  homepage_uri: https://lostisland.github.io/faraday
146
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.0.0
160
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.1.0
147
161
  source_code_uri: https://github.com/lostisland/faraday
148
162
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
149
163
  post_install_message:
150
164
  rdoc_options: []
151
165
  require_paths:
152
166
  - lib
167
+ - spec/external_adapters
153
168
  required_ruby_version: !ruby/object:Gem::Requirement
154
169
  requirements:
155
170
  - - ">="
156
171
  - !ruby/object:Gem::Version
157
- version: '2.3'
172
+ version: '2.4'
158
173
  required_rubygems_version: !ruby/object:Gem::Requirement
159
174
  requirements:
160
175
  - - ">="
161
176
  - !ruby/object:Gem::Version
162
177
  version: '0'
163
178
  requirements: []
164
- rubygems_version: 3.1.2
179
+ rubygems_version: 3.0.3
165
180
  signing_key:
166
181
  specification_version: 4
167
182
  summary: HTTP/REST API client library.