chimera_http_client 1.0.0 → 1.1.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 +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
|