grac 4.2.0 → 4.3.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/lib/grac/client.rb +128 -120
- data/lib/grac/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8460d522d154cc9553f4a67210e6509fd1e1e5d9b956cccf4e3e4701eccd6135
|
4
|
+
data.tar.gz: a8c70bc97eadf86324176c301b4bc5bd856f4d20e402ba111236860734d8e0b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2db352fde7d23f9e42d578b50c08c702232c096d0de59d4ed1075bf4db63946fbff16335de25bdbac7d076e9f176a8b18b78f506bd7c5b3fe8bb81e4d9d8ba57
|
7
|
+
data.tar.gz: 146317de75aa8ab0a276016b90cd3aac920594424edc2f601b650bc64746ff531c360b34997be1e6d1af73676826948ddb573ac4be2b11e004a24aed4250d779
|
data/lib/grac/client.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative './response'
|
|
8
8
|
|
9
9
|
module Grac
|
10
10
|
class Client
|
11
|
+
|
11
12
|
attr_reader :uri
|
12
13
|
|
13
14
|
def initialize(uri, options = {})
|
@@ -15,21 +16,22 @@ module Grac
|
|
15
16
|
|
16
17
|
@uri = uri
|
17
18
|
@options = {
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
|
23
|
-
|
19
|
+
connecttimeout: options[:connecttimeout] || 0.1,
|
20
|
+
timeout: options[:timeout] || 15,
|
21
|
+
params: options[:params] || {},
|
22
|
+
headers: {
|
23
|
+
'User-Agent' => "Grac v#{Grac::VERSION}",
|
24
|
+
'Content-Type' => 'application/json;charset=utf-8'
|
24
25
|
}.merge(options[:headers] || {}),
|
25
|
-
:
|
26
|
-
:
|
26
|
+
postprocessing: {},
|
27
|
+
middleware: options[:middleware] || [],
|
28
|
+
retry_get_head: options.fetch(:retry_get_head, true),
|
27
29
|
}
|
28
30
|
|
29
31
|
if options[:postprocessing]
|
30
32
|
options[:postprocessing]
|
31
33
|
.each_with_object(postprocessing = {}) do |(pattern, transformation), obj|
|
32
|
-
if pattern.
|
34
|
+
if pattern.is_a?(Regexp)
|
33
35
|
obj[pattern] = transformation
|
34
36
|
else
|
35
37
|
obj[Regexp.new(pattern)] = transformation
|
@@ -49,10 +51,12 @@ module Grac
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def set(options = {})
|
52
|
-
options = options.merge(
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
options = options.merge(
|
55
|
+
{
|
56
|
+
headers: @options[:headers].merge(options[:headers] || {}),
|
57
|
+
middleware: @options[:middleware] + (options[:middleware] || []),
|
58
|
+
},
|
59
|
+
)
|
56
60
|
|
57
61
|
self.class.new(@uri, @options.merge(options))
|
58
62
|
end
|
@@ -64,35 +68,38 @@ module Grac
|
|
64
68
|
self.class.new("#{@uri}#{path}", @options)
|
65
69
|
end
|
66
70
|
|
67
|
-
|
71
|
+
['post', 'put', 'patch'].each do |method|
|
68
72
|
define_method method do |body = {}, params = {}|
|
69
|
-
response = build_and_run(method, { :
|
73
|
+
response = build_and_run(method, { body: body, params: params })
|
70
74
|
check_response(method, response)
|
71
75
|
end
|
72
76
|
end
|
73
77
|
|
74
|
-
|
78
|
+
['get', 'delete'].each do |method|
|
75
79
|
define_method method do |params = {}|
|
76
|
-
response = build_and_run(method, { :
|
80
|
+
response = build_and_run(method, { params: params })
|
77
81
|
check_response(method, response)
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
85
|
def call(opts, request_uri, method, params, body)
|
82
86
|
request_hash = {
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
87
|
+
method: method,
|
88
|
+
params: params, # Query params are escaped by Typhoeus
|
89
|
+
body: body,
|
90
|
+
connecttimeout: opts[:connecttimeout],
|
91
|
+
timeout: opts[:timeout],
|
92
|
+
headers: opts[:headers],
|
89
93
|
}
|
90
94
|
|
91
95
|
request = ::Typhoeus::Request.new(request_uri, request_hash)
|
92
96
|
response = request.run
|
93
97
|
|
94
98
|
# Retry GET and HEAD requests - modifying requests might not be idempotent
|
95
|
-
|
99
|
+
# Only retry those requests if the feature is enabled
|
100
|
+
if response.timed_out? && ['get', 'head'].include?(method) && @options[:retry_get_head]
|
101
|
+
response = request.run
|
102
|
+
end
|
96
103
|
|
97
104
|
# A request can time out while receiving data. In this case response.code might indicate
|
98
105
|
# success although data hasn't been fully transferred. Thus rely on Typhoeus for
|
@@ -119,122 +126,123 @@ module Grac
|
|
119
126
|
|
120
127
|
private
|
121
128
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
def headers
|
129
|
-
@options[:headers] || {}
|
130
|
-
end
|
131
|
-
|
132
|
-
def prepare_body_by_content_type(body)
|
133
|
-
return nil if body.nil? || body.empty?
|
134
|
-
|
135
|
-
case headers['Content-Type']
|
136
|
-
when /\Aapplication\/json/
|
137
|
-
return body.to_json
|
138
|
-
when /\Aapplication\/x-www-form-urlencoded/
|
139
|
-
# Typhoeus will take care of the encoding when receiving a hash
|
140
|
-
return body
|
141
|
-
else
|
142
|
-
# Do not encode other unknown Content-Types either.
|
143
|
-
# The default is JSON through the Content-Type header which is set by default.
|
144
|
-
return body
|
129
|
+
def build_and_run(method, options = {})
|
130
|
+
body = prepare_body_by_content_type(options[:body])
|
131
|
+
params = @options[:params].merge(options[:params] || {})
|
132
|
+
return middleware_chain.call(@options, uri, method, params, body)
|
145
133
|
end
|
146
|
-
end
|
147
134
|
|
148
|
-
|
149
|
-
|
135
|
+
def headers
|
136
|
+
@options[:headers] || {}
|
137
|
+
end
|
150
138
|
|
151
|
-
|
152
|
-
if
|
153
|
-
middleware_class = mw[0]
|
154
|
-
params = mw[1..-1]
|
139
|
+
def prepare_body_by_content_type(body)
|
140
|
+
return nil if body.nil? || body.empty?
|
155
141
|
|
156
|
-
|
142
|
+
case headers['Content-Type']
|
143
|
+
when /\Aapplication\/json/
|
144
|
+
return body.to_json
|
145
|
+
when /\Aapplication\/x-www-form-urlencoded/
|
146
|
+
# Typhoeus will take care of the encoding when receiving a hash
|
147
|
+
return body
|
157
148
|
else
|
158
|
-
|
149
|
+
# Do not encode other unknown Content-Types either.
|
150
|
+
# The default is JSON through the Content-Type header which is set by default.
|
151
|
+
return body
|
159
152
|
end
|
160
153
|
end
|
161
154
|
|
162
|
-
|
163
|
-
|
155
|
+
def middleware_chain
|
156
|
+
callee = self
|
164
157
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
if response.json_content?
|
170
|
-
return postprocessing(response.parsed_json)
|
171
|
-
end
|
158
|
+
@options[:middleware].reverse.each do |mw|
|
159
|
+
if mw.kind_of?(Array)
|
160
|
+
middleware_class = mw[0]
|
161
|
+
params = mw[1..-1]
|
172
162
|
|
173
|
-
|
174
|
-
when 204, 205
|
175
|
-
return true
|
176
|
-
when 0
|
177
|
-
raise Exception::RequestFailed.new(method, response.effective_url, response.return_message)
|
178
|
-
else
|
179
|
-
begin
|
180
|
-
# The Response class doesn't have enough information to create a proper exception, so
|
181
|
-
# catch its exception and raise a proper one.
|
182
|
-
parsed_body = response.parsed_json
|
183
|
-
rescue Exception::InvalidContent
|
184
|
-
raise Exception::ErrorWithInvalidContent.new(
|
185
|
-
method,
|
186
|
-
response.effective_url,
|
187
|
-
response.code,
|
188
|
-
response.body,
|
189
|
-
'json'
|
190
|
-
)
|
191
|
-
end
|
192
|
-
case response.code
|
193
|
-
when 400
|
194
|
-
raise Exception::BadRequest.new(method, response.effective_url, parsed_body)
|
195
|
-
when 403
|
196
|
-
raise Exception::Forbidden.new(method, response.effective_url, parsed_body)
|
197
|
-
when 404
|
198
|
-
raise Exception::NotFound.new(method, response.effective_url, parsed_body)
|
199
|
-
when 409
|
200
|
-
raise Exception::Conflict.new(method, response.effective_url, parsed_body)
|
163
|
+
callee = middleware_class.new(callee, *params)
|
201
164
|
else
|
202
|
-
|
165
|
+
callee = mw.new(callee)
|
203
166
|
end
|
167
|
+
end
|
168
|
+
|
169
|
+
return callee
|
204
170
|
end
|
205
|
-
end
|
206
171
|
|
207
|
-
|
208
|
-
|
172
|
+
def check_response(method, response)
|
173
|
+
case response.code
|
174
|
+
when 200..203, 206..299
|
175
|
+
# unknown status codes must be treated as the x00 of their class, so 200
|
176
|
+
if response.json_content?
|
177
|
+
return postprocessing(response.parsed_json)
|
178
|
+
end
|
179
|
+
|
180
|
+
return response.body
|
181
|
+
when 204, 205
|
182
|
+
return true
|
183
|
+
when 0
|
184
|
+
raise Exception::RequestFailed.new(method, response.effective_url, response.return_message)
|
185
|
+
else
|
186
|
+
begin
|
187
|
+
# The Response class doesn't have enough information to create a proper exception, so
|
188
|
+
# catch its exception and raise a proper one.
|
189
|
+
parsed_body = response.parsed_json
|
190
|
+
rescue Exception::InvalidContent
|
191
|
+
raise Exception::ErrorWithInvalidContent.new(
|
192
|
+
method,
|
193
|
+
response.effective_url,
|
194
|
+
response.code,
|
195
|
+
response.body,
|
196
|
+
'json'
|
197
|
+
)
|
198
|
+
end
|
199
|
+
case response.code
|
200
|
+
when 400
|
201
|
+
raise Exception::BadRequest.new(method, response.effective_url, parsed_body)
|
202
|
+
when 403
|
203
|
+
raise Exception::Forbidden.new(method, response.effective_url, parsed_body)
|
204
|
+
when 404
|
205
|
+
raise Exception::NotFound.new(method, response.effective_url, parsed_body)
|
206
|
+
when 409
|
207
|
+
raise Exception::Conflict.new(method, response.effective_url, parsed_body)
|
208
|
+
else
|
209
|
+
raise Exception::ServiceError.new(method, response.effective_url, parsed_body)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def postprocessing(data, processing = nil)
|
215
|
+
return data if @options[:postprocessing].nil? || @options[:postprocessing].empty?
|
209
216
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
217
|
+
if data.kind_of?(Hash)
|
218
|
+
data.each do |key, value|
|
219
|
+
processing = nil
|
220
|
+
regexp = @options[:postprocessing].keys.detect { |pattern| pattern.match?(key) }
|
214
221
|
|
215
|
-
|
216
|
-
|
222
|
+
if !regexp.nil?
|
223
|
+
processing = @options[:postprocessing][regexp]
|
224
|
+
end
|
225
|
+
data[key] = postprocessing(value, processing)
|
217
226
|
end
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
227
|
+
elsif data.kind_of?(Array)
|
228
|
+
data.each_with_index do |value, index|
|
229
|
+
data[index] = postprocessing(value, processing)
|
230
|
+
end
|
231
|
+
else
|
232
|
+
data = processing.nil? ? data : processing.call(data)
|
223
233
|
end
|
224
|
-
|
225
|
-
|
234
|
+
|
235
|
+
return data
|
226
236
|
end
|
227
237
|
|
228
|
-
|
229
|
-
|
238
|
+
def escape_url_param(value)
|
239
|
+
# We don't want spaces to be encoded as plus sign - a plus sign can be ambiguous in a URL and
|
240
|
+
# either represent a plus sign or a space.
|
241
|
+
# CGI::escape replaces all plus signs with their percent-encoding representation, so all
|
242
|
+
# remaining plus signs are spaces. Replacing these with a space's percent encoding makes the
|
243
|
+
# encoding unambiguous.
|
244
|
+
CGI::escape(value).gsub('+', '%20')
|
245
|
+
end
|
230
246
|
|
231
|
-
def escape_url_param(value)
|
232
|
-
# We don't want spaces to be encoded as plus sign - a plus sign can be ambiguous in a URL and
|
233
|
-
# either represent a plus sign or a space.
|
234
|
-
# CGI::escape replaces all plus signs with their percent-encoding representation, so all
|
235
|
-
# remaining plus signs are spaces. Replacing these with a space's percent encoding makes the
|
236
|
-
# encoding unambiguous.
|
237
|
-
CGI::escape(value).gsub('+', '%20')
|
238
|
-
end
|
239
247
|
end
|
240
248
|
end
|
data/lib/grac/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schoknecht
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -72,28 +72,28 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 3.
|
75
|
+
version: '3.1'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.
|
82
|
+
version: '3.1'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rack-test
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 2.
|
89
|
+
version: '2.1'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 2.
|
96
|
+
version: '2.1'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: oj
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -138,7 +138,7 @@ homepage: https://github.com/Barzahlen/grac
|
|
138
138
|
licenses:
|
139
139
|
- MIT
|
140
140
|
metadata: {}
|
141
|
-
post_install_message:
|
141
|
+
post_install_message:
|
142
142
|
rdoc_options: []
|
143
143
|
require_paths:
|
144
144
|
- lib
|
@@ -153,8 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
153
|
- !ruby/object:Gem::Version
|
154
154
|
version: '0'
|
155
155
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
157
|
-
signing_key:
|
156
|
+
rubygems_version: 3.5.3
|
157
|
+
signing_key:
|
158
158
|
specification_version: 4
|
159
159
|
summary: Very generic client for REST API with basic error handling
|
160
160
|
test_files: []
|