bootic_client 0.0.3 → 0.0.4
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 +1 -1
- data/lib/bootic_client/client.rb +32 -28
- data/lib/bootic_client/relation.rb +3 -11
- data/lib/bootic_client/strategies/strategy.rb +6 -6
- data/lib/bootic_client/version.rb +1 -1
- data/spec/authorized_strategy_spec.rb +27 -4
- data/spec/client_credentials_strategy_spec.rb +2 -2
- data/spec/client_spec.rb +53 -24
- data/spec/entity_spec.rb +2 -2
- data/spec/relation_spec.rb +11 -5
- 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: 7f0b175f26ae3c07540c632a0e07488ab48256a2
|
4
|
+
data.tar.gz: 6dfc0cc96fdadce33457631b507ea355b0eefff9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cff8a48dc27c6b5362df02749e63de27258ee8cf58091ee9bf3cac17079eea3a21c138a16245f16b96d5b2e22215411e97a5486c299944197fe0d8a2e66e8aa7
|
7
|
+
data.tar.gz: c82c7be167dd5519bc7750a60dabf4d0af95c2372f284817ace9d9e4a508216a0de8f4fbd0523ca6e3a288a9b0aef93c44e4d71a1a51276dac731ab7b78fabee
|
data/README.md
CHANGED
@@ -127,7 +127,7 @@ All resource link relations include a "docs" URL so you can learn more about tha
|
|
127
127
|
|
128
128
|
```ruby
|
129
129
|
shop = root.shops.first
|
130
|
-
puts shop.rels[:create_product] # => 'https://developers.bootic.net/rels/create_product'
|
130
|
+
puts shop.rels[:create_product].docs # => 'https://developers.bootic.net/rels/create_product'
|
131
131
|
```
|
132
132
|
|
133
133
|
## Cache storage
|
data/lib/bootic_client/client.rb
CHANGED
@@ -11,10 +11,9 @@ module BooticClient
|
|
11
11
|
USER_AGENT = "[BooticClient v#{VERSION}] Ruby-#{RUBY_VERSION} - #{RUBY_PLATFORM}".freeze
|
12
12
|
JSON_MIME = 'application/json'.freeze
|
13
13
|
|
14
|
-
attr_reader :options
|
14
|
+
attr_reader :options
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
@api_root = api_root
|
16
|
+
def initialize(options = {}, &block)
|
18
17
|
@options = {
|
19
18
|
access_token: nil,
|
20
19
|
logging: false
|
@@ -25,40 +24,38 @@ module BooticClient
|
|
25
24
|
conn &block if block_given?
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
|
27
|
+
def get(href, query = {})
|
28
|
+
validated_request!(:get, href) do |req|
|
29
|
+
req.params.update(query)
|
30
|
+
end
|
30
31
|
end
|
31
32
|
|
32
|
-
def
|
33
|
-
|
33
|
+
def post(href, payload = {})
|
34
|
+
validated_request!(:post, href) do |req|
|
35
|
+
req.body = JSON.dump(payload)
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
req.url href
|
40
|
-
req.headers.update request_headers
|
41
|
-
req.params.update(query)
|
42
|
-
end
|
39
|
+
def put(href, payload = {})
|
40
|
+
validated_request!(:put, href) do |req|
|
41
|
+
req.body = JSON.dump(payload)
|
43
42
|
end
|
44
43
|
end
|
45
44
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
req.url href
|
50
|
-
req.headers.update request_headers
|
51
|
-
req.headers['Accept'] = JSON_MIME
|
52
|
-
req.headers['Content-Type'] = JSON_MIME
|
53
|
-
req.body = JSON.dump(payload)
|
54
|
-
end
|
45
|
+
def patch(href, payload = {})
|
46
|
+
validated_request!(:patch, href) do |req|
|
47
|
+
req.body = JSON.dump(payload)
|
55
48
|
end
|
56
49
|
end
|
57
50
|
|
51
|
+
def delete(href, query = {})
|
52
|
+
validated_request!(:delete, href)
|
53
|
+
end
|
54
|
+
|
58
55
|
protected
|
59
56
|
|
60
57
|
def conn(&block)
|
61
|
-
@conn ||= Faraday.new
|
58
|
+
@conn ||= Faraday.new do |f|
|
62
59
|
cache_options = {shared_cache: false, store: options[:cache_store]}
|
63
60
|
cache_options[:logger] = options[:logger] if options[:logging]
|
64
61
|
|
@@ -73,13 +70,20 @@ module BooticClient
|
|
73
70
|
def request_headers
|
74
71
|
{
|
75
72
|
'Authorization' => "Bearer #{options[:access_token]}",
|
76
|
-
'User-Agent' => USER_AGENT
|
73
|
+
'User-Agent' => USER_AGENT,
|
74
|
+
'Accept' => JSON_MIME,
|
75
|
+
'Content-Type' => JSON_MIME
|
77
76
|
}
|
78
77
|
end
|
79
78
|
|
80
|
-
def
|
79
|
+
def validated_request!(verb, href, &block)
|
81
80
|
validate_request!
|
82
|
-
resp =
|
81
|
+
resp = conn.send(verb) do |req|
|
82
|
+
req.url href
|
83
|
+
req.headers.update request_headers
|
84
|
+
yield req if block_given?
|
85
|
+
end
|
86
|
+
|
83
87
|
raise_if_invalid! resp
|
84
88
|
resp
|
85
89
|
end
|
@@ -96,4 +100,4 @@ module BooticClient
|
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
99
|
-
end
|
103
|
+
end
|
@@ -44,21 +44,13 @@ module BooticClient
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def run(opts = {})
|
47
|
-
self.send(transport_method, opts)
|
48
|
-
end
|
49
|
-
|
50
|
-
def get(opts = {})
|
51
47
|
if templated?
|
52
|
-
client.
|
48
|
+
client.request_and_wrap transport_method.to_sym, uri.expand(opts), wrapper_class, opts
|
53
49
|
else
|
54
|
-
client.
|
50
|
+
client.request_and_wrap transport_method.to_sym, href, wrapper_class, opts
|
55
51
|
end
|
56
52
|
end
|
57
53
|
|
58
|
-
def post(opts = {})
|
59
|
-
client.post_and_wrap href, wrapper_class, opts
|
60
|
-
end
|
61
|
-
|
62
54
|
def self.expand(href, opts = {})
|
63
55
|
URITemplate.new(href).expand(opts)
|
64
56
|
end
|
@@ -71,4 +63,4 @@ module BooticClient
|
|
71
63
|
end
|
72
64
|
end
|
73
65
|
|
74
|
-
end
|
66
|
+
end
|
@@ -13,17 +13,17 @@ module BooticClient
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def root
|
16
|
-
get config.api_root
|
16
|
+
request_and_wrap :get, config.api_root, Entity
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def request_and_wrap(request_method, href, wrapper_class, payload = {})
|
20
20
|
begin
|
21
|
-
client.
|
21
|
+
wrapper_class.new client.send(request_method, href, payload).body, self
|
22
22
|
rescue TokenError => e
|
23
23
|
new_token = get_token
|
24
24
|
client.options[:access_token] = new_token
|
25
25
|
on_new_token.call new_token
|
26
|
-
client.
|
26
|
+
wrapper_class.new client.send(request_method, href, payload).body, self
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -52,8 +52,8 @@ module BooticClient
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def client
|
55
|
-
@client ||= Client.new(
|
55
|
+
@client ||= Client.new(options)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
59
|
-
end
|
59
|
+
end
|
@@ -18,7 +18,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
18
18
|
|
19
19
|
def stub_api_root(access_token, status, body)
|
20
20
|
stub_request(:get, "https://api.bootic.net/v1").
|
21
|
-
with(headers: {'Accept'=>'
|
21
|
+
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer #{access_token}"}).
|
22
22
|
to_return(status: status, :body => JSON.dump(body))
|
23
23
|
end
|
24
24
|
|
@@ -45,7 +45,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
45
45
|
let(:root_data) {
|
46
46
|
{
|
47
47
|
'_links' => {
|
48
|
-
'shops' => {'href' => 'https://api.bootic.net/v1/
|
48
|
+
'shops' => {'href' => 'https://api.bootic.net/v1/shops'}
|
49
49
|
},
|
50
50
|
'message' => "Hello!"
|
51
51
|
}
|
@@ -109,7 +109,7 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
109
109
|
@successful_root_request = stub_api_root('foobar', 200, root_data)
|
110
110
|
end
|
111
111
|
|
112
|
-
it 'attempts unauthorised API request, refreshes token from auth
|
112
|
+
it 'attempts unauthorised API request, refreshes token from auth and tries again' do
|
113
113
|
root = client.root
|
114
114
|
expect(@failed_root_request).to have_been_requested
|
115
115
|
expect(@auth_request).to have_been_requested
|
@@ -121,5 +121,28 @@ describe 'BooticClient::Strategies::Authorized' do
|
|
121
121
|
expect(store[:access_token]).to eql('foobar')
|
122
122
|
end
|
123
123
|
end
|
124
|
+
|
125
|
+
context 'expired token, other resources' do
|
126
|
+
before do
|
127
|
+
stub_api_root('abc', 200, root_data)
|
128
|
+
@unauthorized_request = stub_request(:get, "https://api.bootic.net/v1/shops").
|
129
|
+
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer abc"}).
|
130
|
+
to_return(status: 401, :body => JSON.dump(message: 'authorized'))
|
131
|
+
@auth_request = stub_auth('abc', 200, access_token: 'validtoken')
|
132
|
+
|
133
|
+
@authorized_request = stub_request(:get, "https://api.bootic.net/v1/shops").
|
134
|
+
with(headers: {'Accept'=>'application/json', 'Authorization' => "Bearer validtoken"}).
|
135
|
+
to_return(status: 200, :body => JSON.dump(title: 'All shops'))
|
136
|
+
@root = client.root
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'attempts unauthorized API request to shops, refreshes token and tries again' do
|
140
|
+
shops = @root.shops
|
141
|
+
expect(@unauthorized_request).to have_been_requested
|
142
|
+
expect(@auth_request).to have_been_requested
|
143
|
+
expect(@authorized_request).to have_been_requested
|
144
|
+
expect(shops.title).to eql('All shops')
|
145
|
+
end
|
146
|
+
end
|
124
147
|
end
|
125
|
-
end
|
148
|
+
end
|
@@ -32,7 +32,7 @@ describe 'BooticClient::Strategies::ClientCredentials' do
|
|
32
32
|
|
33
33
|
def stub_api_root(access_token, status, body)
|
34
34
|
stub_request(:get, "https://api.bootic.net/v1").
|
35
|
-
with(headers: {'Accept'=>'
|
35
|
+
with(headers: {'Accept'=>'application/json', 'Authorization'=>"Bearer #{access_token}"}).
|
36
36
|
to_return(status: status, :body => JSON.dump(body))
|
37
37
|
end
|
38
38
|
|
@@ -94,4 +94,4 @@ describe 'BooticClient::Strategies::ClientCredentials' do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
-
end
|
97
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -6,7 +6,10 @@ describe BooticClient::Client do
|
|
6
6
|
|
7
7
|
describe 'valid response' do
|
8
8
|
let(:root_url) { 'https://api.bootic.net/v1' }
|
9
|
-
let(:client) { BooticClient::Client.new(
|
9
|
+
let(:client) { BooticClient::Client.new(access_token: 'xxx') }
|
10
|
+
let(:request_headers) {
|
11
|
+
{'Accept' => 'application/json', 'Authorization' => "Bearer xxx"}
|
12
|
+
}
|
10
13
|
let(:response_headers) {
|
11
14
|
{'Content-Type' => 'application/json', 'Last-Modified' => 'Sat, 07 Jun 2014 12:10:33 GMT'}
|
12
15
|
}
|
@@ -60,7 +63,7 @@ describe BooticClient::Client do
|
|
60
63
|
describe 'no access token' do
|
61
64
|
it 'raises error' do
|
62
65
|
expect{
|
63
|
-
BooticClient::Client.new
|
66
|
+
BooticClient::Client.new.get(root_url)
|
64
67
|
}.to raise_error(BooticClient::NoAccessTokenError)
|
65
68
|
end
|
66
69
|
end
|
@@ -120,35 +123,61 @@ describe BooticClient::Client do
|
|
120
123
|
|
121
124
|
end
|
122
125
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
126
|
+
context 'HTTP verbs' do
|
127
|
+
describe 'GET' do
|
128
|
+
|
129
|
+
before do
|
130
|
+
stub_request(:get, root_url)
|
131
|
+
.with(query: {foo: 'bar'}, headers: request_headers)
|
132
|
+
.to_return(status: 200, body: JSON.dump(root_data), headers: response_headers)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'GETs response' do
|
136
|
+
expect(client.get(root_url, foo: 'bar').body['message']).to eql('Hello!')
|
137
|
+
end
|
128
138
|
end
|
129
139
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
140
|
+
describe 'POST' do
|
141
|
+
before do
|
142
|
+
stub_request(:post, root_url)
|
143
|
+
.with(body: JSON.dump({foo: 'bar'}), headers: request_headers)
|
144
|
+
.to_return(status: 201, body: JSON.dump(root_data), headers: response_headers)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'POSTs request and parses response' do
|
148
|
+
expect(client.post(root_url, foo: 'bar').body['message']).to eql('Hello!')
|
149
|
+
end
|
135
150
|
end
|
136
|
-
|
151
|
+
|
152
|
+
[:put, :patch].each do |verb|
|
153
|
+
describe verb.to_s.upcase do
|
154
|
+
before do
|
155
|
+
stub_request(verb, root_url)
|
156
|
+
.with(body: JSON.dump({foo: 'bar'}), headers: request_headers)
|
157
|
+
.to_return(status: 200, body: JSON.dump(root_data), headers: response_headers)
|
158
|
+
end
|
137
159
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
.to_return(status: 200, body: JSON.dump(root_data), headers: response_headers)
|
160
|
+
it "#{verb.to_s.upcase}s request and parses response" do
|
161
|
+
expect(client.send(verb, root_url, foo: 'bar').body['message']).to eql('Hello!')
|
162
|
+
end
|
163
|
+
end
|
143
164
|
end
|
165
|
+
|
166
|
+
context 'DELETE' do
|
167
|
+
before do
|
168
|
+
@delete_requst = stub_request(:delete, root_url)
|
169
|
+
.with(headers: request_headers)
|
170
|
+
.to_return(status: 200, body: JSON.dump(root_data), headers: response_headers)
|
171
|
+
end
|
144
172
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
expect(client.post_and_wrap(root_url, wrapper, foo: 'bar')).to eql(entity)
|
173
|
+
it 'DELETEs request and parses response' do
|
174
|
+
expect(client.send(:delete, root_url).status).to eql(200)
|
175
|
+
expect(@delete_requst).to have_been_requested
|
176
|
+
end
|
150
177
|
end
|
178
|
+
|
151
179
|
end
|
152
180
|
|
181
|
+
|
153
182
|
end
|
154
|
-
end
|
183
|
+
end
|
data/spec/entity_spec.rb
CHANGED
@@ -139,7 +139,7 @@ describe BooticClient::Entity do
|
|
139
139
|
let(:next_page) { BooticClient::Entity.new({'page' => 2}, client) }
|
140
140
|
|
141
141
|
it 'exposes link target resources as normal properties' do
|
142
|
-
expect(client).to receive(:
|
142
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2', BooticClient::Entity, {}).and_return next_page
|
143
143
|
entity.next.tap do |next_entity|
|
144
144
|
expect(next_entity).to be_kind_of(BooticClient::Entity)
|
145
145
|
expect(next_entity.page).to eql(2)
|
@@ -147,7 +147,7 @@ describe BooticClient::Entity do
|
|
147
147
|
end
|
148
148
|
|
149
149
|
it 'takes optional URI parameters' do
|
150
|
-
expect(client).to receive(:
|
150
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/search?q=foo', BooticClient::Entity, {q: 'foo'}).and_return next_page
|
151
151
|
entity.search(q: 'foo').tap do |next_entity|
|
152
152
|
expect(next_entity).to be_kind_of(BooticClient::Entity)
|
153
153
|
expect(next_entity.page).to eql(2)
|
data/spec/relation_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe BooticClient::Relation do
|
|
18
18
|
|
19
19
|
describe 'running GET by default' do
|
20
20
|
it 'fetches data and returns entity' do
|
21
|
-
client.stub(:
|
21
|
+
client.stub(:request_and_wrap).with(:get, '/foo/bars', BooticClient::Entity, {}).and_return entity
|
22
22
|
expect(relation.run).to eql(entity)
|
23
23
|
end
|
24
24
|
|
@@ -30,7 +30,7 @@ describe BooticClient::Relation do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'passes query string to client' do
|
33
|
-
expect(client).to receive(:
|
33
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/bar', BooticClient::Entity, id: 2, q: 'test', page: 2).and_return entity
|
34
34
|
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
35
35
|
end
|
36
36
|
end
|
@@ -43,12 +43,12 @@ describe BooticClient::Relation do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'works with defaults' do
|
46
|
-
expect(client).to receive(:
|
46
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/', BooticClient::Entity, {}).and_return entity
|
47
47
|
expect(relation.run).to eql(entity)
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'interpolates tokens' do
|
51
|
-
expect(client).to receive(:
|
51
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foos/2?q=test&page=2', BooticClient::Entity, {id:2,q:'test',page:2}).and_return entity
|
52
52
|
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
53
53
|
end
|
54
54
|
end
|
@@ -56,11 +56,17 @@ describe BooticClient::Relation do
|
|
56
56
|
|
57
57
|
describe 'POST' do
|
58
58
|
let(:relation) { BooticClient::Relation.new({'href' => '/foo/bars', 'type' => 'application/json', 'name' => 'self', 'method' => 'post'}, client) }
|
59
|
+
let(:relation_templated) { BooticClient::Relation.new({'href' => '/foo/{bars}', 'templated' => true, 'type' => 'application/json', 'name' => 'self', 'method' => 'post'}, client) }
|
59
60
|
|
60
61
|
it 'POSTS data and returns resulting entity' do
|
61
|
-
client.stub(:
|
62
|
+
client.stub(:request_and_wrap).with(:post, '/foo/bars', BooticClient::Entity, {}).and_return entity
|
62
63
|
expect(relation.run).to eql(entity)
|
63
64
|
end
|
65
|
+
|
66
|
+
it 'interpolates templated URLs' do
|
67
|
+
client.stub(:request_and_wrap).with(:post, '/foo/123', BooticClient::Entity, {foo: 'bar', bars: 123}).and_return entity
|
68
|
+
expect(relation_templated.run(bars: 123, foo: 'bar')).to eql(entity)
|
69
|
+
end
|
64
70
|
end
|
65
71
|
end
|
66
72
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|