bootic_client 0.0.2 → 0.0.3
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 +42 -0
- data/lib/bootic_client/client.rb +36 -10
- data/lib/bootic_client/entity.rb +5 -1
- data/lib/bootic_client/relation.rb +14 -0
- data/lib/bootic_client/version.rb +1 -1
- data/spec/client_spec.rb +15 -0
- data/spec/entity_spec.rb +5 -0
- data/spec/relation_spec.rb +36 -25
- 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: 727d4868928f07359322ccc49dfed3d20edfae0a
|
|
4
|
+
data.tar.gz: 7626595ffbef58af0b052b6a74c3dc220b3e32ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56d7d5c1c16e19f61f5d9e59de8b494f3ba20f8510c7eaba5d21a1d2aeab1a97eaf125c80c5923b524dd422c6f75904ddb5f20fd3c98f4d52765dce18e2e1071
|
|
7
|
+
data.tar.gz: 8fb91e3fcfbed8a09bf3903e3952f9078554b381d657eec9d92b08b1e955741a40c5e3c46b2f67dada5f3d0cac45feb1a337d2536c25a762606835a27af2ef94
|
data/README.md
CHANGED
|
@@ -88,6 +88,48 @@ client = BooticClient.client(:client_credentials, scope: 'admin', access_token:
|
|
|
88
88
|
end
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
## Non GET links
|
|
92
|
+
|
|
93
|
+
Most resource links lead to `GET` resources, but some will expect `POST`, `PUT`, `DELETE` or others.
|
|
94
|
+
|
|
95
|
+
The Bootic API encodes this information in its link metadata so the client will do the right thing. The following example creates a new product on your first shop:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
bootic = BooticClient.client(:client_credentials)
|
|
99
|
+
|
|
100
|
+
root = bootic.root
|
|
101
|
+
|
|
102
|
+
shop = root.shops.first
|
|
103
|
+
|
|
104
|
+
if shop.can?(:create_product)
|
|
105
|
+
product = shop.create_product(
|
|
106
|
+
title: 'A shiny new product',
|
|
107
|
+
price: 122332,
|
|
108
|
+
status: "visible",
|
|
109
|
+
collecton_names: ["Featured products", "Homepage"],
|
|
110
|
+
variants: [
|
|
111
|
+
{
|
|
112
|
+
title: 'First variant',
|
|
113
|
+
sku: 'F23332-X',
|
|
114
|
+
available_if_no_stock: 1,
|
|
115
|
+
stock: 12
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
puts product.rels[:web].href # => 'http://acme.bootic.net/products/a-shiny-new-product'
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Relation docs
|
|
125
|
+
|
|
126
|
+
All resource link relations include a "docs" URL so you can learn more about that particular resource.
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
shop = root.shops.first
|
|
130
|
+
puts shop.rels[:create_product] # => 'https://developers.bootic.net/rels/create_product'
|
|
131
|
+
```
|
|
132
|
+
|
|
91
133
|
## Cache storage
|
|
92
134
|
|
|
93
135
|
`BooticClient` honours HTTP caching headers included in API responses (such as `ETag` and `Last-Modified`).
|
data/lib/bootic_client/client.rb
CHANGED
|
@@ -9,6 +9,7 @@ module BooticClient
|
|
|
9
9
|
class Client
|
|
10
10
|
|
|
11
11
|
USER_AGENT = "[BooticClient v#{VERSION}] Ruby-#{RUBY_VERSION} - #{RUBY_PLATFORM}".freeze
|
|
12
|
+
JSON_MIME = 'application/json'.freeze
|
|
12
13
|
|
|
13
14
|
attr_reader :options, :api_root
|
|
14
15
|
|
|
@@ -28,19 +29,30 @@ module BooticClient
|
|
|
28
29
|
wrapper_class.new get(href, query).body, self
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def
|
|
32
|
-
|
|
32
|
+
def post_and_wrap(href, wrapper_class, payload = {})
|
|
33
|
+
wrapper_class.new post(href, payload).body, self
|
|
34
|
+
end
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
req
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
def get(href, query = {})
|
|
37
|
+
validated! do
|
|
38
|
+
conn.get do |req|
|
|
39
|
+
req.url href
|
|
40
|
+
req.headers.update request_headers
|
|
41
|
+
req.params.update(query)
|
|
42
|
+
end
|
|
39
43
|
end
|
|
44
|
+
end
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
def post(href, payload = {})
|
|
47
|
+
validated! do
|
|
48
|
+
conn.post do |req|
|
|
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
|
|
55
|
+
end
|
|
44
56
|
end
|
|
45
57
|
|
|
46
58
|
protected
|
|
@@ -58,6 +70,20 @@ module BooticClient
|
|
|
58
70
|
end
|
|
59
71
|
end
|
|
60
72
|
|
|
73
|
+
def request_headers
|
|
74
|
+
{
|
|
75
|
+
'Authorization' => "Bearer #{options[:access_token]}",
|
|
76
|
+
'User-Agent' => USER_AGENT
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validated!(&block)
|
|
81
|
+
validate_request!
|
|
82
|
+
resp = yield
|
|
83
|
+
raise_if_invalid! resp
|
|
84
|
+
resp
|
|
85
|
+
end
|
|
86
|
+
|
|
61
87
|
def validate_request!
|
|
62
88
|
raise NoAccessTokenError, "Missing access token" unless options[:access_token]
|
|
63
89
|
end
|
data/lib/bootic_client/entity.rb
CHANGED
|
@@ -23,6 +23,10 @@ module BooticClient
|
|
|
23
23
|
has_property?(prop_name) || has_entity?(prop_name) || has_rel?(prop_name)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def can?(rel_name)
|
|
27
|
+
has_rel? rel_name
|
|
28
|
+
end
|
|
29
|
+
|
|
26
30
|
def inspect
|
|
27
31
|
%(#<#{self.class.name} props: [#{properties.keys.join(', ')}] rels: [#{rels.keys.join(', ')}] entities: [#{entities.keys.join(', ')}]>)
|
|
28
32
|
end
|
|
@@ -55,7 +59,7 @@ module BooticClient
|
|
|
55
59
|
elsif has_entity?(name)
|
|
56
60
|
entities[name]
|
|
57
61
|
elsif has_rel?(name)
|
|
58
|
-
rels[name].
|
|
62
|
+
rels[name].run(*args)
|
|
59
63
|
else
|
|
60
64
|
super
|
|
61
65
|
end
|
|
@@ -5,6 +5,8 @@ module BooticClient
|
|
|
5
5
|
|
|
6
6
|
class Relation
|
|
7
7
|
|
|
8
|
+
GET = 'get'.freeze
|
|
9
|
+
|
|
8
10
|
def initialize(attrs, client, wrapper_class = Entity)
|
|
9
11
|
@attrs, @client, @wrapper_class = attrs, client, wrapper_class
|
|
10
12
|
end
|
|
@@ -37,6 +39,14 @@ module BooticClient
|
|
|
37
39
|
attrs['docs']
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
def transport_method
|
|
43
|
+
@transport_method ||= attrs['method'] || GET
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def run(opts = {})
|
|
47
|
+
self.send(transport_method, opts)
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
def get(opts = {})
|
|
41
51
|
if templated?
|
|
42
52
|
client.get_and_wrap uri.expand(opts), wrapper_class
|
|
@@ -45,6 +55,10 @@ module BooticClient
|
|
|
45
55
|
end
|
|
46
56
|
end
|
|
47
57
|
|
|
58
|
+
def post(opts = {})
|
|
59
|
+
client.post_and_wrap href, wrapper_class, opts
|
|
60
|
+
end
|
|
61
|
+
|
|
48
62
|
def self.expand(href, opts = {})
|
|
49
63
|
URITemplate.new(href).expand(opts)
|
|
50
64
|
end
|
data/spec/client_spec.rb
CHANGED
|
@@ -135,5 +135,20 @@ describe BooticClient::Client do
|
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
+
describe '#post_and_wrap' do
|
|
139
|
+
before do
|
|
140
|
+
stub_request(:post, root_url)
|
|
141
|
+
.with(body: JSON.dump({foo: 'bar'}), headers: {'Accept' => 'application/json', 'Content-Type' => 'application/json'})
|
|
142
|
+
.to_return(status: 200, body: JSON.dump(root_data), headers: response_headers)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'wraps JSON response in entity' do
|
|
146
|
+
wrapper = double('Wrapper Class')
|
|
147
|
+
entity = double('Entity')
|
|
148
|
+
expect(wrapper).to receive(:new).with(root_data, client).and_return entity
|
|
149
|
+
expect(client.post_and_wrap(root_url, wrapper, foo: 'bar')).to eql(entity)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
138
153
|
end
|
|
139
154
|
end
|
data/spec/entity_spec.rb
CHANGED
|
@@ -111,6 +111,11 @@ describe BooticClient::Entity do
|
|
|
111
111
|
expect(entity.has?(:next)).to eql(true)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
it 'responds to #can? for link relations' do
|
|
115
|
+
expect(entity.can?(:next)).to eql(true)
|
|
116
|
+
expect(entity.can?(:foo)).to eql(false)
|
|
117
|
+
end
|
|
118
|
+
|
|
114
119
|
it 'builds relation objects' do
|
|
115
120
|
expect(entity.rels[:next]).to be_kind_of(BooticClient::Relation)
|
|
116
121
|
expect(entity.rels[:next].href).to eql('/foo?page=2')
|
data/spec/relation_spec.rb
CHANGED
|
@@ -13,42 +13,53 @@ describe BooticClient::Relation do
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
describe '#
|
|
16
|
+
describe '#run' do
|
|
17
17
|
let(:entity) { BooticClient::Entity.new({'title' => 'Foobar'}, client) }
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
describe 'running GET by default' do
|
|
20
|
+
it 'fetches data and returns entity' do
|
|
21
|
+
client.stub(:get_and_wrap).with('/foo/bars', BooticClient::Entity, {}).and_return entity
|
|
22
|
+
expect(relation.run).to eql(entity)
|
|
23
|
+
end
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
context 'without URI templates' do
|
|
26
|
+
let(:relation) { BooticClient::Relation.new({'href' => '/foos/bar', 'type' => 'application/json', 'title' => 'A relation'}, client) }
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
it 'is not templated' do
|
|
29
|
+
expect(relation.templated?).to eql(false)
|
|
30
|
+
end
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
it 'passes query string to client' do
|
|
33
|
+
expect(client).to receive(:get_and_wrap).with('/foos/bar', BooticClient::Entity, id: 2, q: 'test', page: 2).and_return entity
|
|
34
|
+
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
|
35
|
+
end
|
|
34
36
|
end
|
|
35
|
-
end
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
context 'with URI templates' do
|
|
39
|
+
let(:relation) { BooticClient::Relation.new({'href' => '/foos/{id}{?q,page}', 'type' => 'application/json', 'title' => 'A relation', 'templated' => true}, client) }
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
it 'is templated' do
|
|
42
|
+
expect(relation.templated?).to eql(true)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'works with defaults' do
|
|
46
|
+
expect(client).to receive(:get_and_wrap).with('/foos/', BooticClient::Entity).and_return entity
|
|
47
|
+
expect(relation.run).to eql(entity)
|
|
48
|
+
end
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
it 'interpolates tokens' do
|
|
51
|
+
expect(client).to receive(:get_and_wrap).with('/foos/2?q=test&page=2', BooticClient::Entity).and_return entity
|
|
52
|
+
expect(relation.run(id: 2, q: 'test', page: 2)).to eql(entity)
|
|
53
|
+
end
|
|
47
54
|
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe 'POST' do
|
|
58
|
+
let(:relation) { BooticClient::Relation.new({'href' => '/foo/bars', 'type' => 'application/json', 'name' => 'self', 'method' => 'post'}, client) }
|
|
48
59
|
|
|
49
|
-
it '
|
|
50
|
-
|
|
51
|
-
expect(relation.
|
|
60
|
+
it 'POSTS data and returns resulting entity' do
|
|
61
|
+
client.stub(:post_and_wrap).with('/foo/bars', BooticClient::Entity, {}).and_return entity
|
|
62
|
+
expect(relation.run).to eql(entity)
|
|
52
63
|
end
|
|
53
64
|
end
|
|
54
65
|
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.3
|
|
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-06-
|
|
11
|
+
date: 2014-06-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|