bootic_client 0.0.30 → 0.0.32

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88fee920f8b9082a62647609e19ca3492a3eba9028c581ce6df09a2560335b34
4
- data.tar.gz: 05b7c8e729b035f486252716392c74563ce0a0cb513a19a90b49309d135c8d6d
3
+ metadata.gz: 6eb630196e916b01799d6ff1079895e608073f9cf141c2fb66496c08494f2825
4
+ data.tar.gz: f84b45a3e467863a71f21e8560985302197ca3e7b1e1598945fb9502ce10cd51
5
5
  SHA512:
6
- metadata.gz: b205b02d5020df2a8dbb2d104a9b2110e2ae5fd39e48961214b85fc7ef562f81169d3b9021529324dd8d873a94fe58a9195a1113994a9d655d50f53a47ce421d
7
- data.tar.gz: 193493b1e377c8db82e8adb6744186892d0ff6d02e31b3ed1dac1e21c9d4e18dea04dc155af082fc8064f5deb6d143e46ea4730fa6e6a7163bdbb51ba4230551
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
@@ -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", '>= 0.15'
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'
@@ -4,7 +4,7 @@ require 'base64'
4
4
  require 'faraday'
5
5
  require 'faraday-http-cache'
6
6
  require "bootic_client/errors"
7
- require 'faraday/adapter/net_http_persistent'
7
+ require 'faraday/net_http_persistent'
8
8
 
9
9
  module BooticClient
10
10
 
@@ -15,9 +15,14 @@ module BooticClient
15
15
 
16
16
  Enumerator.new do |yielder|
17
17
  loop do
18
- page.each{|item| yielder.yield item }
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
@@ -19,7 +19,7 @@ module BooticClient
19
19
 
20
20
  def client
21
21
  @client ||= Client.new(options) do |c|
22
- c.request :basic_auth, options[:username], options[:password]
22
+ c.request :authorization, :basic, options[:username], options[:password]
23
23
  end
24
24
  end
25
25
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BooticClient
4
- VERSION = "0.0.30".freeze
4
+ VERSION = "0.0.32".freeze
5
5
  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.30
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: 2021-05-05 00:00:00.000000000 Z
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: '0.15'
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.15'
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.0.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