hal-client 3.15.0 → 3.16.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/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
|