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 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