hal-client 3.15.0 → 3.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hal_client/errors.rb +2 -0
- data/lib/hal_client/version.rb +1 -1
- data/lib/hal_client.rb +72 -13
- data/spec/hal_client_spec.rb +67 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e97fd75dd9bb812c2cdf28bcd0a9a316a94bb71
|
4
|
+
data.tar.gz: d260178d87921fe2031ae62a33952cdbbc7019a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cd9c0e7192d66633ec12c0409a4cde8a8b6fb413965671f7590dd9bc77778399da2be2cc229567066a422d317c94eab89e7822869744ade921be700ee1c14f3
|
7
|
+
data.tar.gz: 77e05a49bc31a09950cce202d93936e9eea97c1776229b3d7c43f89e647bafa5618d97a3f099693622f295eecdac0c9cfe55cd3fc751564a1e3f1db60d5ad85c
|
data/lib/hal_client/errors.rb
CHANGED
data/lib/hal_client/version.rb
CHANGED
data/lib/hal_client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "hal_client/version"
|
2
2
|
require 'http'
|
3
3
|
require 'multi_json'
|
4
|
+
require 'benchmark'
|
4
5
|
|
5
6
|
# Adapter used to access resources.
|
6
7
|
class HalClient
|
@@ -31,11 +32,18 @@ class HalClient
|
|
31
32
|
# the authorization header
|
32
33
|
# :headers - a hash of other headers to send on each request.
|
33
34
|
# :base_client - An HTTP::Client object to use.
|
35
|
+
# :logger - a Logger object to which benchmark and activity info
|
36
|
+
# will be written. Benchmark data will be written at info level
|
37
|
+
# and activity at debug level.
|
38
|
+
# :timeout - number of seconds that after which any request will be
|
39
|
+
# terminated and an exception raised. Default: Float::INFINITY
|
34
40
|
def initialize(options={})
|
35
41
|
@default_message_request_headers = HTTP::Headers.new
|
36
42
|
@default_entity_request_headers = HTTP::Headers.new
|
37
43
|
@auth_helper = as_callable(options.fetch(:authorization, NullAuthHelper))
|
38
44
|
@base_client ||= options[:base_client]
|
45
|
+
@logger = options.fetch(:logger, NullLogger.new)
|
46
|
+
@timeout = options.fetch(:timeout, Float::INFINITY)
|
39
47
|
|
40
48
|
default_message_request_headers.set('Accept', options[:accept]) if
|
41
49
|
options[:accept]
|
@@ -65,6 +73,7 @@ class HalClient
|
|
65
73
|
default_message_request_headers.set('Accept', accept_values.join(", "))
|
66
74
|
# We can work with HAL so provide a back stop accept.
|
67
75
|
end
|
76
|
+
protected :initialize
|
68
77
|
|
69
78
|
# Returns a `Representation` of the resource identified by `url`.
|
70
79
|
#
|
@@ -72,7 +81,9 @@ class HalClient
|
|
72
81
|
# headers - custom header fields to use for this request
|
73
82
|
def get(url, headers={})
|
74
83
|
headers = auth_headers(url).merge(headers)
|
75
|
-
|
84
|
+
client = client_for_get(override_headers: headers)
|
85
|
+
resp = bmtb("GET <#{url}>") { client.get(url) }
|
86
|
+
interpret_response resp
|
76
87
|
|
77
88
|
rescue HttpError => e
|
78
89
|
fail e.class.new("GET <#{url}> failed with code #{e.response.status}", e.response)
|
@@ -82,6 +93,8 @@ class HalClient
|
|
82
93
|
protected
|
83
94
|
|
84
95
|
def def_unsafe_request(method)
|
96
|
+
verb = method.to_s.upcase
|
97
|
+
|
85
98
|
define_method(method) do |url, data, headers={}|
|
86
99
|
headers = auth_headers(url).merge(headers)
|
87
100
|
|
@@ -94,11 +107,12 @@ class HalClient
|
|
94
107
|
end
|
95
108
|
|
96
109
|
begin
|
97
|
-
|
98
|
-
|
110
|
+
client = client_for_post(override_headers: headers)
|
111
|
+
resp = bmtb("#{verb} <#{url}>") { client.request(method, url, body: req_body) }
|
112
|
+
interpret_response resp
|
99
113
|
|
100
114
|
rescue HttpError => e
|
101
|
-
fail e.class.new("#{
|
115
|
+
fail e.class.new("#{verb} <#{url}> failed with code #{e.response.status}", e.response)
|
102
116
|
end
|
103
117
|
end
|
104
118
|
end
|
@@ -133,22 +147,17 @@ class HalClient
|
|
133
147
|
headers = auth_headers(url).merge(headers)
|
134
148
|
|
135
149
|
begin
|
136
|
-
|
137
|
-
|
150
|
+
client = client_for_post(override_headers: headers)
|
151
|
+
resp = bmtb("DELETE <#{url}>") { client.request(:delete, url) }
|
152
|
+
interpret_response resp
|
138
153
|
rescue HttpError => e
|
139
154
|
fail e.class.new("DELETE <#{url}> failed with code #{e.response.status}", e.response)
|
140
155
|
end
|
141
156
|
end
|
142
157
|
|
143
|
-
class << self
|
144
|
-
def delete(url, headers={})
|
145
|
-
new.delete(url, headers)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
158
|
protected
|
150
159
|
|
151
|
-
attr_reader :headers, :auth_helper
|
160
|
+
attr_reader :headers, :auth_helper, :logger, :timeout
|
152
161
|
|
153
162
|
NullAuthHelper = ->(_url) { nil }
|
154
163
|
|
@@ -248,6 +257,33 @@ class HalClient
|
|
248
257
|
[:content_type, /^content-type$/i].any?{|pat| pat === field_name}
|
249
258
|
end
|
250
259
|
|
260
|
+
def bmtb(msg, &blk)
|
261
|
+
benchmark(msg) { timebox(msg, &blk) }
|
262
|
+
end
|
263
|
+
|
264
|
+
def timebox(msg, &blk)
|
265
|
+
if timeout < Float::INFINITY
|
266
|
+
Timeout.timeout(timeout, &blk)
|
267
|
+
else
|
268
|
+
yield
|
269
|
+
end
|
270
|
+
|
271
|
+
rescue Timeout::Error
|
272
|
+
timeout_ms = timeout * 1000
|
273
|
+
raise TimeoutError, "Killed %s for taking more than %.1fms." % [msg, timeout_ms]
|
274
|
+
end
|
275
|
+
def benchmark(msg, &blk)
|
276
|
+
result = nil
|
277
|
+
elapsed = Benchmark.realtime do
|
278
|
+
result = yield
|
279
|
+
end
|
280
|
+
|
281
|
+
logger.info '%s (%.1fms)' % [ msg, elapsed*1000 ]
|
282
|
+
|
283
|
+
result
|
284
|
+
end
|
285
|
+
|
286
|
+
|
251
287
|
module EntryPointCovenienceMethods
|
252
288
|
# Returns a `Representation` of the resource identified by `url`.
|
253
289
|
#
|
@@ -266,6 +302,24 @@ class HalClient
|
|
266
302
|
default_client.post(url, data, options)
|
267
303
|
end
|
268
304
|
|
305
|
+
# Patch a `Representation` or `String` to the resource identified at `url`.
|
306
|
+
#
|
307
|
+
# url - The URL of the resource of interest.
|
308
|
+
# data - a `String` or an object that responds to `#to_hal`
|
309
|
+
# options - set of options to pass to `RestClient#get`
|
310
|
+
def patch(url, data, options={})
|
311
|
+
default_client.patch(url, data, options)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Delete the resource identified at `url`.
|
315
|
+
#
|
316
|
+
# url - The URL of the resource of interest.
|
317
|
+
# options - set of options to pass to `RestClient#get`
|
318
|
+
def delete(url, options={})
|
319
|
+
default_client.delete(url, options)
|
320
|
+
end
|
321
|
+
|
322
|
+
|
269
323
|
protected
|
270
324
|
|
271
325
|
def default_client
|
@@ -274,4 +328,9 @@ class HalClient
|
|
274
328
|
end
|
275
329
|
extend EntryPointCovenienceMethods
|
276
330
|
|
331
|
+
|
332
|
+
class NullLogger
|
333
|
+
def info(*_); end
|
334
|
+
def debug(*_); end
|
335
|
+
end
|
277
336
|
end
|
data/spec/hal_client_spec.rb
CHANGED
@@ -77,6 +77,43 @@ describe HalClient do
|
|
77
77
|
expect(request.with(headers: { "DummyHeader" => "Test" })).to have_been_made
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
context "logging" do
|
82
|
+
let(:logger) { MemoryLogger.new }
|
83
|
+
subject(:client) { HalClient.new logger: logger }
|
84
|
+
it "logs the request" do
|
85
|
+
expect(logger.info_entries).to have(1).item
|
86
|
+
expect(logger.info_entries.first).to include "http://example.com/foo"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
context "server takes too long" do
|
93
|
+
let!(:request) { stub_request(:any, "http://example.com/foo")
|
94
|
+
.with { sleep 1 } }
|
95
|
+
|
96
|
+
subject(:client) { HalClient.new timeout: 0.1 }
|
97
|
+
|
98
|
+
it "#get raises TimeoutError" do
|
99
|
+
expect{client.get "http://example.com/foo"}.to raise_exception HalClient::TimeoutError
|
100
|
+
end
|
101
|
+
|
102
|
+
it "#get includes url and verb in the error message" do
|
103
|
+
err = client.get("http://example.com/foo") rescue $!
|
104
|
+
expect(err.to_s).to include "GET <http://example.com/foo>"
|
105
|
+
end
|
106
|
+
|
107
|
+
it "#post raises HttpClientError" do
|
108
|
+
expect{client.post "http://example.com/foo", "foo"}.to raise_exception HalClient::TimeoutError
|
109
|
+
end
|
110
|
+
|
111
|
+
it "#post attaches response to the raise error" do
|
112
|
+
err = client.post("http://example.com/foo", "") rescue $!
|
113
|
+
expect(err.to_s).to include "POST <http://example.com/foo>"
|
114
|
+
end
|
115
|
+
|
116
|
+
|
80
117
|
end
|
81
118
|
|
82
119
|
context "server responds with client error" do
|
@@ -233,6 +270,16 @@ describe HalClient do
|
|
233
270
|
expect(post_request).to have_been_made
|
234
271
|
end
|
235
272
|
end
|
273
|
+
|
274
|
+
context "logging" do
|
275
|
+
before do return_val end
|
276
|
+
let(:logger) { MemoryLogger.new }
|
277
|
+
subject(:client) { HalClient.new logger: logger }
|
278
|
+
it "logs the request" do
|
279
|
+
expect(logger.info_entries).to have(1).item
|
280
|
+
expect(logger.info_entries.first).to include "http://example.com/foo"
|
281
|
+
end
|
282
|
+
end
|
236
283
|
end
|
237
284
|
|
238
285
|
describe ".post(<url>)" do
|
@@ -298,5 +345,24 @@ describe HalClient do
|
|
298
345
|
to_return body: "{}" }
|
299
346
|
|
300
347
|
let!(:request) { stub_request(:get, "http://example.com/foo").
|
301
|
-
|
348
|
+
to_return body: "{}" }
|
349
|
+
|
350
|
+
class MemoryLogger
|
351
|
+
def info_entries
|
352
|
+
@info_entries ||= []
|
353
|
+
end
|
354
|
+
def debug_entries
|
355
|
+
@debug_entries ||= []
|
356
|
+
end
|
357
|
+
|
358
|
+
def info(msg=nil)
|
359
|
+
msg = yield if block_given?
|
360
|
+
info_entries << msg
|
361
|
+
end
|
362
|
+
|
363
|
+
def debug(msg=nil)
|
364
|
+
msg = yield if block_given?
|
365
|
+
debug_entries << msg
|
366
|
+
end
|
367
|
+
end
|
302
368
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hal-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|