spree-api-client 0.0.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +674 -0
- data/README.md +31 -38
- data/lib/spree_client.rb +3 -0
- data/lib/spree_client.rb~ +71 -0
- data/lib/spree_client/api.rb +1 -0
- data/lib/spree_client/api/v1.rb +53 -0
- data/lib/spree_client/api/v1/products.rb +23 -0
- data/lib/spree_client/api/v1/properties.rb +12 -0
- data/lib/spree_client/api/v1/resources.rb +142 -0
- data/lib/spree_client/api/v1/stock_items.rb +27 -0
- data/lib/spree_client/api/v1/stock_locations.rb +33 -0
- data/lib/spree_client/api/v1/stock_movements.rb +16 -0
- data/lib/spree_client/api/v1/taxonomies.rb +12 -0
- data/lib/spree_client/api/v1/variants.rb +13 -0
- data/lib/spree_client/attributes.rb +27 -0
- data/lib/spree_client/models.rb +5 -0
- data/lib/spree_client/models/product.rb +7 -0
- data/lib/spree_client/models/property.rb +6 -0
- data/lib/spree_client/models/stock_item.rb +7 -0
- data/lib/spree_client/models/stock_location.rb +7 -0
- data/lib/spree_client/models/stock_movement.rb +7 -0
- data/lib/spree_client/models/store.rb +7 -0
- data/lib/spree_client/models/taxonomy.rb +5 -0
- data/lib/spree_client/models/variant.rb +7 -0
- metadata +79 -118
- data/.gitignore +0 -17
- data/.travis.yml +0 -9
- data/CONTRIBUTING.md +0 -8
- data/Gemfile +0 -3
- data/LICENSE +0 -22
- data/Rakefile +0 -7
- data/lib/spree-api-client.rb +0 -49
- data/lib/spree-api-client/addresses.rb +0 -15
- data/lib/spree-api-client/connection.rb +0 -30
- data/lib/spree-api-client/countries.rb +0 -15
- data/lib/spree-api-client/line_items.rb +0 -19
- data/lib/spree-api-client/orders.rb +0 -31
- data/lib/spree-api-client/payments.rb +0 -43
- data/lib/spree-api-client/products.rb +0 -31
- data/lib/spree-api-client/properties.rb +0 -31
- data/lib/spree-api-client/request.rb +0 -49
- data/lib/spree-api-client/return_authorizations.rb +0 -31
- data/lib/spree-api-client/shipments.rb +0 -15
- data/lib/spree-api-client/taxonomies.rb +0 -31
- data/lib/spree-api-client/taxons.rb +0 -27
- data/lib/spree-api-client/variants.rb +0 -31
- data/lib/spree-api-client/version.rb +0 -7
- data/lib/spree-api-client/zones.rb +0 -31
- data/spec/addresses_spec.rb +0 -11
- data/spec/client_spec.rb +0 -31
- data/spec/countries_spec.rb +0 -11
- data/spec/orders_spec.rb +0 -11
- data/spec/payments_spec.rb +0 -11
- data/spec/products_spec.rb +0 -11
- data/spec/properties_spec.rb +0 -11
- data/spec/return_authorizations_spec.rb +0 -11
- data/spec/shipments_spec.rb +0 -11
- data/spec/spec_helper.rb +0 -5
- data/spec/taxonomies_spec.rb +0 -11
- data/spec/taxons_spec.rb +0 -11
- data/spec/variants_spec.rb +0 -11
- data/spec/zones_spec.rb +0 -11
- data/spree-api-client.gemspec +0 -27
data/README.md
CHANGED
@@ -1,18 +1,12 @@
|
|
1
|
-
# Spree
|
2
|
-
|
3
|
-
[![Build Status](https://secure.travis-ci.org/andrew/spree-api-client.png)](https://travis-ci.org/andrew/spree-api-client)
|
4
|
-
|
5
|
-
** Beware: very alpha and untested **
|
6
|
-
|
7
|
-
A rubygem for interacting with the Spree API: http://api.spreecommerce.com/v1/
|
8
|
-
|
9
|
-
Heavily inspired by the excellent Octokit: https://github.com/pengwynn/octokit
|
1
|
+
# Spree Ecommerce API client
|
10
2
|
|
11
3
|
## Installation
|
12
4
|
|
13
|
-
Add this line to your
|
5
|
+
Add this line to your site's Gemfile:
|
14
6
|
|
15
|
-
|
7
|
+
```ruby
|
8
|
+
gem 'spree_client'
|
9
|
+
```
|
16
10
|
|
17
11
|
And then execute:
|
18
12
|
|
@@ -20,42 +14,41 @@ And then execute:
|
|
20
14
|
|
21
15
|
Or install it yourself as:
|
22
16
|
|
23
|
-
$ gem install
|
17
|
+
$ gem install spree_client
|
24
18
|
|
25
19
|
## Usage
|
26
20
|
|
27
|
-
|
28
|
-
|
21
|
+
Get an API key for your user in `/admin/users`.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
spree = SpreeClient::Api::V1.new api_key: 'api key', spree_url: 'http://localhost:3000', store: 'https://my.store'
|
25
|
+
|
26
|
+
spree.products.index
|
27
|
+
=> true
|
29
28
|
|
30
|
-
|
29
|
+
# The results are in the response
|
30
|
+
spree.products.response
|
31
|
+
=> []
|
32
|
+
```
|
31
33
|
|
32
|
-
|
33
|
-
* Mock web requests in tests
|
34
|
-
* documentation
|
35
|
-
* rdoc generation
|
36
|
-
* release to rubygems.org
|
37
|
-
* gemnasium
|
38
|
-
* code climate
|
39
|
-
* Error handling
|
40
|
-
* Autopagination?
|
34
|
+
[Read the documentation](https://rubydoc.info/gems/spree_client)
|
41
35
|
|
42
|
-
## Development
|
43
36
|
|
44
|
-
|
45
|
-
Report Issues/Feature requests on [GitHub Issues](http://github.com/andrew/spree-api-client/issues).
|
37
|
+
## Contributing
|
46
38
|
|
47
|
-
|
39
|
+
Bug reports and pull requests are welcome on 0xacab.org at
|
40
|
+
<https://0xacab.org/sutty/spree_client>. This project is
|
41
|
+
intended to be a safe, welcoming space for collaboration, and
|
42
|
+
contributors are expected to adhere to the [Sutty code of
|
43
|
+
conduct](https://sutty.nl/en/code-of-conduct/).
|
48
44
|
|
49
|
-
|
45
|
+
## License
|
50
46
|
|
51
|
-
|
52
|
-
|
53
|
-
* Add tests for it. This is important so I don't break it in a
|
54
|
-
future version unintentionally.
|
55
|
-
* Commit, do not mess with rakefile, version, or history.
|
56
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
57
|
-
* Send me a pull request. Bonus points for topic branches.
|
47
|
+
The gem is available as free software under the terms of the GPL3
|
48
|
+
License.
|
58
49
|
|
59
|
-
##
|
50
|
+
## Code of Conduct
|
60
51
|
|
61
|
-
|
52
|
+
Everyone interacting in the `spree_client` project’s codebases, issue
|
53
|
+
trackers, chat rooms and mailing lists is expected to follow the [code
|
54
|
+
of conduct](https://sutty.nl/en/code-of-conduct/).
|
data/lib/spree_client.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
require_relative 'spree_client/models'
|
4
|
+
require_relative 'spree_client/api/v1'
|
5
|
+
|
6
|
+
# This class is a client for Spree Commerce Backend. It can push
|
7
|
+
# information to it and also retrieve it from the APIs.
|
8
|
+
#
|
9
|
+
# The Spree Backend needs to have Devise configured with
|
10
|
+
# http_authenticatable = true so it can send the credentials as basic
|
11
|
+
# authorization header.
|
12
|
+
module SpreeClient
|
13
|
+
include HTTParty
|
14
|
+
|
15
|
+
# The backend will redirect in certain actions, we want to be able to
|
16
|
+
# distinguish them.
|
17
|
+
follow_redirects false
|
18
|
+
|
19
|
+
# Cookies
|
20
|
+
# @return [String]
|
21
|
+
attr_accessor :cookies
|
22
|
+
|
23
|
+
# Initialize an SpreeClient. If not parameters are given it'll try to
|
24
|
+
# login to the default development Spree.
|
25
|
+
#
|
26
|
+
# @param [String] :spree_url The Spree site
|
27
|
+
# @param [String] :username Username
|
28
|
+
# @param [String] :password Password
|
29
|
+
def initialize(spree_url: 'http://localhost:3000', username: 'spree@example.com', password: 'spree123')
|
30
|
+
self.class.basic_auth username, password
|
31
|
+
self.class.default_options[:base_uri] = HTTParty.normalize_base_uri(spree_url)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Gets an authenticity token from the API by sending credentials to a
|
35
|
+
# custom API endpoint. Otherwise we have to download and parse the
|
36
|
+
# forms. It stores the cookie so the token can be verified afterwards.
|
37
|
+
#
|
38
|
+
# @see SpreeClient#cookies
|
39
|
+
# @return [String]
|
40
|
+
def authenticity_token
|
41
|
+
response = self.class.get('/admin/authenticity_token',
|
42
|
+
headers: headers)
|
43
|
+
|
44
|
+
@cookies = response.headers['set-cookie']
|
45
|
+
|
46
|
+
response.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# Taxonomies
|
50
|
+
#
|
51
|
+
# @see SpreeClient::Taxonomies
|
52
|
+
# @return [SpreeClient::Taxonomies]
|
53
|
+
def taxonomies
|
54
|
+
@taxonomies ||= Taxonomies.new spree: self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Properties
|
58
|
+
#
|
59
|
+
# @see SpreeClient::Properties
|
60
|
+
# @return [SpreeClient::Properties]
|
61
|
+
def properties
|
62
|
+
@properties ||= Properties.new spree: self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Default headers. If there're cookies it sends them.
|
66
|
+
#
|
67
|
+
# @return [Hash] Headers
|
68
|
+
def headers(extra = {})
|
69
|
+
extra.merge({ 'Cookie' => cookies })
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'api/v1'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require_relative 'v1/resources'
|
3
|
+
require_relative 'v1/products'
|
4
|
+
require_relative 'v1/variants'
|
5
|
+
require_relative 'v1/stock_locations'
|
6
|
+
require_relative 'v1/stock_items'
|
7
|
+
require_relative 'v1/stock_movements'
|
8
|
+
|
9
|
+
module SpreeClient
|
10
|
+
module API
|
11
|
+
class V1
|
12
|
+
include ::HTTParty
|
13
|
+
|
14
|
+
attr_reader :api_key, :store
|
15
|
+
|
16
|
+
def initialize(api_key:, spree_url: 'http://localhost:3000', store: nil)
|
17
|
+
@api_key = api_key
|
18
|
+
@store = HTTParty.normalize_base_uri(store) if store
|
19
|
+
self.class.default_options[:base_uri] = HTTParty.normalize_base_uri(spree_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: Meta programming
|
23
|
+
def products(**args)
|
24
|
+
@products ||= {}
|
25
|
+
@products[args.hash.to_s] ||= Products.new **{ api: self }.merge(args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def variants(**args)
|
29
|
+
@variants ||= {}
|
30
|
+
@variants[args.hash.to_s] ||= Variants.new **{ api: self }.merge(args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def stock_locations(**args)
|
34
|
+
@stock_locations ||= {}
|
35
|
+
@stock_locations[args.hash.to_s] ||= StockLocations.new **{ api: self }.merge(args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stock_movements(**args)
|
39
|
+
@stock_movements ||= {}
|
40
|
+
@stock_movements[args.hash.to_s] ||= StockMovements.new **{ api: self }.merge(args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def stock_items(**args)
|
44
|
+
@stock_items ||= {}
|
45
|
+
@stock_items[args.hash.to_s] ||= StockItems.new **{ api: self }.merge(args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def headers(extra = {})
|
49
|
+
extra.merge({ 'Content-Type' => 'application/json', 'X-Spree-Token' => api_key, 'Origin' => store })
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SpreeClient
|
2
|
+
module API
|
3
|
+
class V1
|
4
|
+
# Products
|
5
|
+
# @see SpreeClient::API:V1::Resource
|
6
|
+
class Products < Resources
|
7
|
+
ENDPOINT = '/api/v1/products'
|
8
|
+
RESOURCE = SpreeClient::Models::Product
|
9
|
+
NAME = 'product'
|
10
|
+
|
11
|
+
# @return [SpreeClient::API::V1::Variants]
|
12
|
+
def variants(**args)
|
13
|
+
product_id = default_args.dig(:id) || args.dig(:product_id)
|
14
|
+
|
15
|
+
raise ArgumentError, 'Needs a product ID' unless product_id
|
16
|
+
|
17
|
+
@variants ||= {}
|
18
|
+
@variants[product_id.to_s.to_sym] ||= Variants.new **{ api: api, product_id: product_id }.merge(args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module SpreeClient
|
4
|
+
module API
|
5
|
+
class V1
|
6
|
+
class Resources
|
7
|
+
# Spree API client instance.
|
8
|
+
# @return [SpreeClient::API::V1]
|
9
|
+
attr_reader :api
|
10
|
+
# Every method caches the response so it can be inspected afterwards.
|
11
|
+
# @return [HTTParty::Response] The API response
|
12
|
+
attr_reader :response
|
13
|
+
# @return [Hash]
|
14
|
+
attr_reader :default_args
|
15
|
+
|
16
|
+
# Initialize
|
17
|
+
#
|
18
|
+
# @param [SpreeClient::API::V1] :api An API client instance
|
19
|
+
def initialize(**args)
|
20
|
+
@api = args.delete :api
|
21
|
+
@default_args = args
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initialize a new resource
|
25
|
+
#
|
26
|
+
# @param [Hash] Attributes
|
27
|
+
# @return [Struct]
|
28
|
+
def new(**args)
|
29
|
+
resource_class.new **args
|
30
|
+
end
|
31
|
+
|
32
|
+
# Gets all products or filter with params
|
33
|
+
#
|
34
|
+
# Filters:
|
35
|
+
#
|
36
|
+
# ids: comma-separated list of IDs
|
37
|
+
# q: Ransack search params (mutually exclusive with ids)
|
38
|
+
#
|
39
|
+
# Pagination:
|
40
|
+
#
|
41
|
+
# page: page number
|
42
|
+
# per_page: results per page
|
43
|
+
#
|
44
|
+
# @param [Hash] Query params
|
45
|
+
def index(**q)
|
46
|
+
@response = api.class.get endpoint(q),
|
47
|
+
query: q.slice(:ids, :q, :page, :per_page),
|
48
|
+
headers: api.headers
|
49
|
+
|
50
|
+
response.ok?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get a single product by ID
|
54
|
+
def show(resource)
|
55
|
+
@response = api.class.get endpoint(resource), headers: api.headers
|
56
|
+
|
57
|
+
response.ok?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates a resource
|
61
|
+
#
|
62
|
+
# @see SpreeClient::API::V1#headers
|
63
|
+
# @param [Struct,Hash] Model instance
|
64
|
+
# @return [Boolean]
|
65
|
+
def create(resource)
|
66
|
+
resource = new **resource unless resource.is_a? resource_class
|
67
|
+
|
68
|
+
@response = api.class.post endpoint(resource),
|
69
|
+
body: params(resource),
|
70
|
+
headers: api.headers
|
71
|
+
|
72
|
+
response.created?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Updates a resource
|
76
|
+
#
|
77
|
+
# @see SpreeClient::API::V1#headers
|
78
|
+
# @param [Struct,Hash]
|
79
|
+
# @return [Boolean]
|
80
|
+
def update(resource)
|
81
|
+
resource = new resource unless resource.is_a? resource_class
|
82
|
+
|
83
|
+
@response = api.class.patch endpoint(resource),
|
84
|
+
body: params(resource),
|
85
|
+
headers: api.headers
|
86
|
+
|
87
|
+
response.ok?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Deletes a resource
|
91
|
+
#
|
92
|
+
# @see SpreeClient::API::V1#headers
|
93
|
+
# @param [Struct,Hash]
|
94
|
+
# @return [Boolean]
|
95
|
+
def delete(resource)
|
96
|
+
@response = spree.class.delete endpoint(resource), headers: api.headers
|
97
|
+
|
98
|
+
response.no_content?
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Strong parameters
|
104
|
+
#
|
105
|
+
# @see SpreeClient#authenticity_token
|
106
|
+
# @param [Struct,Nil]
|
107
|
+
# @return [Hash]
|
108
|
+
def params(resource = nil)
|
109
|
+
{ resource_name => default_args.merge(resource.to_h.compact) }.to_json
|
110
|
+
end
|
111
|
+
|
112
|
+
# Backend endpoint
|
113
|
+
#
|
114
|
+
# @param [Struct]
|
115
|
+
# @return [String]
|
116
|
+
def endpoint(resource = {})
|
117
|
+
endpoint = self.class::ENDPOINT + (resource.dig(:id) ? '/:id' : '')
|
118
|
+
|
119
|
+
parameterize_with endpoint, resource
|
120
|
+
end
|
121
|
+
|
122
|
+
# Generates a URL with :parameter replaced with values
|
123
|
+
#
|
124
|
+
# @param [String]
|
125
|
+
# @param [Struct,Hash]
|
126
|
+
def parameterize_with(endpoint, resource = {})
|
127
|
+
default_args.merge(resource.to_h.compact).inject(endpoint) do |e, pair|
|
128
|
+
e.gsub ':' + pair.first.to_s, pair.last.to_s
|
129
|
+
end + '.json'
|
130
|
+
end
|
131
|
+
|
132
|
+
def resource_class
|
133
|
+
self.class::RESOURCE
|
134
|
+
end
|
135
|
+
|
136
|
+
def resource_name
|
137
|
+
self.class::NAME
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SpreeClient
|
2
|
+
module API
|
3
|
+
class V1
|
4
|
+
# Stock Movements
|
5
|
+
# @see SpreeClient::API:V1::Resources
|
6
|
+
class StockItems < Resources
|
7
|
+
ENDPOINT = '/api/v1/stock_locations/:stock_location_id/stock_items'
|
8
|
+
RESOURCE = SpreeClient::Models::StockItem
|
9
|
+
NAME = 'stock_item'
|
10
|
+
|
11
|
+
def update(_); end
|
12
|
+
def destroy(_); end
|
13
|
+
|
14
|
+
# @return [SpreeClient::API::V1::StockMovements]
|
15
|
+
def stock_movements(**args)
|
16
|
+
stock_location_id = default_args.dig(:stock_location_id) || args.dig(:stock_location_id)
|
17
|
+
stock_item_id = default_args.dig(:id) || args.dig(:stock_item_id)
|
18
|
+
|
19
|
+
raise ArgumentError, 'Needs a stock location ID' unless stock_location_id
|
20
|
+
|
21
|
+
@stock_movements ||= {}
|
22
|
+
@stock_movements[stock_location_id.to_s.to_sym] ||= StockMovements.new **{ api: api, stock_location_id: stock_location_id, stock_item_id: stock_item_id }.merge(args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SpreeClient
|
2
|
+
module API
|
3
|
+
class V1
|
4
|
+
# Stock Locations
|
5
|
+
# @see SpreeClient::API:V1::Resource
|
6
|
+
class StockLocations < Resources
|
7
|
+
ENDPOINT = '/api/v1/stock_locations'
|
8
|
+
RESOURCE = SpreeClient::Models::StockLocation
|
9
|
+
NAME = 'stock_location'
|
10
|
+
|
11
|
+
# @return [SpreeClient::API::V1::StockMovements]
|
12
|
+
def stock_movements(**args)
|
13
|
+
stock_location_id = default_args.dig(:id) || args.dig(:stock_location_id)
|
14
|
+
|
15
|
+
raise ArgumentError, 'Needs a stock location ID' unless stock_location_id
|
16
|
+
|
17
|
+
@stock_movements ||= {}
|
18
|
+
@stock_movements[stock_location_id.to_s.to_sym] ||= StockMovements.new **{ api: api, stock_location_id: stock_location_id }.merge(args)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [SpreeClient::API::V1::StockItems]
|
22
|
+
def stock_items(**args)
|
23
|
+
stock_location_id = default_args.dig(:id) || args.dig(:stock_location_id)
|
24
|
+
|
25
|
+
raise ArgumentError, 'Needs a stock location ID' unless stock_location_id
|
26
|
+
|
27
|
+
@stock_items ||= {}
|
28
|
+
@stock_items[stock_location_id.to_s.to_sym] ||= StockItems.new **{ api: api, stock_location_id: stock_location_id }.merge(args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|