bootic_client 0.0.21 → 0.0.22
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 +5 -5
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -1
- data/README.md +43 -0
- data/lib/bootic_client/client.rb +1 -2
- data/lib/bootic_client/configuration.rb +74 -0
- data/lib/bootic_client/entity.rb +4 -5
- data/lib/bootic_client/relation.rb +5 -5
- data/lib/bootic_client/response_handlers.rb +61 -0
- data/lib/bootic_client/strategies/authorized.rb +2 -2
- data/lib/bootic_client/strategies/basic_auth.rb +3 -3
- data/lib/bootic_client/strategies/bearer.rb +1 -1
- data/lib/bootic_client/strategies/client_credentials.rb +1 -1
- data/lib/bootic_client/strategies/oauth2_strategy.rb +4 -4
- data/lib/bootic_client/strategies/strategy.rb +7 -5
- data/lib/bootic_client/version.rb +1 -1
- data/lib/bootic_client.rb +9 -64
- data/spec/authorized_strategy_spec.rb +5 -4
- data/spec/basic_auth_strategy_spec.rb +10 -6
- data/spec/bearer_strategy_spec.rb +4 -3
- data/spec/client_credentials_strategy_spec.rb +3 -2
- data/spec/client_spec.rb +19 -16
- data/spec/configuration_spec.rb +62 -50
- data/spec/entity_spec.rb +9 -5
- data/spec/relation_spec.rb +8 -8
- data/spec/response_handlers_spec.rb +73 -0
- data/spec/strategy_spec.rb +73 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 30679ee6a43b99369706f814812e081c631f7fb430ee1aee257f39690dd065bd
|
4
|
+
data.tar.gz: 5edc48fe9a7f34e4adb0cd2eeab702953b7371d29ff538fb7a2c629ebd74d29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7177b27e419870b4e158b717f94d5a5209748c9f2d8ca590bfeec21acfaef677feabd50d70a9a67f669c4679400be9b10937e21db6114d606fa4338e289b737
|
7
|
+
data.tar.gz: 034fa3444247619b836be72220d1009ff407a53e5b5551eb5faa127196aa0dfa5a908c7697ba24f2f881150c5e0eb7d5b3410295c5a93a2493abe611ab690a64
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [Unreleased](https://github.com/bootic/bootic_client.rb/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bootic/bootic_client.rb/compare/v0.0.21...HEAD)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Custom response handlers [\#18](https://github.com/bootic/bootic_client.rb/pull/18) ([ismasan](https://github.com/ismasan))
|
10
|
+
- Wrap all hash properties in Entity.new, even if not in \_embedded group. [\#17](https://github.com/bootic/bootic_client.rb/pull/17) ([ismasan](https://github.com/ismasan))
|
11
|
+
- Make configuration an instance [\#16](https://github.com/bootic/bootic_client.rb/pull/16) ([ismasan](https://github.com/ismasan))
|
12
|
+
|
13
|
+
## [v0.0.21](https://github.com/bootic/bootic_client.rb/tree/v0.0.21) (2018-08-30)
|
14
|
+
[Full Changelog](https://github.com/bootic/bootic_client.rb/compare/v0.0.20...v0.0.21)
|
15
|
+
|
16
|
+
**Merged pull requests:**
|
17
|
+
|
18
|
+
- Add :user\_agent option [\#13](https://github.com/bootic/bootic_client.rb/pull/13) ([ismasan](https://github.com/ismasan))
|
19
|
+
|
20
|
+
## [v0.0.20](https://github.com/bootic/bootic_client.rb/tree/v0.0.20) (2018-07-05)
|
21
|
+
[Full Changelog](https://github.com/bootic/bootic_client.rb/compare/v0.0.19...v0.0.20)
|
22
|
+
|
23
|
+
**Merged pull requests:**
|
24
|
+
|
25
|
+
- Validate config setters [\#15](https://github.com/bootic/bootic_client.rb/pull/15) ([ismasan](https://github.com/ismasan))
|
26
|
+
- Whitelist undeclared params [\#12](https://github.com/bootic/bootic_client.rb/pull/12) ([ismasan](https://github.com/ismasan))
|
27
|
+
|
3
28
|
## [v0.0.19](https://github.com/bootic/bootic_client.rb/tree/v0.0.19) (2017-06-01)
|
4
29
|
[Full Changelog](https://github.com/bootic/bootic_client.rb/compare/v0.0.18...v0.0.19)
|
5
30
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -218,6 +218,49 @@ asset = product.create_product_asset(
|
|
218
218
|
)
|
219
219
|
```
|
220
220
|
|
221
|
+
## Non-JSON responses
|
222
|
+
|
223
|
+
HTTP responses are resolved by handler callables in `BooticClient::Configuration#response_handlers`.
|
224
|
+
|
225
|
+
The default stack is:
|
226
|
+
|
227
|
+
* `BooticClient::ResponseHandlers::Hal`: handles `application/json` responses and wraps JSON data in `BooticClient::Entity` instances.
|
228
|
+
* `BooticClient::ResponseHandlers::File`: handles `image/*` responses and wraps image data in IO-like objects.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# Fetching product images and saving them to local files:
|
232
|
+
product.images.each do |img|
|
233
|
+
io = img.original # HTTP request to image file
|
234
|
+
# now write image data to local file.
|
235
|
+
File.open(io.file_name, 'wb') do |f|
|
236
|
+
f.write io.read
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
You can register custom response handlers. The example below parses CSV response data.
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
require 'csv'
|
245
|
+
|
246
|
+
# Response handlers are callable (anything that responds to #call(faraday_response, client)
|
247
|
+
# if a handler returns `nil`, the next handler in the stack will be called.
|
248
|
+
CSVHandler = Proc.new do |resp, _client|
|
249
|
+
if resp.headers['Content-Type'] =~ /text\/csv/
|
250
|
+
CSV.parse(resp.body, headings: true)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
BooticClient.configure do |c|
|
255
|
+
c.response_handlers.append(CSVHandler)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Now CSV resources will be returned as parsed CSV data
|
259
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
260
|
+
root = client.root
|
261
|
+
csv = root.some_csv_resource # returns parsed CSV object.
|
262
|
+
```
|
263
|
+
|
221
264
|
## Relation docs
|
222
265
|
|
223
266
|
All resource link relations include a "docs" URL so you can learn more about that particular resource.
|
data/lib/bootic_client/client.rb
CHANGED
@@ -60,7 +60,7 @@ module BooticClient
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
private
|
64
64
|
|
65
65
|
def conn(&block)
|
66
66
|
@conn ||= Faraday.new do |f|
|
@@ -69,7 +69,6 @@ module BooticClient
|
|
69
69
|
|
70
70
|
f.use :http_cache, cache_options
|
71
71
|
f.response :logger, options[:logger] if options[:logging]
|
72
|
-
f.response :json
|
73
72
|
yield f if block_given?
|
74
73
|
f.adapter *Array(options[:faraday_adapter])
|
75
74
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'bootic_client/response_handlers'
|
2
|
+
|
3
|
+
module BooticClient
|
4
|
+
InvalidConfigurationError = Class.new(StandardError)
|
5
|
+
VERY_BASIC_URL_CHECK = /^(http|https):/.freeze
|
6
|
+
|
7
|
+
AUTH_HOST = 'https://auth.bootic.net'.freeze
|
8
|
+
API_ROOT = 'https://api.bootic.net/v1'.freeze
|
9
|
+
|
10
|
+
class Configuration
|
11
|
+
attr_accessor :logging
|
12
|
+
attr_reader :client_id, :client_secret, :cache_store, :user_agent
|
13
|
+
|
14
|
+
def user_agent=(v)
|
15
|
+
set_non_nil :user_agent, v
|
16
|
+
end
|
17
|
+
|
18
|
+
def client_id=(v)
|
19
|
+
set_non_nil :client_id, v
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_secret=(v)
|
23
|
+
set_non_nil :client_secret, v
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache_store=(v)
|
27
|
+
set_non_nil :cache_store, v
|
28
|
+
end
|
29
|
+
|
30
|
+
def auth_host=(v)
|
31
|
+
check_url! :auth_host, v
|
32
|
+
set_non_nil :auth_host, v
|
33
|
+
end
|
34
|
+
|
35
|
+
def api_root=(v)
|
36
|
+
check_url! :api_root, v
|
37
|
+
set_non_nil :api_root, v
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger=(v)
|
41
|
+
set_non_nil :logger, v
|
42
|
+
end
|
43
|
+
|
44
|
+
def auth_host
|
45
|
+
@auth_host || AUTH_HOST
|
46
|
+
end
|
47
|
+
|
48
|
+
def api_root
|
49
|
+
@api_root || API_ROOT
|
50
|
+
end
|
51
|
+
|
52
|
+
def logger
|
53
|
+
@logger || ::Logger.new(STDOUT)
|
54
|
+
end
|
55
|
+
|
56
|
+
def response_handlers
|
57
|
+
@response_handlers ||= ResponseHandlers::Set.new([
|
58
|
+
ResponseHandlers::Hal,
|
59
|
+
ResponseHandlers::File
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def set_non_nil(name, v)
|
66
|
+
raise InvalidConfigurationError, "#{name} cannot be nil" if v.nil?
|
67
|
+
instance_variable_set("@#{name}", v)
|
68
|
+
end
|
69
|
+
|
70
|
+
def check_url!(name, v)
|
71
|
+
raise InvalidConfigurationError, "#{name} must be a valid URL" unless v.to_s =~ VERY_BASIC_URL_CHECK
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/bootic_client/entity.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "bootic_client/relation"
|
2
|
-
require 'ostruct'
|
3
2
|
|
4
3
|
module BooticClient
|
5
4
|
module EnumerableEntity
|
@@ -60,7 +59,7 @@ module BooticClient
|
|
60
59
|
|
61
60
|
def properties
|
62
61
|
@properties ||= attrs.select{|k,v| !(k =~ SPECIAL_PROP_EXP)}.each_with_object({}) do |(k,v),memo|
|
63
|
-
memo[k.to_sym] = Entity.wrap(v)
|
62
|
+
memo[k.to_sym] = Entity.wrap(v, client: client, top: top)
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
@@ -68,10 +67,10 @@ module BooticClient
|
|
68
67
|
@links ||= attrs.fetch('_links', {})
|
69
68
|
end
|
70
69
|
|
71
|
-
def self.wrap(obj)
|
70
|
+
def self.wrap(obj, client: nil, top: nil)
|
72
71
|
case obj
|
73
72
|
when Hash
|
74
|
-
|
73
|
+
new(obj, client, top: top)
|
75
74
|
when Array
|
76
75
|
obj.map{|e| wrap(e)}
|
77
76
|
else
|
@@ -129,7 +128,7 @@ module BooticClient
|
|
129
128
|
)
|
130
129
|
end
|
131
130
|
|
132
|
-
|
131
|
+
private
|
133
132
|
|
134
133
|
attr_reader :client, :top, :attrs
|
135
134
|
|
@@ -18,8 +18,8 @@ module BooticClient
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def initialize(attrs, client,
|
22
|
-
@attrs, @client
|
21
|
+
def initialize(attrs, client, complain_on_undeclared_params: self.class.complain_on_undeclared_params)
|
22
|
+
@attrs, @client = attrs, client
|
23
23
|
@complain_on_undeclared_params = complain_on_undeclared_params
|
24
24
|
end
|
25
25
|
|
@@ -71,9 +71,9 @@ module BooticClient
|
|
71
71
|
end
|
72
72
|
# remove payload vars from URI opts if destructive action
|
73
73
|
opts = opts.reject{|k, v| !uri_vars.include?(k.to_s) } if destructive?
|
74
|
-
client.request_and_wrap transport_method.to_sym, uri.expand(opts),
|
74
|
+
client.request_and_wrap transport_method.to_sym, uri.expand(opts), payload
|
75
75
|
else
|
76
|
-
client.request_and_wrap transport_method.to_sym, href,
|
76
|
+
client.request_and_wrap transport_method.to_sym, href, opts
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -82,7 +82,7 @@ module BooticClient
|
|
82
82
|
end
|
83
83
|
|
84
84
|
protected
|
85
|
-
attr_reader :
|
85
|
+
attr_reader :client, :attrs, :complain_on_undeclared_params
|
86
86
|
|
87
87
|
def uri
|
88
88
|
@uri ||= WhinyURI.new(href, complain_on_undeclared_params)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module BooticClient
|
4
|
+
module ResponseHandlers
|
5
|
+
class Set
|
6
|
+
def initialize(handlers = [])
|
7
|
+
@handlers = handlers
|
8
|
+
end
|
9
|
+
|
10
|
+
def resolve(response, client)
|
11
|
+
custom = @handlers.find do |handler|
|
12
|
+
obj = handler.call(response, client)
|
13
|
+
break obj if obj
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
# if no handler found,
|
17
|
+
# return raw Faraday response
|
18
|
+
custom || response
|
19
|
+
end
|
20
|
+
|
21
|
+
def append(handler)
|
22
|
+
@handlers << handler
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepend(handler)
|
26
|
+
@handlers.unshift handler
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_a
|
30
|
+
@handlers
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
JSON_MIME_EXP = /^application\/json/.freeze
|
35
|
+
IMAGE_MIME_EXP = /^image\//.freeze
|
36
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
37
|
+
IO = Struct.new(:io, :file_name, :mime_type) do
|
38
|
+
def read
|
39
|
+
io.read
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Hal = Proc.new do |resp, client|
|
44
|
+
if resp.headers[CONTENT_TYPE] =~ JSON_MIME_EXP
|
45
|
+
data = ::JSON.parse(resp.body)
|
46
|
+
Entity.new(data, client)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
File = Proc.new do |resp, client|
|
51
|
+
if resp.headers[CONTENT_TYPE] =~ IMAGE_MIME_EXP
|
52
|
+
fname = ::File.basename(resp.env[:url].to_s)
|
53
|
+
IO.new(
|
54
|
+
StringIO.new(resp.body),
|
55
|
+
fname,
|
56
|
+
resp.headers[CONTENT_TYPE]
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -4,10 +4,10 @@ module BooticClient
|
|
4
4
|
module Strategies
|
5
5
|
|
6
6
|
class Authorized < Oauth2Strategy
|
7
|
-
|
7
|
+
private
|
8
8
|
|
9
9
|
def validate!
|
10
|
-
raise ArgumentError,
|
10
|
+
raise ArgumentError, 'options MUST include access_token' unless options[:access_token]
|
11
11
|
end
|
12
12
|
|
13
13
|
def get_token
|
@@ -8,11 +8,11 @@ module BooticClient
|
|
8
8
|
%(#<#{self.class.name} root: #{config.api_root} username: #{options[:username]}>)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
private
|
12
12
|
|
13
13
|
def validate!
|
14
|
-
raise ArgumentError,
|
15
|
-
raise ArgumentError,
|
14
|
+
raise ArgumentError, 'options MUST include username' unless options[:username]
|
15
|
+
raise ArgumentError, 'options MUST include password' unless options[:password]
|
16
16
|
end
|
17
17
|
|
18
18
|
def client
|
@@ -10,12 +10,12 @@ module BooticClient
|
|
10
10
|
%(#<#{self.class.name} cid: #{config.client_id} root: #{config.api_root} auth: #{config.auth_host}>)
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
private
|
14
14
|
|
15
15
|
def validate!
|
16
|
-
raise ArgumentError,
|
17
|
-
raise ArgumentError,
|
18
|
-
raise ArgumentError,
|
16
|
+
raise ArgumentError, 'MUST include client_id' unless config.client_id
|
17
|
+
raise ArgumentError, 'MUST include client_secret' unless config.client_secret
|
18
|
+
raise ArgumentError, 'MUST include api_root' unless config.api_root
|
19
19
|
end
|
20
20
|
|
21
21
|
def pre_flight
|
@@ -6,25 +6,27 @@ module BooticClient
|
|
6
6
|
|
7
7
|
def initialize(config, client_opts = {}, &on_new_token)
|
8
8
|
@config, @options, @on_new_token = config, client_opts, (on_new_token || Proc.new{})
|
9
|
+
raise ArgumentError, 'must include a Configuration object' unless config
|
9
10
|
validate!
|
10
11
|
end
|
11
12
|
|
12
13
|
def root
|
13
|
-
request_and_wrap :get, config.api_root
|
14
|
+
request_and_wrap :get, config.api_root
|
14
15
|
end
|
15
16
|
|
16
17
|
def from_hash(hash, wrapper_class = Entity)
|
17
18
|
wrapper_class.new hash, self
|
18
19
|
end
|
19
20
|
|
20
|
-
def from_url(url
|
21
|
-
request_and_wrap :get, url
|
21
|
+
def from_url(url)
|
22
|
+
request_and_wrap :get, url
|
22
23
|
end
|
23
24
|
|
24
|
-
def request_and_wrap(request_method, href,
|
25
|
+
def request_and_wrap(request_method, href, payload = {})
|
25
26
|
pre_flight
|
26
27
|
retryable do
|
27
|
-
|
28
|
+
resp = client.send(request_method, href, payload, request_headers)
|
29
|
+
config.response_handlers.resolve(resp, self)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
data/lib/bootic_client.rb
CHANGED
@@ -3,85 +3,30 @@ require "bootic_client/version"
|
|
3
3
|
require "bootic_client/entity"
|
4
4
|
require "bootic_client/relation"
|
5
5
|
require "bootic_client/client"
|
6
|
+
require "bootic_client/configuration"
|
6
7
|
|
7
8
|
module BooticClient
|
8
|
-
InvalidConfigurationError = Class.new(StandardError)
|
9
|
-
VERY_BASIC_URL_CHECK = /^(http|https):/.freeze
|
10
|
-
|
11
|
-
AUTH_HOST = 'https://auth.bootic.net'.freeze
|
12
|
-
API_ROOT = 'https://api.bootic.net/v1'.freeze
|
13
|
-
|
14
9
|
class << self
|
15
|
-
attr_accessor :logging
|
16
|
-
attr_reader :client_id, :client_secret, :cache_store, :user_agent
|
17
|
-
|
18
10
|
def strategies
|
19
11
|
@strategies ||= {}
|
20
12
|
end
|
21
13
|
|
22
14
|
def client(strategy_name, client_opts = {}, &on_new_token)
|
23
15
|
opts = client_opts.dup
|
24
|
-
opts[:logging] = logging
|
25
|
-
opts[:logger] = logger if logging
|
26
|
-
opts[:cache_store] = cache_store if cache_store
|
27
|
-
opts[:user_agent] = user_agent if user_agent
|
16
|
+
opts[:logging] = configuration.logging
|
17
|
+
opts[:logger] = configuration.logger if configuration.logging
|
18
|
+
opts[:cache_store] = configuration.cache_store if configuration.cache_store
|
19
|
+
opts[:user_agent] = configuration.user_agent if configuration.user_agent
|
28
20
|
require "bootic_client/strategies/#{strategy_name}"
|
29
|
-
strategies.fetch(strategy_name.to_sym).new
|
30
|
-
end
|
31
|
-
|
32
|
-
def user_agent=(v)
|
33
|
-
set_non_nil :user_agent, v
|
34
|
-
end
|
35
|
-
|
36
|
-
def client_id=(v)
|
37
|
-
set_non_nil :client_id, v
|
38
|
-
end
|
39
|
-
|
40
|
-
def client_secret=(v)
|
41
|
-
set_non_nil :client_secret, v
|
42
|
-
end
|
43
|
-
|
44
|
-
def cache_store=(v)
|
45
|
-
set_non_nil :cache_store, v
|
46
|
-
end
|
47
|
-
|
48
|
-
def auth_host=(v)
|
49
|
-
check_url! :auth_host, v
|
50
|
-
set_non_nil :auth_host, v
|
51
|
-
end
|
52
|
-
|
53
|
-
def api_root=(v)
|
54
|
-
check_url! :api_root, v
|
55
|
-
set_non_nil :api_root, v
|
56
|
-
end
|
57
|
-
|
58
|
-
def logger=(v)
|
59
|
-
set_non_nil :logger, v
|
60
|
-
end
|
61
|
-
|
62
|
-
def auth_host
|
63
|
-
@auth_host || AUTH_HOST
|
64
|
-
end
|
65
|
-
|
66
|
-
def api_root
|
67
|
-
@api_root || API_ROOT
|
68
|
-
end
|
69
|
-
|
70
|
-
def logger
|
71
|
-
@logger || ::Logger.new(STDOUT)
|
21
|
+
strategies.fetch(strategy_name.to_sym).new configuration, opts, &on_new_token
|
72
22
|
end
|
73
23
|
|
74
24
|
def configure(&block)
|
75
|
-
yield
|
76
|
-
end
|
77
|
-
|
78
|
-
def set_non_nil(name, v)
|
79
|
-
raise InvalidConfigurationError, "#{name} cannot be nil" if v.nil?
|
80
|
-
instance_variable_set("@#{name}", v)
|
25
|
+
yield configuration
|
81
26
|
end
|
82
27
|
|
83
|
-
def
|
84
|
-
|
28
|
+
def configuration
|
29
|
+
@configuration ||= Configuration.new
|
85
30
|
end
|
86
31
|
end
|
87
32
|
end
|
@@ -6,6 +6,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
6
6
|
|
7
7
|
let(:client_id) {'aaa'}
|
8
8
|
let(:client_secret) {'bbb'}
|
9
|
+
let(:response_headers) { {'Content-Type' => 'application/json'} }
|
9
10
|
|
10
11
|
def jwt_assertion(expired_token, now)
|
11
12
|
JWT.encode({
|
@@ -19,7 +20,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
19
20
|
def stub_api_root(access_token, status, body)
|
20
21
|
stub_request(:get, "https://api.bootic.net/v1").
|
21
22
|
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer #{access_token}"}).
|
22
|
-
to_return(status: status, :body => JSON.dump(body))
|
23
|
+
to_return(status: status, :headers => response_headers, :body => JSON.dump(body))
|
23
24
|
end
|
24
25
|
|
25
26
|
def stub_auth(expired_token, status, body, client_id: '', client_secret: '', scope: '')
|
@@ -127,12 +128,12 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
127
128
|
stub_api_root('abc', 200, root_data)
|
128
129
|
@unauthorized_request = stub_request(:get, "https://api.bootic.net/v1/shops").
|
129
130
|
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer abc"}).
|
130
|
-
to_return(status: 401, :body => JSON.dump(message: 'authorized'))
|
131
|
+
to_return(status: 401, headers: response_headers, :body => JSON.dump(message: 'authorized'))
|
131
132
|
@auth_request = stub_auth('abc', 200, access_token: 'validtoken')
|
132
133
|
|
133
134
|
@authorized_request = stub_request(:get, "https://api.bootic.net/v1/shops").
|
134
135
|
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer validtoken"}).
|
135
|
-
to_return(status: 200, :body => JSON.dump(title: 'All shops'))
|
136
|
+
to_return(status: 200, headers: response_headers, :body => JSON.dump(title: 'All shops'))
|
136
137
|
@root = client.root
|
137
138
|
end
|
138
139
|
|
@@ -157,7 +158,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
157
158
|
describe '#from_url' do
|
158
159
|
it 'builds and returns an entity' do
|
159
160
|
authorized_request = stub_request(:get, "https://api.bootic.net/v1/shops").
|
160
|
-
to_return(status: 200, :body => JSON.dump(title: 'All shops'))
|
161
|
+
to_return(status: 200, headers: response_headers, :body => JSON.dump(title: 'All shops'))
|
161
162
|
|
162
163
|
entity = client.from_url('https://api.bootic.net/v1/shops')
|
163
164
|
expect(entity).to be_kind_of(BooticClient::Entity)
|
@@ -3,6 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe 'BooticClient::Strategies::BasicAuth' do
|
4
4
|
require 'webmock/rspec'
|
5
5
|
|
6
|
+
let(:response_headers) { {'Content-Type' => 'application/json'} }
|
6
7
|
let(:root_data) {
|
7
8
|
{
|
8
9
|
'_links' => {
|
@@ -34,8 +35,9 @@ describe 'BooticClient::Strategies::BasicAuth' do
|
|
34
35
|
|
35
36
|
context 'with invalid BasicAuth credentials' do
|
36
37
|
let!(:root_request) do
|
37
|
-
stub_request(:get, 'https://
|
38
|
-
.
|
38
|
+
stub_request(:get, 'https://api.bootic.net/v1')
|
39
|
+
.with(basic_auth: ['foo', 'bar'])
|
40
|
+
.to_return(status: 401, headers: response_headers, body: JSON.dump(root_data))
|
39
41
|
end
|
40
42
|
|
41
43
|
it 'raises an Unauthorized error' do
|
@@ -46,13 +48,15 @@ describe 'BooticClient::Strategies::BasicAuth' do
|
|
46
48
|
|
47
49
|
context 'with valid BasicAuth credentials' do
|
48
50
|
let!(:root_request) do
|
49
|
-
stub_request(:get, 'https://
|
50
|
-
.
|
51
|
+
stub_request(:get, 'https://api.bootic.net/v1')
|
52
|
+
.with(basic_auth: ['foo', 'bar'])
|
53
|
+
.to_return(status: 200, headers: response_headers, body: JSON.dump(root_data))
|
51
54
|
end
|
52
55
|
|
53
56
|
let!(:product_request) do
|
54
|
-
stub_request(:get, 'https://
|
55
|
-
.
|
57
|
+
stub_request(:get, 'https://api.bootic.net/v1/products/1')
|
58
|
+
.with(basic_auth: ['foo', 'bar'])
|
59
|
+
.to_return(status: 200, headers: response_headers, body: JSON.dump(product_data))
|
56
60
|
end
|
57
61
|
|
58
62
|
let!(:root) { client.root }
|
@@ -3,6 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe 'BooticClient::Strategies::Bearer' do
|
4
4
|
require 'webmock/rspec'
|
5
5
|
|
6
|
+
let(:response_headers) { {'Content-Type' => 'application/json'} }
|
6
7
|
let(:root_data) {
|
7
8
|
{
|
8
9
|
'_links' => {
|
@@ -36,7 +37,7 @@ describe 'BooticClient::Strategies::Bearer' do
|
|
36
37
|
let!(:root_request) do
|
37
38
|
stub_request(:get, 'https://api.bootic.net/v1')
|
38
39
|
.with(headers: {"Authorization" => "Bearer foobar"})
|
39
|
-
.to_return(status: 401, body: JSON.dump(root_data))
|
40
|
+
.to_return(status: 401, headers: response_headers, body: JSON.dump(root_data))
|
40
41
|
end
|
41
42
|
|
42
43
|
it 'raises an Unauthorized error' do
|
@@ -49,13 +50,13 @@ describe 'BooticClient::Strategies::Bearer' do
|
|
49
50
|
let!(:root_request) do
|
50
51
|
stub_request(:get, 'https://api.bootic.net/v1')
|
51
52
|
.with(headers: {"Authorization" => "Bearer foobar"})
|
52
|
-
.to_return(status: 200, body: JSON.dump(root_data))
|
53
|
+
.to_return(status: 200, headers: response_headers, body: JSON.dump(root_data))
|
53
54
|
end
|
54
55
|
|
55
56
|
let!(:product_request) do
|
56
57
|
stub_request(:get, 'https://api.bootic.net/v1/products/1')
|
57
58
|
.with(headers: {"Authorization" => "Bearer foobar"})
|
58
|
-
.to_return(status: 200, body: JSON.dump(product_data))
|
59
|
+
.to_return(status: 200, headers: response_headers, body: JSON.dump(product_data))
|
59
60
|
end
|
60
61
|
|
61
62
|
let!(:root) { client.root }
|
@@ -3,6 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe 'BooticClient::Strategies::ClientCredentials' do
|
4
4
|
require 'webmock/rspec'
|
5
5
|
|
6
|
+
let(:response_headers) { {'Content-Type' => 'application/json'} }
|
6
7
|
let(:store){ Hash.new }
|
7
8
|
let(:root_data) {
|
8
9
|
{
|
@@ -15,7 +16,7 @@ describe 'BooticClient::Strategies::ClientCredentials' do
|
|
15
16
|
|
16
17
|
describe 'with missing client credentials' do
|
17
18
|
it 'raises error' do
|
18
|
-
allow(BooticClient).to receive(:client_id).and_return nil
|
19
|
+
allow(BooticClient.configuration).to receive(:client_id).and_return nil
|
19
20
|
expect{
|
20
21
|
BooticClient.client(:client_credentials, scope: 'admin')
|
21
22
|
}.to raise_error(ArgumentError)
|
@@ -33,7 +34,7 @@ describe 'BooticClient::Strategies::ClientCredentials' do
|
|
33
34
|
def stub_api_root(access_token, status, body)
|
34
35
|
stub_request(:get, "https://api.bootic.net/v1").
|
35
36
|
with(headers: {'Accept'=>'application/json', 'Authorization'=>"Bearer #{access_token}"}).
|
36
|
-
to_return(status: status, body: JSON.dump(body))
|
37
|
+
to_return(status: status, headers: response_headers, body: JSON.dump(body))
|
37
38
|
end
|
38
39
|
|
39
40
|
before do
|
data/spec/client_spec.rb
CHANGED
@@ -37,9 +37,7 @@ describe BooticClient::Client do
|
|
37
37
|
def assert_successful_response(response)
|
38
38
|
expect(response).to be_kind_of(Faraday::Response)
|
39
39
|
expect(response.status).to eql(200)
|
40
|
-
response.body.
|
41
|
-
expect(b['_links']['shops']).to eql({'href' => 'https://api.bootic.net/v1/products'})
|
42
|
-
end
|
40
|
+
expect(response.body).not_to be nil
|
43
41
|
end
|
44
42
|
|
45
43
|
context 'switching cache key as per Vary header' do
|
@@ -193,11 +191,11 @@ describe BooticClient::Client do
|
|
193
191
|
before do
|
194
192
|
stub_request(:get, root_url)
|
195
193
|
.with(query: {foo: 'bar'}, headers: request_headers)
|
196
|
-
.to_return(status: 200, body:
|
194
|
+
.to_return(status: 200, body: 'abc')
|
197
195
|
end
|
198
196
|
|
199
197
|
it 'GETs response' do
|
200
|
-
expect(client.get(root_url, {foo: 'bar'}, request_headers).body
|
198
|
+
expect(client.get(root_url, {foo: 'bar'}, request_headers).body).to eq 'abc'
|
201
199
|
end
|
202
200
|
end
|
203
201
|
|
@@ -209,7 +207,7 @@ describe BooticClient::Client do
|
|
209
207
|
end
|
210
208
|
|
211
209
|
it 'POSTs request and parses response' do
|
212
|
-
expect(client.post(root_url, {foo: 'bar'}, request_headers).
|
210
|
+
expect(client.post(root_url, {foo: 'bar'}, request_headers).status).to eq 201
|
213
211
|
end
|
214
212
|
end
|
215
213
|
|
@@ -223,18 +221,19 @@ describe BooticClient::Client do
|
|
223
221
|
.to_return(status: 201, body: JSON.dump(root_data), headers: response_headers)
|
224
222
|
end
|
225
223
|
|
226
|
-
it 'POSTs request with base64-encoded file
|
227
|
-
|
224
|
+
it 'POSTs request with base64-encoded file' do
|
225
|
+
resp = client.post(root_url, {foo: 'bar', data: file}, request_headers)
|
226
|
+
expect(resp.status).to eq 201
|
228
227
|
end
|
229
228
|
|
230
229
|
it "works with anything that responds to #read" do
|
231
230
|
reader = double("reader", read: File.read(fixture_path("file.gif")))
|
232
|
-
expect(client.post(root_url, {foo: 'bar', data: reader}, request_headers).
|
231
|
+
expect(client.post(root_url, {foo: 'bar', data: reader}, request_headers).status).to eq 201
|
233
232
|
end
|
234
233
|
|
235
234
|
it "works with open-uri" do
|
236
235
|
reader = open(fixture_path("file.gif"))
|
237
|
-
expect(client.post(root_url, {foo: 'bar', data: reader}, request_headers).
|
236
|
+
expect(client.post(root_url, {foo: 'bar', data: reader}, request_headers).status).to eq 201
|
238
237
|
end
|
239
238
|
end
|
240
239
|
|
@@ -243,11 +242,13 @@ describe BooticClient::Client do
|
|
243
242
|
before do
|
244
243
|
stub_request(verb, root_url)
|
245
244
|
.with(body: JSON.dump({foo: 'bar'}), headers: request_headers)
|
246
|
-
.to_return(status: 200, body:
|
245
|
+
.to_return(status: 200, body: 'abc', headers: response_headers)
|
247
246
|
end
|
248
247
|
|
249
|
-
it "#{verb.to_s.upcase}s request
|
250
|
-
|
248
|
+
it "#{verb.to_s.upcase}s request" do
|
249
|
+
resp = client.send(verb, root_url, {foo: 'bar'}, request_headers)
|
250
|
+
expect(resp.status).to eq 200
|
251
|
+
expect(resp.body).to eq 'abc'
|
251
252
|
end
|
252
253
|
end
|
253
254
|
|
@@ -258,11 +259,13 @@ describe BooticClient::Client do
|
|
258
259
|
before do
|
259
260
|
stub_request(verb, root_url)
|
260
261
|
.with(body: JSON.dump({foo: 'bar', data: {name: 'la', file: base64_data}}), headers: request_headers)
|
261
|
-
.to_return(status: 200, body:
|
262
|
+
.to_return(status: 200, body: 'abc', headers: response_headers)
|
262
263
|
end
|
263
264
|
|
264
|
-
it "#{verb.to_s.upcase}s request with base64-encoded file data
|
265
|
-
|
265
|
+
it "#{verb.to_s.upcase}s request with base64-encoded file data" do
|
266
|
+
resp = client.send(verb, root_url, {foo: 'bar', data: {name: 'la', file: file}}, request_headers)
|
267
|
+
expect(resp.status).to eq 200
|
268
|
+
expect(resp.body).to eq 'abc'
|
266
269
|
end
|
267
270
|
end
|
268
271
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -1,63 +1,75 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe BooticClient do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
describe BooticClient::Configuration do
|
4
|
+
context 'errors' do
|
5
|
+
it "raises if nil client_id" do
|
6
|
+
expect {
|
7
|
+
BooticClient.configure do |c|
|
8
|
+
c.client_id = nil
|
9
|
+
end
|
10
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
it "raises if nil client_secret" do
|
14
|
+
expect {
|
15
|
+
BooticClient.configure do |c|
|
16
|
+
c.client_secret = nil
|
17
|
+
end
|
18
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
it "raises if nil cache_store" do
|
22
|
+
expect {
|
23
|
+
BooticClient.configure do |c|
|
24
|
+
c.cache_store = nil
|
25
|
+
end
|
26
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
it "raises if nil or invalid auth_host" do
|
30
|
+
expect {
|
31
|
+
BooticClient.configure do |c|
|
32
|
+
c.auth_host = nil
|
33
|
+
end
|
34
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
expect {
|
37
|
+
BooticClient.configure do |c|
|
38
|
+
c.auth_host = 'not-a-url'
|
39
|
+
end
|
40
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
it "raises if nil or invalid api_root" do
|
44
|
+
expect {
|
45
|
+
BooticClient.configure do |c|
|
46
|
+
c.api_root = nil
|
47
|
+
end
|
48
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
expect {
|
51
|
+
BooticClient.configure do |c|
|
52
|
+
c.api_root = 'not-a-url'
|
53
|
+
end
|
54
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
55
|
+
end
|
56
|
+
|
57
|
+
it "raises if nil logger" do
|
58
|
+
expect {
|
59
|
+
BooticClient.configure do |c|
|
60
|
+
c.logger = nil
|
61
|
+
end
|
62
|
+
}.to raise_error BooticClient::InvalidConfigurationError
|
63
|
+
end
|
54
64
|
end
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
66
|
+
context 'instance' do
|
67
|
+
subject(:config) { described_class.new }
|
68
|
+
|
69
|
+
describe '#response_handlers' do
|
70
|
+
it 'includes Entity handler by default' do
|
71
|
+
expect(config.response_handlers.to_a.first).not_to be_nil
|
60
72
|
end
|
61
|
-
|
73
|
+
end
|
62
74
|
end
|
63
75
|
end
|
data/spec/entity_spec.rb
CHANGED
@@ -9,7 +9,8 @@ describe BooticClient::Entity do
|
|
9
9
|
'page' => 1,
|
10
10
|
'an_object' => {
|
11
11
|
'name' => 'Foobar',
|
12
|
-
'age' => 22
|
12
|
+
'age' => 22,
|
13
|
+
'another_object' => {'foo' => 'bar'}
|
13
14
|
},
|
14
15
|
'_links' => {
|
15
16
|
'self' => {'href' => '/foo'},
|
@@ -74,8 +75,11 @@ describe BooticClient::Entity do
|
|
74
75
|
end
|
75
76
|
|
76
77
|
it 'wraps object properties as entities' do
|
78
|
+
expect(entity.an_object).to be_a described_class
|
77
79
|
expect(entity.an_object.name).to eql('Foobar')
|
78
80
|
expect(entity.an_object.age).to eql(22)
|
81
|
+
expect(entity.an_object.another_object).to be_a described_class
|
82
|
+
expect(entity.an_object.another_object.foo).to eq 'bar'
|
79
83
|
end
|
80
84
|
|
81
85
|
it 'has a #properties object' do
|
@@ -160,7 +164,7 @@ describe BooticClient::Entity do
|
|
160
164
|
let(:next_page) { BooticClient::Entity.new({'page' => 2}, client) }
|
161
165
|
|
162
166
|
it 'exposes link target resources as normal properties' do
|
163
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2',
|
167
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2', {}).and_return next_page
|
164
168
|
entity.next.tap do |next_entity|
|
165
169
|
expect(next_entity).to be_kind_of(BooticClient::Entity)
|
166
170
|
expect(next_entity.page).to eql(2)
|
@@ -168,7 +172,7 @@ describe BooticClient::Entity do
|
|
168
172
|
end
|
169
173
|
|
170
174
|
it 'takes optional URI parameters' do
|
171
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/search?q=foo',
|
175
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/search?q=foo', {}).and_return next_page
|
172
176
|
entity.search(q: 'foo').tap do |next_entity|
|
173
177
|
expect(next_entity).to be_kind_of(BooticClient::Entity)
|
174
178
|
expect(next_entity.page).to eql(2)
|
@@ -222,8 +226,8 @@ describe BooticClient::Entity do
|
|
222
226
|
let(:page_2) { BooticClient::Entity.new(page_2_data, client) }
|
223
227
|
|
224
228
|
it 'lazily enumerates entries across pages, making as little requests as possible' do
|
225
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2',
|
226
|
-
expect(client).to_not receive(:request_and_wrap).with(:get, '/foo?page=3',
|
229
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2', {}).and_return page_2
|
230
|
+
expect(client).to_not receive(:request_and_wrap).with(:get, '/foo?page=3', {})
|
227
231
|
results = entity.full_set.first(4)
|
228
232
|
titles = results.map(&:title)
|
229
233
|
expect(titles).to match_array(['iPhone 4', 'iPhone 5', 'Item 3', 'Item 4'])
|
data/spec/relation_spec.rb
CHANGED
@@ -26,7 +26,7 @@ describe BooticClient::Relation do
|
|
26
26
|
|
27
27
|
describe 'running GET by default' do
|
28
28
|
it 'fetches data and returns entity' do
|
29
|
-
allow(client).to receive(:request_and_wrap).with(:get, '/foo/bars',
|
29
|
+
allow(client).to receive(:request_and_wrap).with(:get, '/foo/bars', {}).and_return entity
|
30
30
|
expect(relation.run).to eql(entity)
|
31
31
|
end
|
32
32
|
|
@@ -42,7 +42,7 @@ describe BooticClient::Relation do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'passes query string to client' do
|
45
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foos/bar',
|
45
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/bar', id: 2, q: 'test', page: 2).and_return entity
|
46
46
|
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
47
47
|
end
|
48
48
|
end
|
@@ -61,7 +61,7 @@ describe BooticClient::Relation do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'works with defaults' do
|
64
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foos/123',
|
64
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/123', {}).and_return entity
|
65
65
|
expect(relation.run(id: 123)).to eql(entity)
|
66
66
|
end
|
67
67
|
|
@@ -70,7 +70,7 @@ describe BooticClient::Relation do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'interpolates tokens' do
|
73
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foos/2?q=test&page=2',
|
73
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/2?q=test&page=2', {}).and_return entity
|
74
74
|
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
75
75
|
end
|
76
76
|
|
@@ -92,7 +92,7 @@ describe BooticClient::Relation do
|
|
92
92
|
client,
|
93
93
|
)
|
94
94
|
|
95
|
-
expect(client).to receive(:request_and_wrap).with(:get, '/foos/2?q=test&page=3',
|
95
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/2?q=test&page=3', {foo: 1}).and_return entity
|
96
96
|
|
97
97
|
relation.run(id: 2, q: 'test', page: 3, foo: 1)
|
98
98
|
|
@@ -106,12 +106,12 @@ describe BooticClient::Relation do
|
|
106
106
|
let(:relation_templated) { BooticClient::Relation.new({'href' => '/foo/{bars}', 'templated' => true, 'type' => 'application/json', 'name' => 'self', 'method' => 'post'}, client) }
|
107
107
|
|
108
108
|
it 'POSTS data and returns resulting entity' do
|
109
|
-
allow(client).to receive(:request_and_wrap).with(:post, '/foo/bars',
|
109
|
+
allow(client).to receive(:request_and_wrap).with(:post, '/foo/bars', {}).and_return entity
|
110
110
|
expect(relation.run).to eql(entity)
|
111
111
|
end
|
112
112
|
|
113
113
|
it 'interpolates templated URLs and sends remaining as BODY' do
|
114
|
-
allow(client).to receive(:request_and_wrap).with(:post, '/foo/123',
|
114
|
+
allow(client).to receive(:request_and_wrap).with(:post, '/foo/123', {foo: 'bar'}).and_return entity
|
115
115
|
expect(relation_templated.run(bars: 123, foo: 'bar')).to eql(entity)
|
116
116
|
end
|
117
117
|
end
|
@@ -120,7 +120,7 @@ describe BooticClient::Relation do
|
|
120
120
|
let(:relation) { BooticClient::Relation.new({'href' => '/foo/bars', 'type' => 'application/json', 'name' => 'self', 'method' => 'delete'}, client) }
|
121
121
|
|
122
122
|
it 'DELETEs data and returns resulting entity' do
|
123
|
-
allow(client).to receive(:request_and_wrap).with(:delete, '/foo/bars',
|
123
|
+
allow(client).to receive(:request_and_wrap).with(:delete, '/foo/bars', {}).and_return entity
|
124
124
|
expect(relation.run).to eql(entity)
|
125
125
|
end
|
126
126
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bootic_client/response_handlers'
|
3
|
+
|
4
|
+
describe BooticClient::ResponseHandlers::Set do
|
5
|
+
let(:h1) do
|
6
|
+
Proc.new do |resp, _client|
|
7
|
+
if resp.headers['Content-Type'] =~ /json/
|
8
|
+
'H1'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
let(:h2) do
|
13
|
+
Proc.new do |resp, _client|
|
14
|
+
if resp.headers['Content-Type'] =~ /text/
|
15
|
+
'H2'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
let(:h3) do
|
20
|
+
Proc.new do |resp, _client|
|
21
|
+
if resp.headers['Content-Type'] =~ /json/
|
22
|
+
'H3'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'initializes with default handlers' do
|
28
|
+
set = described_class.new([h1])
|
29
|
+
expect(set.to_a).to eq [h1]
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#resolve' do
|
33
|
+
it 'finds first matching handler and returns result' do
|
34
|
+
set = described_class.new([h1, h2, h3])
|
35
|
+
|
36
|
+
text_resp = instance_double(::Faraday::Response, headers: {'Content-Type' => 'text/plain'})
|
37
|
+
expect(set.resolve(text_resp, nil)).to eq 'H2'
|
38
|
+
|
39
|
+
json_resp = instance_double(::Faraday::Response, headers: {'Content-Type' => 'application/json'})
|
40
|
+
expect(set.resolve(json_resp, nil)).to eq 'H1'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns raw response if no handler found' do
|
44
|
+
set = described_class.new([h1, h2, h3])
|
45
|
+
img_resp = instance_double(::Faraday::Response, headers: {'Content-Type' => 'image/jpeg'})
|
46
|
+
expect(set.resolve(img_resp, nil)).to eq img_resp
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#append' do
|
51
|
+
it 'adds handlers' do
|
52
|
+
set = described_class.new
|
53
|
+
|
54
|
+
text_resp = instance_double(::Faraday::Response, headers: {'Content-Type' => 'text/plain'})
|
55
|
+
expect(set.resolve(text_resp, nil)).to eq text_resp
|
56
|
+
|
57
|
+
set.append(h2)
|
58
|
+
expect(set.resolve(text_resp, nil)).to eq 'H2'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#prepend' do
|
63
|
+
it 'prepends handlers' do
|
64
|
+
set = described_class.new([h1, h2])
|
65
|
+
|
66
|
+
json_resp = instance_double(::Faraday::Response, headers: {'Content-Type' => 'application/json'})
|
67
|
+
expect(set.resolve(json_resp, nil)).to eq 'H1'
|
68
|
+
|
69
|
+
set.prepend(h3)
|
70
|
+
expect(set.resolve(json_resp, nil)).to eq 'H3'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bootic_client/strategies/strategy'
|
3
|
+
|
4
|
+
describe BooticClient::Strategies::Strategy do
|
5
|
+
require 'webmock/rspec'
|
6
|
+
|
7
|
+
let(:config) { BooticClient::Configuration.new }
|
8
|
+
subject(:strategy) { described_class.new(config) }
|
9
|
+
|
10
|
+
describe '#request_and_wrap' do
|
11
|
+
context 'response is JSON' do
|
12
|
+
let(:resp_data) do
|
13
|
+
{
|
14
|
+
'name' => 'Jason'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
stub_request(:get, 'https://a.server.com/foo')
|
20
|
+
.to_return(status: 200, body: JSON.dump(resp_data), headers: {'Content-Type' => 'application/json'})
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns the raw response' do
|
24
|
+
resp = strategy.request_and_wrap(:get, 'https://a.server.com/foo')
|
25
|
+
expect(resp).to be_a(BooticClient::Entity)
|
26
|
+
expect(resp.name).to eq 'Jason'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'response type is not handled' do
|
31
|
+
before do
|
32
|
+
stub_request(:get, 'https://a.server.com/foo')
|
33
|
+
.to_return(status: 200, body: 'abc', headers: {'Content-Type' => 'text/plain'})
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the raw response' do
|
37
|
+
resp = strategy.request_and_wrap(:get, 'https://a.server.com/foo')
|
38
|
+
expect(resp).to be_a(Faraday::Response)
|
39
|
+
expect(resp.body).to eq 'abc'
|
40
|
+
expect(resp.status).to eq 200
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'uses available request handlers' do
|
44
|
+
custom_resp = Struct.new(:message)
|
45
|
+
|
46
|
+
config.response_handlers.append(Proc.new { |resp, client|
|
47
|
+
if resp.headers['Content-Type'] == 'text/plain'
|
48
|
+
custom_resp.new(resp.body)
|
49
|
+
end
|
50
|
+
})
|
51
|
+
|
52
|
+
resp = strategy.request_and_wrap(:get, 'https://a.server.com/foo')
|
53
|
+
expect(resp).to be_a custom_resp
|
54
|
+
expect(resp.message).to eq 'abc'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'response is an image' do
|
59
|
+
before do
|
60
|
+
stub_request(:get, 'https://a.server.com/a/b/foo.jpg')
|
61
|
+
.to_return(status: 200, body: 'abc', headers: {'Content-Type' => 'image/jpeg'})
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns an IO-like object' do
|
65
|
+
img = strategy.request_and_wrap(:get, 'https://a.server.com/a/b/foo.jpg')
|
66
|
+
expect(img.io).to be_a StringIO
|
67
|
+
expect(img.read).to eq 'abc'
|
68
|
+
expect(img.file_name).to eq 'foo.jpg'
|
69
|
+
expect(img.mime_type).to eq 'image/jpeg'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -183,9 +183,11 @@ files:
|
|
183
183
|
- bootic_client.gemspec
|
184
184
|
- lib/bootic_client.rb
|
185
185
|
- lib/bootic_client/client.rb
|
186
|
+
- lib/bootic_client/configuration.rb
|
186
187
|
- lib/bootic_client/entity.rb
|
187
188
|
- lib/bootic_client/errors.rb
|
188
189
|
- lib/bootic_client/relation.rb
|
190
|
+
- lib/bootic_client/response_handlers.rb
|
189
191
|
- lib/bootic_client/stores/memcache.rb
|
190
192
|
- lib/bootic_client/strategies/authorized.rb
|
191
193
|
- lib/bootic_client/strategies/basic_auth.rb
|
@@ -206,7 +208,9 @@ files:
|
|
206
208
|
- spec/fixtures/file.gif
|
207
209
|
- spec/memcache_storage_spec.rb
|
208
210
|
- spec/relation_spec.rb
|
211
|
+
- spec/response_handlers_spec.rb
|
209
212
|
- spec/spec_helper.rb
|
213
|
+
- spec/strategy_spec.rb
|
210
214
|
- spec/whiny_uri_spec.rb
|
211
215
|
homepage: https://developers.bootic.net
|
212
216
|
licenses:
|
@@ -228,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
228
232
|
version: '0'
|
229
233
|
requirements: []
|
230
234
|
rubyforge_project:
|
231
|
-
rubygems_version: 2.
|
235
|
+
rubygems_version: 2.7.7
|
232
236
|
signing_key:
|
233
237
|
specification_version: 4
|
234
238
|
summary: Official Ruby client for the Bootic API
|
@@ -244,5 +248,7 @@ test_files:
|
|
244
248
|
- spec/fixtures/file.gif
|
245
249
|
- spec/memcache_storage_spec.rb
|
246
250
|
- spec/relation_spec.rb
|
251
|
+
- spec/response_handlers_spec.rb
|
247
252
|
- spec/spec_helper.rb
|
253
|
+
- spec/strategy_spec.rb
|
248
254
|
- spec/whiny_uri_spec.rb
|