chimera_http_client 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/README.markdown +23 -8
- data/TODO.markdown +3 -3
- data/lib/chimera_http_client/base.rb +13 -8
- data/lib/chimera_http_client/connection.rb +6 -1
- data/lib/chimera_http_client/deserializer.rb +31 -0
- data/lib/chimera_http_client/error.rb +5 -6
- data/lib/chimera_http_client/queue.rb +6 -1
- data/lib/chimera_http_client/request.rb +16 -15
- data/lib/chimera_http_client/response.rb +5 -5
- data/lib/chimera_http_client/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01402cd235d413d16acc9adb72267b7a244898b5913d14d07e039ad6b1bd830b
|
4
|
+
data.tar.gz: 3909459cb620e544da67ee5864f39ab7ea51a6c33b6dd1b3fcddda5a56ec4dd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 655127ba58b327eb2bc9f2ff6342ad3c93bd84c9cfa08fc262a4628005643e2562ae76ee68378331a92edb11cd9fb91fe0cf942f45d4ce911f176bfae8392c02
|
7
|
+
data.tar.gz: b3b282c08dc802b187ee95e974deb996ee833d3350f86ef4d6c89818ef1b182bedee2f043aaad50f392157829559b18b579480d7d50beef154617bb744e6cf9e
|
data/.travis.yml
CHANGED
data/README.markdown
CHANGED
@@ -14,7 +14,13 @@ The `chimera_http_client` gem is wrapping the **libcurl** wrapper [**Typhoeus**]
|
|
14
14
|
|
15
15
|
The only other runtime dependency is Ruby's latest code loader [**zeitwerk**](https://github.com/fxn/zeitwerk) which is also part of Rails 6.
|
16
16
|
|
17
|
-
|
17
|
+
### Ruby version
|
18
|
+
|
19
|
+
| Chimera version | Ruby version |
|
20
|
+
|:----------------|:-------------|
|
21
|
+
| >= 1.1 | >= 2.5 |
|
22
|
+
| = 1.0 | >= 2.4 |
|
23
|
+
| <= 0.5 | >= 2.1 |
|
18
24
|
|
19
25
|
### ENV variables
|
20
26
|
|
@@ -81,6 +87,19 @@ The optional parameters are:
|
|
81
87
|
* `user_agent` - if you would like your calls to identify with a specific user agent
|
82
88
|
* `verbose` - the default is `false`, set it to true while debugging issues
|
83
89
|
* `cache` - an instance of your cache solution, can be overwritten in any request
|
90
|
+
* `deserializers` - custom methods to deserialize the response body, below more details
|
91
|
+
|
92
|
+
##### Custom deserializers
|
93
|
+
|
94
|
+
In case the API you are connecting to does not return JSON, you can pass custom deserializers to `Connection.new` or `Queue.new`:
|
95
|
+
|
96
|
+
deserializers: { error: your_error_deserializer, response: your_response_deserializer }
|
97
|
+
|
98
|
+
A Deserializer has to be an object on which the method `call` with the parameter `body` can be called:
|
99
|
+
|
100
|
+
custom_deserializer.call(body)
|
101
|
+
|
102
|
+
where `body` is the response body (in the default case a JSON object). The class `Deserializer` contains the default objects that are used. They might help you creating your own. Don't forget to make requests with another header than the default `"Content-Type" => "application/json"`, when the API you connect to does not support JSON.
|
84
103
|
|
85
104
|
### Request methods
|
86
105
|
|
@@ -263,10 +282,6 @@ Usually it does not have to be used directly. It is the class that executes the
|
|
263
282
|
|
264
283
|
The `body` which it receives from the `Connection` class has to be in the in the (serialized) form in which the endpoint expects it. Usually this means you have to pass a JSON string to the `body` (it will **not** be serialized automatically).
|
265
284
|
|
266
|
-
> Upcoming feature:
|
267
|
-
>
|
268
|
-
> It will be expanded by a `.queue` method, that will queue (sic) calls and run them in parallel and not run every call directly, like the `.run` method does.
|
269
|
-
|
270
285
|
## The Response class
|
271
286
|
|
272
287
|
The `ChimeraHttpClient::Response` objects have the following interface:
|
@@ -276,9 +291,9 @@ The `ChimeraHttpClient::Response` objects have the following interface:
|
|
276
291
|
* time (for monitoring)
|
277
292
|
* response (the full response object, including the request)
|
278
293
|
* error? (returns false)
|
279
|
-
* parsed_body (returns the result of
|
294
|
+
* parsed_body (returns the result of `deserializer[:response].call(body)`)
|
280
295
|
|
281
|
-
If your API does not use JSON, but a different format e.g. XML, you can
|
296
|
+
If your API does not use JSON, but a different format e.g. XML, you can pass a custom deserializer to the Connection.
|
282
297
|
|
283
298
|
## Error classes
|
284
299
|
|
@@ -358,7 +373,7 @@ To inspect the requests waiting for execution, call `queue.queued_requests`.
|
|
358
373
|
|
359
374
|
Add this line to your application's Gemfile:
|
360
375
|
|
361
|
-
gem 'chimera_http_client', '~>
|
376
|
+
gem 'chimera_http_client', '~> 1.1'
|
362
377
|
|
363
378
|
And then execute:
|
364
379
|
|
data/TODO.markdown
CHANGED
@@ -17,9 +17,9 @@ _none known_
|
|
17
17
|
|
18
18
|
### Custom De-serializer
|
19
19
|
|
20
|
-
* [
|
21
|
-
* [
|
22
|
-
* [
|
20
|
+
* [x] ~~allow to pass custom deserializer~~
|
21
|
+
* [x] ~~use custom deserializer in #parsed_body instead of default JSON parsing~~
|
22
|
+
* [x] ~~add example to README~~
|
23
23
|
|
24
24
|
### Queueing / running in parallel
|
25
25
|
|
@@ -2,17 +2,18 @@ module ChimeraHttpClient
|
|
2
2
|
class Base
|
3
3
|
USER_AGENT = "ChimeraHttpClient (by mediafinger)".freeze
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
fail(ChimeraHttpClient::ParameterMissingError, "base_url expected, but not given") if base_url.nil?
|
5
|
+
def initialize(options = {})
|
6
|
+
fail(ChimeraHttpClient::ParameterMissingError, "base_url expected, but not given") if options[:base_url].nil?
|
7
7
|
|
8
|
-
@base_url = base_url
|
9
|
-
@
|
10
|
-
@
|
8
|
+
@base_url = options.fetch(:base_url)
|
9
|
+
@deserializer = default_deserializer.merge(options.fetch(:deserializer, {}))
|
10
|
+
@logger = options[:logger]
|
11
|
+
@timeout = options[:timeout]
|
11
12
|
|
12
|
-
Typhoeus::Config.cache = cache
|
13
|
+
Typhoeus::Config.cache = options[:cache]
|
13
14
|
Typhoeus::Config.memoize = false # hydra setting, prevents a possible memory leak
|
14
|
-
Typhoeus::Config.user_agent = user_agent
|
15
|
-
Typhoeus::Config.verbose = verbose
|
15
|
+
Typhoeus::Config.user_agent = options.fetch(:user_agent, USER_AGENT)
|
16
|
+
Typhoeus::Config.verbose = options.fetch(:verbose, false)
|
16
17
|
end
|
17
18
|
|
18
19
|
private
|
@@ -42,6 +43,10 @@ module ChimeraHttpClient
|
|
42
43
|
{ "Content-Type" => "application/json" }
|
43
44
|
end
|
44
45
|
|
46
|
+
def default_deserializer
|
47
|
+
{ error: ::ChimeraHttpClient::Deserializer.json_error, response: ::ChimeraHttpClient::Deserializer.json_response }
|
48
|
+
end
|
49
|
+
|
45
50
|
# Build URL out of @base_url and endpoint given as String or Array, while trimming redundant "/"
|
46
51
|
def url(endpoint)
|
47
52
|
trimmed_endpoint = Array(endpoint).map { |e| trim(e) }
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This two JSON deserializers are the default ones
|
2
|
+
#
|
3
|
+
# To use custom deserializers, pass them as param to Connection.new or Queue.new:
|
4
|
+
# `deserializers: { error: your_error_deserializer, response: your_response_deserializer }`
|
5
|
+
# (you might be able to use the same for both cases)
|
6
|
+
#
|
7
|
+
# a Deserializer has to be an object on which the method `call` with the parameter `body` can be called:
|
8
|
+
# `custom_deserializer.call(body)`
|
9
|
+
# where `body` is the response body (in the default case a JSON object)
|
10
|
+
#
|
11
|
+
module ChimeraHttpClient
|
12
|
+
class Deserializer
|
13
|
+
class << self
|
14
|
+
def json_error
|
15
|
+
proc do |body|
|
16
|
+
JSON.parse(body)
|
17
|
+
rescue JSON::ParserError
|
18
|
+
{ "non_json_body" => body }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def json_response
|
23
|
+
proc do |body|
|
24
|
+
JSON.parse(body)
|
25
|
+
rescue JSON::ParserError => e
|
26
|
+
raise ::ChimeraHttpClient::JsonParserError, "Could not parse body as JSON: #{body}, error: #{e.message}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
module ChimeraHttpClient
|
2
2
|
class Error < StandardError
|
3
|
-
attr_reader :body, :code, :time, :response
|
3
|
+
attr_reader :body, :code, :time, :response, :deserializer
|
4
4
|
alias message body
|
5
5
|
|
6
|
-
def initialize(response)
|
6
|
+
def initialize(response, options = {})
|
7
7
|
@body = response.body
|
8
8
|
@code = response.code
|
9
9
|
@time = response.options&.fetch(:total_time, nil)
|
10
10
|
@response = response # contains the request
|
11
|
-
|
11
|
+
|
12
|
+
@deserializer = options[:deserializer][:error]
|
12
13
|
end
|
13
14
|
|
14
15
|
def error?
|
@@ -16,9 +17,7 @@ module ChimeraHttpClient
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def parsed_body
|
19
|
-
|
20
|
-
rescue JSON::ParserError
|
21
|
-
{ "non_json_body" => body }
|
20
|
+
deserializer.call(body)
|
22
21
|
end
|
23
22
|
|
24
23
|
def to_s
|
@@ -38,7 +38,12 @@ module ChimeraHttpClient
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def create_request(method:, url:, body:, headers:, options:)
|
41
|
-
|
41
|
+
class_options = {
|
42
|
+
logger: @logger,
|
43
|
+
deserializer: @deserializer,
|
44
|
+
}
|
45
|
+
|
46
|
+
Request.new(class_options).create(
|
42
47
|
method: method,
|
43
48
|
url: url,
|
44
49
|
body: body,
|
@@ -4,8 +4,9 @@ module ChimeraHttpClient
|
|
4
4
|
|
5
5
|
attr_reader :request, :result
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@logger = logger
|
7
|
+
def initialize(options = {})
|
8
|
+
@logger = options[:logger]
|
9
|
+
@options = options
|
9
10
|
end
|
10
11
|
|
11
12
|
def run(url:, method:, body: nil, options: {}, headers: {})
|
@@ -50,39 +51,39 @@ module ChimeraHttpClient
|
|
50
51
|
private
|
51
52
|
|
52
53
|
def on_complete_handler(response)
|
53
|
-
return Response.new(response) if response.success?
|
54
|
+
return Response.new(response, @options) if response.success?
|
54
55
|
|
55
56
|
exception_for(response)
|
56
57
|
end
|
57
58
|
|
58
59
|
def exception_for(response)
|
59
|
-
return TimeoutError.new(response) if response.timed_out?
|
60
|
+
return TimeoutError.new(response, @options) if response.timed_out?
|
60
61
|
|
61
62
|
case response.code.to_i
|
62
63
|
when 301, 302, 303, 307
|
63
|
-
RedirectionError.new(response)
|
64
|
+
RedirectionError.new(response, @options)
|
64
65
|
when 200..399
|
65
66
|
nil
|
66
67
|
when 400
|
67
|
-
BadRequestError.new(response)
|
68
|
+
BadRequestError.new(response, @options)
|
68
69
|
when 401
|
69
|
-
UnauthorizedError.new(response)
|
70
|
+
UnauthorizedError.new(response, @options)
|
70
71
|
when 403
|
71
|
-
ForbiddenError.new(response)
|
72
|
+
ForbiddenError.new(response, @options)
|
72
73
|
when 404
|
73
|
-
NotFoundError.new(response)
|
74
|
+
NotFoundError.new(response, @options)
|
74
75
|
when 405
|
75
|
-
MethodNotAllowedError.new(response)
|
76
|
+
MethodNotAllowedError.new(response, @options)
|
76
77
|
when 409
|
77
|
-
ResourceConflictError.new(response)
|
78
|
+
ResourceConflictError.new(response, @options)
|
78
79
|
when 422
|
79
|
-
UnprocessableEntityError.new(response)
|
80
|
+
UnprocessableEntityError.new(response, @options)
|
80
81
|
when 400..499
|
81
|
-
ClientError.new(response)
|
82
|
+
ClientError.new(response, @options)
|
82
83
|
when 500..599
|
83
|
-
ServerError.new(response)
|
84
|
+
ServerError.new(response, @options)
|
84
85
|
else # response.code.zero?
|
85
|
-
ConnectionError.new(response)
|
86
|
+
ConnectionError.new(response, @options)
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module ChimeraHttpClient
|
2
2
|
class Response
|
3
|
-
attr_reader :body, :code, :time, :response
|
3
|
+
attr_reader :body, :code, :time, :response, :deserializer
|
4
4
|
|
5
|
-
def initialize(response)
|
5
|
+
def initialize(response, options = {})
|
6
6
|
@body = response.body
|
7
7
|
@code = response.code
|
8
8
|
@time = response.total_time
|
9
9
|
@response = response # contains the request
|
10
|
+
|
11
|
+
@deserializer = options[:deserializer][:response]
|
10
12
|
end
|
11
13
|
|
12
14
|
def parsed_body
|
13
|
-
|
14
|
-
rescue JSON::ParserError => e
|
15
|
-
raise ChimeraHttpClient::JsonParserError, "Could not parse body as JSON: #{body}, error: #{e.message}"
|
15
|
+
deserializer.call(body)
|
16
16
|
end
|
17
17
|
|
18
18
|
def error?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chimera_http_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Finger
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- lib/chimera_http_client.rb
|
172
172
|
- lib/chimera_http_client/base.rb
|
173
173
|
- lib/chimera_http_client/connection.rb
|
174
|
+
- lib/chimera_http_client/deserializer.rb
|
174
175
|
- lib/chimera_http_client/error.rb
|
175
176
|
- lib/chimera_http_client/queue.rb
|
176
177
|
- lib/chimera_http_client/request.rb
|