bootic_client 0.0.30 → 0.0.32
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/README.md +34 -0
- data/bootic_client.gemspec +2 -1
- data/lib/bootic_client/client.rb +1 -1
- data/lib/bootic_client/entity.rb +6 -1
- data/lib/bootic_client/strategies/basic_auth.rb +1 -1
- data/lib/bootic_client/stubbing.rb +118 -0
- data/lib/bootic_client/version.rb +1 -1
- data/lib/bootic_client.rb +18 -0
- data/spec/entity_spec.rb +28 -0
- data/spec/stubbing_spec.rb +96 -0
- metadata +27 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6eb630196e916b01799d6ff1079895e608073f9cf141c2fb66496c08494f2825
|
4
|
+
data.tar.gz: f84b45a3e467863a71f21e8560985302197ca3e7b1e1598945fb9502ce10cd51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8ce6a6af2642f5d73122e7c7097b57efaf0594509ca8119e0d4c62606b60b094aa4debeb070b1ea42a9d04007fc7e66f56c27ee517a0a2d854c114893b9fd11
|
7
|
+
data.tar.gz: f5979b3ca50b880006d29ab0555d2fb5ab063f8805e1415785d64cdeb729b7514d3ab833c2d429075206acf45cb8d3db4d92cbe8e5893bb151c6aacf4b0a7f1a
|
data/README.md
CHANGED
@@ -320,6 +320,40 @@ messaging_api_root = client.from_url("https://some.api.com")
|
|
320
320
|
messaging_api.do_something(foo: "bar") # etc
|
321
321
|
```
|
322
322
|
|
323
|
+
## Testing
|
324
|
+
|
325
|
+
# What
|
326
|
+
|
327
|
+
This library provides methods to simplify testing. For example, to stub a chain of links you can simply do:
|
328
|
+
|
329
|
+
```rb
|
330
|
+
BooticClient.stub_chain('root.shops.first').and_return_data({
|
331
|
+
'name' => 'Foo bar'
|
332
|
+
})
|
333
|
+
|
334
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
335
|
+
shop = client.root.shops.first
|
336
|
+
expect(shop).to be_a BooticClient::Entity
|
337
|
+
expect(shop.name).to eq 'Foo bar'
|
338
|
+
```
|
339
|
+
|
340
|
+
You can also stub links that requires arguments, like this:
|
341
|
+
|
342
|
+
```rb
|
343
|
+
BooticClient.stub_chain('root.shops', foo: 1).and_return_data({
|
344
|
+
'name' => 'Foo 1'
|
345
|
+
})
|
346
|
+
BooticClient.stub_chain('root.shops', foo: 1, bar: { hello: 123 }).and_return_data({
|
347
|
+
'name' => 'Foo 2'
|
348
|
+
})
|
349
|
+
|
350
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
351
|
+
expect(client.root.shops(foo: 1).name).to eq 'Foo 1'
|
352
|
+
|
353
|
+
# arg order doesn't matter
|
354
|
+
expect(client.root.shops(bar: { hello: 123 }, foo: 2).name).to eq 'Foo 2'
|
355
|
+
```
|
356
|
+
|
323
357
|
## Contributing
|
324
358
|
|
325
359
|
1. Fork it
|
data/bootic_client.gemspec
CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "faraday", '
|
20
|
+
spec.add_dependency "faraday", '~> 2.2'
|
21
|
+
spec.add_dependency 'faraday-net_http_persistent', '~> 2.0'
|
21
22
|
spec.add_dependency "uri_template", '~> 0.7'
|
22
23
|
spec.add_dependency "faraday-http-cache", '~> 2'
|
23
24
|
spec.add_dependency "net-http-persistent", '~> 4'
|
data/lib/bootic_client/client.rb
CHANGED
data/lib/bootic_client/entity.rb
CHANGED
@@ -15,9 +15,14 @@ module BooticClient
|
|
15
15
|
|
16
16
|
Enumerator.new do |yielder|
|
17
17
|
loop do
|
18
|
-
page.each{|item| yielder.yield
|
18
|
+
page.each { |item| yielder.yield(item) }
|
19
19
|
raise StopIteration unless page.has_rel?(:next)
|
20
20
|
page = page.next
|
21
|
+
|
22
|
+
if page.has?(:errors) # && page.errors.first.messages.first['cannot be higher'] # reached last page
|
23
|
+
yielder.yield(nil, page.errors) # yield a nil value so caller can stop gracefully
|
24
|
+
raise StopIteration
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module BooticClient
|
2
|
+
module Stubbing
|
3
|
+
MissingStubError = Class.new(StandardError)
|
4
|
+
|
5
|
+
module Stubber
|
6
|
+
def from_hash(h)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def stub_chain(method_path, opts = {})
|
11
|
+
meths = method_path.split('.')
|
12
|
+
c = 0
|
13
|
+
opts = stringify_keys(opts)
|
14
|
+
|
15
|
+
meths.reduce(self) do |stub, method_name|
|
16
|
+
c += 1
|
17
|
+
a = c == meths.size ? opts : {}
|
18
|
+
stub.stub(method_name, a)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub(method_name, opts = {})
|
23
|
+
key = stub_key(method_name, opts)
|
24
|
+
if st = stubs[key]
|
25
|
+
st.stub(method_name, opts)
|
26
|
+
st
|
27
|
+
else
|
28
|
+
stubs[key] = Stub.new(method_name, opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(method_name, *args, &block)
|
33
|
+
opts = stringify_keys(args.first)
|
34
|
+
if stub = stubs[stub_key(method_name, opts)]
|
35
|
+
stub.returns? ? stub.returns : stub
|
36
|
+
else
|
37
|
+
raise MissingStubError, "No method stubbed for '#{method_name}' with options #{opts.inspect}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
|
+
stubs.keys.any?{|k|
|
43
|
+
k.to_s =~ /^#{method_name.to_s}/
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def stub_key(method_name, opts)
|
49
|
+
[method_name.to_s, options_key(opts || {})].join('_')
|
50
|
+
end
|
51
|
+
|
52
|
+
def options_key(opts)
|
53
|
+
# sort keys
|
54
|
+
keys = opts.keys.sort
|
55
|
+
hash = keys.each_with_object({}) do |key, h|
|
56
|
+
value = if opts[key].is_a?(Hash)
|
57
|
+
options_key(opts[key])
|
58
|
+
else
|
59
|
+
opts[key].to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
h[key] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
hash.inspect
|
66
|
+
end
|
67
|
+
|
68
|
+
def stringify_keys(hash)
|
69
|
+
return hash unless hash.is_a?(Hash)
|
70
|
+
|
71
|
+
hash.each_with_object({}) do |(k, v), h|
|
72
|
+
h[k.to_s] = stringify_keys(v)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class StubRoot
|
78
|
+
include Stubber
|
79
|
+
|
80
|
+
def initialize
|
81
|
+
@stubs = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
attr_reader :stubs
|
86
|
+
end
|
87
|
+
|
88
|
+
class Stub
|
89
|
+
include Stubber
|
90
|
+
|
91
|
+
def initialize(method_name = '', opts = {})
|
92
|
+
@method_name, @opts = method_name, opts
|
93
|
+
@return_data = nil
|
94
|
+
@stubs = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def and_return_data(data)
|
98
|
+
@return_data = data
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def returns?
|
103
|
+
!!@return_data
|
104
|
+
end
|
105
|
+
|
106
|
+
def returns
|
107
|
+
if @return_data.is_a?(Array)
|
108
|
+
@return_data.map{|d| BooticClient::Entity.new(d, nil)}
|
109
|
+
else
|
110
|
+
BooticClient::Entity.new(@return_data || {}, nil)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
attr_reader :stubs
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/bootic_client.rb
CHANGED
@@ -12,6 +12,7 @@ module BooticClient
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def client(strategy_name, client_opts = {}, &on_new_token)
|
15
|
+
return @stubber if @stubber
|
15
16
|
opts = client_opts.dup
|
16
17
|
opts[:logging] = configuration.logging
|
17
18
|
opts[:logger] = configuration.logger if configuration.logging
|
@@ -21,6 +22,10 @@ module BooticClient
|
|
21
22
|
strategies.fetch(strategy_name.to_sym).new configuration, opts, &on_new_token
|
22
23
|
end
|
23
24
|
|
25
|
+
def auth_host
|
26
|
+
@auth_host || AUTH_HOST
|
27
|
+
end
|
28
|
+
|
24
29
|
def configure(&block)
|
25
30
|
yield configuration
|
26
31
|
end
|
@@ -28,5 +33,18 @@ module BooticClient
|
|
28
33
|
def configuration
|
29
34
|
@configuration ||= Configuration.new
|
30
35
|
end
|
36
|
+
|
37
|
+
def stub!
|
38
|
+
require "bootic_client/stubbing"
|
39
|
+
@stubber = Stubbing::StubRoot.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def stub_chain(method_chain, opts = {})
|
43
|
+
@stubber.stub_chain(method_chain, opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
def unstub!
|
47
|
+
@stubber = nil
|
48
|
+
end
|
31
49
|
end
|
32
50
|
end
|
data/spec/entity_spec.rb
CHANGED
@@ -232,6 +232,34 @@ describe BooticClient::Entity do
|
|
232
232
|
titles = results.map(&:title)
|
233
233
|
expect(titles).to match_array(['iPhone 4', 'iPhone 5', 'Item 3', 'Item 4'])
|
234
234
|
end
|
235
|
+
|
236
|
+
it 'handles errors gracefully' do
|
237
|
+
error_data = {
|
238
|
+
"_embedded" => {
|
239
|
+
'errors' => [
|
240
|
+
{ field: 'foo', messages: ['Ugly error'] }
|
241
|
+
]
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
error_page = BooticClient::Entity.new(error_data, client)
|
246
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2', {}).and_return(error_page)
|
247
|
+
expect(client).to_not receive(:request_and_wrap).with(:get, '/foo?page=3', {})
|
248
|
+
|
249
|
+
valid = []
|
250
|
+
errors = nil
|
251
|
+
entity.full_set.each do |item, errors|
|
252
|
+
if item
|
253
|
+
valid.push(item)
|
254
|
+
else
|
255
|
+
expect(valid.count).to eq(2)
|
256
|
+
expect(errors.first.to_hash).to eq({ field: 'foo', messages: ['Ugly error']})
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
titles = valid.map(&:title)
|
261
|
+
expect(titles).to match_array(['iPhone 4', 'iPhone 5'])
|
262
|
+
end
|
235
263
|
end
|
236
264
|
end
|
237
265
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "stubbing" do
|
4
|
+
before do
|
5
|
+
BooticClient.stub!
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
BooticClient.unstub!
|
10
|
+
end
|
11
|
+
|
12
|
+
it "stubs method chains and returns entities" do
|
13
|
+
BooticClient.stub_chain('root.shops.first').and_return_data({
|
14
|
+
'name' => 'Foo bar'
|
15
|
+
})
|
16
|
+
|
17
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
18
|
+
shop = client.root.shops.first
|
19
|
+
expect(shop).to be_a BooticClient::Entity
|
20
|
+
expect(shop.name).to eq 'Foo bar'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "stubs method chains and returns arrays of entities" do
|
24
|
+
BooticClient.stub_chain('root.shops').and_return_data([
|
25
|
+
{'name' => 'Foo bar'},
|
26
|
+
{'name' => 'Bar foo'}
|
27
|
+
])
|
28
|
+
|
29
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
30
|
+
shops = client.root.shops
|
31
|
+
expect(shops.first).to be_a BooticClient::Entity
|
32
|
+
expect(shops.last).to be_a BooticClient::Entity
|
33
|
+
expect(shops.first.name).to eq 'Foo bar'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can be chained further' do
|
37
|
+
BooticClient.stub_chain('foo.bar')
|
38
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
39
|
+
|
40
|
+
stub = client.foo.bar
|
41
|
+
stub.stub_chain('another.stubz').and_return_data({
|
42
|
+
'id' => 123
|
43
|
+
})
|
44
|
+
|
45
|
+
expect(stub.another.stubz.id).to eq 123
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'stubs depending on arguments' do
|
49
|
+
BooticClient.stub_chain('root.shops', foo: 0).and_return_data({
|
50
|
+
'name' => 'Foo 0'
|
51
|
+
})
|
52
|
+
BooticClient.stub_chain('root.shops', foo: 1).and_return_data({
|
53
|
+
'name' => 'Foo 1'
|
54
|
+
})
|
55
|
+
BooticClient.stub_chain('root.shops', foo: 2, bar: {yup: 'yiss'}).and_return_data({
|
56
|
+
'name' => 'Foo 2'
|
57
|
+
})
|
58
|
+
|
59
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
60
|
+
|
61
|
+
expect(client.root.shops(foo: 0).name).to eq 'Foo 0'
|
62
|
+
expect(client.root.shops(foo: 1).name).to eq 'Foo 1'
|
63
|
+
expect(client.root.shops(foo: 2, bar: {yup: 'yiss'}).name).to eq 'Foo 2'
|
64
|
+
# arg order shouldn't matter
|
65
|
+
expect(client.root.shops(bar: {yup: 'yiss'}, foo: 2).name).to eq 'Foo 2'
|
66
|
+
|
67
|
+
expect {
|
68
|
+
client.root.shops(foo: 2, bar: {yup: 'nope'})
|
69
|
+
}.to raise_error BooticClient::Stubbing::MissingStubError
|
70
|
+
end
|
71
|
+
|
72
|
+
it "stubs multiple chains with arguments" do
|
73
|
+
BooticClient.stub_chain('one.two', arg: 1).stub_chain('three.four').and_return_data('name' => 'example 1')
|
74
|
+
BooticClient.stub_chain('one.two', arg: 2).stub_chain('three.four').and_return_data('name' => 'example 2')
|
75
|
+
|
76
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
77
|
+
|
78
|
+
expect(client.one.two(arg: 1).three.four.name).to eq 'example 1'
|
79
|
+
expect(client.one.two(arg: 2).three.four.name).to eq 'example 2'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "treats symbol and string keys the same" do
|
83
|
+
BooticClient.stub_chain('one.two', arg: 1).and_return_data('name' => 'example 1')
|
84
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
85
|
+
|
86
|
+
expect(client.one.two("arg" => 1).name).to eq 'example 1'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "raises known exception if no stub found" do
|
90
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
91
|
+
|
92
|
+
expect{
|
93
|
+
client.nope
|
94
|
+
}.to raise_error BooticClient::Stubbing::MissingStubError
|
95
|
+
end
|
96
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootic_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.32
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday-net_http_persistent
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
40
|
+
version: '2.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: uri_template
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -167,6 +181,7 @@ files:
|
|
167
181
|
- lib/bootic_client/strategies/client_credentials.rb
|
168
182
|
- lib/bootic_client/strategies/oauth2_strategy.rb
|
169
183
|
- lib/bootic_client/strategies/strategy.rb
|
184
|
+
- lib/bootic_client/stubbing.rb
|
170
185
|
- lib/bootic_client/version.rb
|
171
186
|
- lib/bootic_client/whiny_uri.rb
|
172
187
|
- spec/authorized_strategy_spec.rb
|
@@ -184,12 +199,13 @@ files:
|
|
184
199
|
- spec/safe_cache_serializer_spec.rb
|
185
200
|
- spec/spec_helper.rb
|
186
201
|
- spec/strategy_spec.rb
|
202
|
+
- spec/stubbing_spec.rb
|
187
203
|
- spec/whiny_uri_spec.rb
|
188
204
|
homepage: https://developers.bootic.net
|
189
205
|
licenses:
|
190
206
|
- MIT
|
191
207
|
metadata: {}
|
192
|
-
post_install_message:
|
208
|
+
post_install_message:
|
193
209
|
rdoc_options: []
|
194
210
|
require_paths:
|
195
211
|
- lib
|
@@ -204,8 +220,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
204
220
|
- !ruby/object:Gem::Version
|
205
221
|
version: '0'
|
206
222
|
requirements: []
|
207
|
-
rubygems_version: 3.
|
208
|
-
signing_key:
|
223
|
+
rubygems_version: 3.4.6
|
224
|
+
signing_key:
|
209
225
|
specification_version: 4
|
210
226
|
summary: Official Ruby client for the Bootic API
|
211
227
|
test_files:
|
@@ -224,4 +240,5 @@ test_files:
|
|
224
240
|
- spec/safe_cache_serializer_spec.rb
|
225
241
|
- spec/spec_helper.rb
|
226
242
|
- spec/strategy_spec.rb
|
243
|
+
- spec/stubbing_spec.rb
|
227
244
|
- spec/whiny_uri_spec.rb
|