ecwid_api 0.0.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +8 -0
- data/README.md +123 -31
- data/ecwid_api.gemspec +10 -8
- data/lib/ecwid_api.rb +18 -29
- data/lib/ecwid_api/api.rb +12 -0
- data/lib/ecwid_api/api/base.rb +18 -0
- data/lib/ecwid_api/api/categories.rb +56 -0
- data/lib/ecwid_api/api/customers.rb +53 -0
- data/lib/ecwid_api/api/orders.rb +36 -0
- data/lib/ecwid_api/api/product_combinations.rb +48 -0
- data/lib/ecwid_api/api/product_types.rb +56 -0
- data/lib/ecwid_api/api/products.rb +148 -0
- data/lib/ecwid_api/category.rb +53 -4
- data/lib/ecwid_api/client.rb +65 -58
- data/lib/ecwid_api/customer.rb +10 -0
- data/lib/ecwid_api/entity.rb +151 -29
- data/lib/ecwid_api/error.rb +10 -0
- data/lib/ecwid_api/o_auth.rb +106 -0
- data/lib/ecwid_api/order.rb +118 -0
- data/lib/ecwid_api/order_item.rb +17 -0
- data/lib/ecwid_api/paged_ecwid_response.rb +57 -0
- data/lib/ecwid_api/paged_enumerator.rb +66 -0
- data/lib/ecwid_api/person.rb +7 -0
- data/lib/ecwid_api/product.rb +65 -0
- data/lib/ecwid_api/product_combination.rb +30 -0
- data/lib/ecwid_api/product_type.rb +18 -0
- data/lib/ecwid_api/product_type_attribute.rb +27 -0
- data/lib/ecwid_api/unpaged_ecwid_response.rb +38 -0
- data/lib/ecwid_api/version.rb +1 -1
- data/lib/ext/string.rb +9 -1
- data/spec/api/categories_spec.rb +31 -0
- data/spec/api/customers_spec.rb +20 -0
- data/spec/api/orders_spec.rb +30 -0
- data/spec/api/product_types_spec.rb +20 -0
- data/spec/api/products_spec.rb +20 -0
- data/spec/category_spec.rb +1 -6
- data/spec/client_spec.rb +4 -32
- data/spec/entity_spec.rb +120 -8
- data/spec/fixtures/categories.json +28 -22
- data/spec/fixtures/classes.json +44 -0
- data/spec/fixtures/customers.json +48 -0
- data/spec/fixtures/order.json +162 -0
- data/spec/fixtures/orders.json +303 -0
- data/spec/fixtures/products.json +141 -0
- data/spec/helpers/client.rb +34 -0
- data/spec/oauth_spec.rb +40 -0
- data/spec/order_item_spec.rb +12 -0
- data/spec/order_spec.rb +71 -0
- data/spec/paged_enumerator_spec.rb +38 -0
- data/spec/spec_helper.rb +3 -3
- metadata +93 -37
- data/lib/ecwid_api/category_api.rb +0 -62
- data/spec/category_api_spec.rb +0 -36
- data/spec/ecwid_api_spec.rb +0 -15
- data/spec/helpers/faraday.rb +0 -30
@@ -0,0 +1,17 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
class OrderItem < Entity
|
3
|
+
ecwid_reader :id, :productId, :categoryId, :price, :productPrice, :weight,
|
4
|
+
:sku, :quantity, :shortDescription, :tax, :shipping,
|
5
|
+
:quantityInStock, :name, :tangible, :trackQuantity,
|
6
|
+
:fixedShippingRateOnly, :imageId, :fixedShippingRate,
|
7
|
+
:digital, :productAvailable, :couponApplied, :selectedOptions,
|
8
|
+
:taxes, :files
|
9
|
+
|
10
|
+
# Public: Returns the default `Category` that the product belongs to
|
11
|
+
def category
|
12
|
+
client.categories.find(data["categoryId"])
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: get the product
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'paged_enumerator'
|
2
|
+
|
3
|
+
# Public: Presents a paged Ecwid response as an Enumerator with a
|
4
|
+
# PagedEnumerator
|
5
|
+
#
|
6
|
+
# Example
|
7
|
+
#
|
8
|
+
# response = PagedEcwidResponse.new(client, "products", priceFrom: 10) do |product_hash|
|
9
|
+
# Product.new(product_hash, click: client)
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# response.each do |product|
|
13
|
+
# # do stuff the the product
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
module EcwidApi
|
17
|
+
class PagedEcwidResponse
|
18
|
+
include Enumerable
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
def_delegator :@paged_enumerator, :each
|
22
|
+
|
23
|
+
# Public: Initialize a new PagedEcwidResponse
|
24
|
+
#
|
25
|
+
# client - an EcwidApi::Client
|
26
|
+
# path - a String that is the path to retrieve from the client
|
27
|
+
# params - a Hash of parameters to pass along with the request
|
28
|
+
# &block - a Block that processes each item returned in the Response
|
29
|
+
#
|
30
|
+
def initialize(client, path, params = {}, &block)
|
31
|
+
params[:limit] = 100
|
32
|
+
params.delete(:offset)
|
33
|
+
|
34
|
+
block ||= proc { |item| item }
|
35
|
+
|
36
|
+
response = client.get(path, params)
|
37
|
+
|
38
|
+
@paged_enumerator = PagedEnumerator.new(response) do |enum_response, yielder|
|
39
|
+
count, offset, total = %w[count offset total].map do |i|
|
40
|
+
enum_response.body[i].to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
if count > 0
|
44
|
+
enum_response.body['items'].each do |item|
|
45
|
+
yielder << block.call(item)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if count.zero? || count + offset >= total
|
50
|
+
false
|
51
|
+
else
|
52
|
+
client.get(path, params.merge(offset: offset + count))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
# Public: Abstracts pagination into an Enumerator so all of the objects for
|
3
|
+
# a given response can be retreived without having to know that they were
|
4
|
+
# split into pages from the server
|
5
|
+
#
|
6
|
+
# Examples
|
7
|
+
#
|
8
|
+
# client.get("orders", limit: 10)
|
9
|
+
#
|
10
|
+
# paged = PagedEnumerator.new(orders) do |response, yielder|
|
11
|
+
# response.body["orders"].each do |obj|
|
12
|
+
# yielder << obj
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# next_url = response.body["nextUrl"]
|
16
|
+
# next_url ? client.get(next_url) : false
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# paged.each do |item|
|
20
|
+
# puts item.inspect
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
class PagedEnumerator
|
24
|
+
include Enumerable
|
25
|
+
|
26
|
+
# Private: A response that we are evaluating
|
27
|
+
attr_reader :response
|
28
|
+
private :response
|
29
|
+
|
30
|
+
# Private: Gets the next response
|
31
|
+
attr_reader :next_block
|
32
|
+
private :next_block
|
33
|
+
|
34
|
+
# Public: Initializes a new PagedEnumerator
|
35
|
+
#
|
36
|
+
# response - the response that contains a collection of objects
|
37
|
+
# next_block - The block will receive the response and a yielder Proc.
|
38
|
+
# The block should use `yielder << item` to yield all of
|
39
|
+
# the items in the collection.
|
40
|
+
#
|
41
|
+
# Then the block should return the next response. If no
|
42
|
+
# response is given (eg: the last page), the block should
|
43
|
+
# return a falsey value.
|
44
|
+
#
|
45
|
+
def initialize(response, &next_block)
|
46
|
+
@response, @next_block = response, next_block
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Iterates over each "page" yielding each "item" in every collection
|
50
|
+
def each(&block)
|
51
|
+
unless @next_enumerator
|
52
|
+
@yielder ||= []
|
53
|
+
@next_response ||= next_block.call(response, @yielder)
|
54
|
+
|
55
|
+
if @next_response
|
56
|
+
@next_enumerator ||= PagedEnumerator.new(@next_response, &@next_block)
|
57
|
+
else
|
58
|
+
@next_enumerator = []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@yielder.each(&block)
|
63
|
+
@next_enumerator.each(&block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
class Product < Entity
|
3
|
+
self.url_root = "products"
|
4
|
+
|
5
|
+
ecwid_reader :id, :sku, :quantity, :unlimited, :inStock, :name, :price,
|
6
|
+
:priceInProductList, :wholesalePrices, :compareToPrice,
|
7
|
+
:weight, :url, :created, :updated, :productClassId,
|
8
|
+
:enabled, :options, :warningLimit, :fixedShippingRateOnly,
|
9
|
+
:fixedShippingRate, :defaultCombinationId, :thumbnailUrl,
|
10
|
+
:imageUrl, :smallThumbnailUrl, :originalImageUrl, :description,
|
11
|
+
:galleryImages, :categoryIds, :defaultCategoryId, :favorites,
|
12
|
+
:attributes, :files, :relatedProducts, :combinations
|
13
|
+
|
14
|
+
ecwid_writer :sku, :name, :quantity, :price, :wholesalePrices,
|
15
|
+
:compareToPrice, :weight, :productClassId, :enabled, :options,
|
16
|
+
:warningLimit, :fixedShippingRateOnly, :fixedShippingRate,
|
17
|
+
:description, :categoryIds, :defaultCategoryId, :attributes,
|
18
|
+
:relatedProducts
|
19
|
+
|
20
|
+
# Public: Uploads a primary image for a Product
|
21
|
+
#
|
22
|
+
# filename - a String that is either a local file name or URL
|
23
|
+
#
|
24
|
+
# Raises ResponseError if the API returns an error
|
25
|
+
#
|
26
|
+
# Returns a Faraday::Response object
|
27
|
+
def upload_image!(filename)
|
28
|
+
client.post_image("#{url}/image", filename)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Uploads gallery images for a Product
|
32
|
+
#
|
33
|
+
# *filenames - Strings that are either a local file name or URL
|
34
|
+
#
|
35
|
+
# Raises ResponseError if the API returns an error
|
36
|
+
#
|
37
|
+
# Returns an Array of Faraday::Response object
|
38
|
+
def upload_gallery_images!(*filenames)
|
39
|
+
filenames.map do |filename|
|
40
|
+
client.post_image("#{url}/gallery", filename)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Deletes all of the gallery images for a Product
|
45
|
+
#
|
46
|
+
# Raises ResponseError if the API returns an error
|
47
|
+
#
|
48
|
+
# Returns a Faraday::Response object
|
49
|
+
def delete_gallery_images!
|
50
|
+
client.delete("#{url}/gallery")
|
51
|
+
end
|
52
|
+
|
53
|
+
def combinations
|
54
|
+
@combinations ||= Api::ProductCombinations.new(self, client)
|
55
|
+
end
|
56
|
+
|
57
|
+
def created
|
58
|
+
@created ||= Time.parse(super)
|
59
|
+
end
|
60
|
+
|
61
|
+
def updated
|
62
|
+
@updated ||= Time.parse(super)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
class ProductCombination < Entity
|
3
|
+
attr_reader :product
|
4
|
+
|
5
|
+
self.url_root = -> { "products/#{product.id}/combinations" }
|
6
|
+
|
7
|
+
ecwid_reader :id, :combinationNumber, :options, :sku, :smallThumbnailUrl,
|
8
|
+
:thumbnailUrl, :imageUrl, :originalImageUrl, :quantity,
|
9
|
+
:unlimited, :price, :wholesalePrices, :weight, :warningLimit
|
10
|
+
|
11
|
+
ecwid_writer :options, :sku, :quantity, :unlimited, :price,
|
12
|
+
:wholesalePrices, :weight, :warningLimit, :inventoryDelta
|
13
|
+
|
14
|
+
def initialize(data, opts={})
|
15
|
+
super(data, opts)
|
16
|
+
@product = opts[:product]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Uploads a primary image for a ProductCombination
|
20
|
+
#
|
21
|
+
# filename - a String that is either a local file name or URL
|
22
|
+
#
|
23
|
+
# Raises ResponseError if the API returns an error
|
24
|
+
#
|
25
|
+
# Returns a Faraday::Response object
|
26
|
+
def upload_image!(filename)
|
27
|
+
client.post_image("#{url}/image", filename)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
class ProductType < Entity
|
3
|
+
|
4
|
+
self.url_root = "classes"
|
5
|
+
|
6
|
+
ecwid_reader :id, :name, :googleTaxonomy, :attributes
|
7
|
+
|
8
|
+
ecwid_writer :name, :attributes
|
9
|
+
|
10
|
+
|
11
|
+
# Public: Returns a Array of `ProductTypeAttribute` objects
|
12
|
+
def attributes
|
13
|
+
@attributes ||= data["attributes"].map { |attribute| ProductTypeAttribute.new(attribute) }
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EcwidApi
|
2
|
+
class ProductTypeAttribute < Entity
|
3
|
+
|
4
|
+
ecwid_reader :id, :name, :type, :show
|
5
|
+
|
6
|
+
VALID_TYPES = %w(CUSTOM UPC BRAND GENDER AGE_GROUP COLOR SIZE PRICE_PER_UNIT UNITS_IN_PRODUCT)
|
7
|
+
VALID_SHOWS = %w(NOTSHOW DESCR PRICE)
|
8
|
+
|
9
|
+
|
10
|
+
def type=(type_type)
|
11
|
+
type_type = type_type.to_s.upcase
|
12
|
+
unless VALID_TYPES.include?(type_type)
|
13
|
+
raise ::StandardError.new("#{type_type} is an invalid 'type'")
|
14
|
+
end
|
15
|
+
super(type_type)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def show=(show_type)
|
20
|
+
show_type = show_type.to_s.upcase
|
21
|
+
unless VALID_SHOWS.include?(show_type)
|
22
|
+
raise ::StandardError.new("#{show_type} is an invalid 'show'")
|
23
|
+
end
|
24
|
+
super(show_type)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Public: Presents an Ecwid response as an Array
|
3
|
+
#
|
4
|
+
# Example
|
5
|
+
#
|
6
|
+
# response = UnpagedEcwidResponse.new(client, "products", priceFrom: 10) do |product_hash|
|
7
|
+
# Product.new(product_hash, click: client)
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# response.each do |product|
|
11
|
+
# # do stuff the the product
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
module EcwidApi
|
15
|
+
class UnpagedEcwidResponse
|
16
|
+
include Enumerable
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :@records, *Enumerable.instance_methods
|
20
|
+
|
21
|
+
# Public: Initialize a new UnpagedEcwidResponse
|
22
|
+
#
|
23
|
+
# client - an EcwidApi::Client
|
24
|
+
# path - a String that is the path to retrieve from the client
|
25
|
+
# params - a Hash of parameters to pass along with the request
|
26
|
+
# &block - a Block that processes each item returned in the Response
|
27
|
+
#
|
28
|
+
def initialize(client, path, params = {}, &block)
|
29
|
+
block ||= Proc.new { |item| item }
|
30
|
+
@records = []
|
31
|
+
|
32
|
+
response = client.get(path, params)
|
33
|
+
response.body.each do |item|
|
34
|
+
@records << block.call(item)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ecwid_api/version.rb
CHANGED
data/lib/ext/string.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
class String
|
2
2
|
def camel_case
|
3
3
|
split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
|
4
|
-
end
|
4
|
+
end unless method_defined?(:camel_case)
|
5
|
+
|
6
|
+
def underscore
|
7
|
+
self.gsub(/::/, '/').
|
8
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
9
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
10
|
+
tr("-", "_").
|
11
|
+
downcase
|
12
|
+
end unless method_defined?(:underscore)
|
5
13
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EcwidApi::Api::Categories, faraday: true do
|
4
|
+
subject { client.categories }
|
5
|
+
|
6
|
+
describe "#all" do
|
7
|
+
it "gets all of the categories from the client" do
|
8
|
+
expect(client).to receive(:get).with("categories", hash_including({})).and_call_original
|
9
|
+
subject.all
|
10
|
+
end
|
11
|
+
|
12
|
+
it "gets sub categories" do
|
13
|
+
expect(client).to receive(:get).with("categories", hash_including(parent: 5)).and_call_original
|
14
|
+
subject.all(parent: 5)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#root" do
|
19
|
+
it "gets the root level categories" do
|
20
|
+
expect(subject).to receive(:all).with(hash_including(parent: 0)).and_call_original
|
21
|
+
subject.root
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#find" do
|
26
|
+
it "finds a single category" do
|
27
|
+
expect(client).to receive(:get).with("categories/#{5}").and_call_original
|
28
|
+
subject.find(5)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EcwidApi::Api::Customers, faraday: true do
|
4
|
+
subject { client.customers }
|
5
|
+
|
6
|
+
describe "#all" do
|
7
|
+
it "passes any other parameters through" do
|
8
|
+
expect(client).to receive(:get).with("customers", hash_including(from_date: '1982-05-17'))
|
9
|
+
subject.all(from_date: '1982-05-17')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "gets the proper response count (see fixture)" do
|
13
|
+
expect(subject.all.count).to eq 5
|
14
|
+
end
|
15
|
+
|
16
|
+
it "gets the proper customer (see fixture)" do
|
17
|
+
expect(subject.all.first.name).to eq "Abe Doe"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EcwidApi::Api::Orders, faraday: true do
|
4
|
+
subject { client.orders }
|
5
|
+
|
6
|
+
describe "#all" do
|
7
|
+
it "passes the parameters to the client" do
|
8
|
+
expect(client).to receive(:get).with("orders", hash_including(from_date: '1982-05-17'))
|
9
|
+
subject.all(from_date: '1982-05-17')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "gets the proper response (see fixtures)" do
|
13
|
+
expect(subject.all.count).to be 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it "gets EcwidApi::Order types" do
|
17
|
+
expect(subject.all.all? { |order| order.is_a?(EcwidApi::Order) }).to be(true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#find" do
|
22
|
+
it "is an `EcwidApi::Order`" do
|
23
|
+
expect(subject.find(35)).to be_a(EcwidApi::Order)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "is nil when not found" do
|
27
|
+
expect(subject.find(404)).to be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EcwidApi::Api::ProductTypes, faraday: true do
|
4
|
+
subject { client.product_types }
|
5
|
+
|
6
|
+
describe "#all" do
|
7
|
+
it "returns an array" do
|
8
|
+
expect(client).to receive(:get).with("classes", {}).and_call_original
|
9
|
+
subject.all
|
10
|
+
end
|
11
|
+
|
12
|
+
it "gets the proper response count (see fixture)" do
|
13
|
+
expect(subject.all.count).to eq 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it "gets the proper product_type (see fixture)" do
|
17
|
+
expect(subject.all.first.name).to eq "Foo"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|