ecwid_api 0.0.2 → 0.2.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.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +8 -0
  3. data/README.md +123 -31
  4. data/ecwid_api.gemspec +10 -8
  5. data/lib/ecwid_api.rb +18 -29
  6. data/lib/ecwid_api/api.rb +12 -0
  7. data/lib/ecwid_api/api/base.rb +18 -0
  8. data/lib/ecwid_api/api/categories.rb +56 -0
  9. data/lib/ecwid_api/api/customers.rb +53 -0
  10. data/lib/ecwid_api/api/orders.rb +36 -0
  11. data/lib/ecwid_api/api/product_combinations.rb +48 -0
  12. data/lib/ecwid_api/api/product_types.rb +56 -0
  13. data/lib/ecwid_api/api/products.rb +148 -0
  14. data/lib/ecwid_api/category.rb +53 -4
  15. data/lib/ecwid_api/client.rb +65 -58
  16. data/lib/ecwid_api/customer.rb +10 -0
  17. data/lib/ecwid_api/entity.rb +151 -29
  18. data/lib/ecwid_api/error.rb +10 -0
  19. data/lib/ecwid_api/o_auth.rb +106 -0
  20. data/lib/ecwid_api/order.rb +118 -0
  21. data/lib/ecwid_api/order_item.rb +17 -0
  22. data/lib/ecwid_api/paged_ecwid_response.rb +57 -0
  23. data/lib/ecwid_api/paged_enumerator.rb +66 -0
  24. data/lib/ecwid_api/person.rb +7 -0
  25. data/lib/ecwid_api/product.rb +65 -0
  26. data/lib/ecwid_api/product_combination.rb +30 -0
  27. data/lib/ecwid_api/product_type.rb +18 -0
  28. data/lib/ecwid_api/product_type_attribute.rb +27 -0
  29. data/lib/ecwid_api/unpaged_ecwid_response.rb +38 -0
  30. data/lib/ecwid_api/version.rb +1 -1
  31. data/lib/ext/string.rb +9 -1
  32. data/spec/api/categories_spec.rb +31 -0
  33. data/spec/api/customers_spec.rb +20 -0
  34. data/spec/api/orders_spec.rb +30 -0
  35. data/spec/api/product_types_spec.rb +20 -0
  36. data/spec/api/products_spec.rb +20 -0
  37. data/spec/category_spec.rb +1 -6
  38. data/spec/client_spec.rb +4 -32
  39. data/spec/entity_spec.rb +120 -8
  40. data/spec/fixtures/categories.json +28 -22
  41. data/spec/fixtures/classes.json +44 -0
  42. data/spec/fixtures/customers.json +48 -0
  43. data/spec/fixtures/order.json +162 -0
  44. data/spec/fixtures/orders.json +303 -0
  45. data/spec/fixtures/products.json +141 -0
  46. data/spec/helpers/client.rb +34 -0
  47. data/spec/oauth_spec.rb +40 -0
  48. data/spec/order_item_spec.rb +12 -0
  49. data/spec/order_spec.rb +71 -0
  50. data/spec/paged_enumerator_spec.rb +38 -0
  51. data/spec/spec_helper.rb +3 -3
  52. metadata +93 -37
  53. data/lib/ecwid_api/category_api.rb +0 -62
  54. data/spec/category_api_spec.rb +0 -36
  55. data/spec/ecwid_api_spec.rb +0 -15
  56. 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,7 @@
1
+ module EcwidApi
2
+ class Person < Entity
3
+ ecwid_reader :name, :companyName, :street, :city, :countryCode,
4
+ :countryName, :postalCode, :stateOrProvinceCode,
5
+ :stateOrProvinceName, :phone
6
+ end
7
+ 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
@@ -1,3 +1,3 @@
1
1
  module EcwidApi
2
- VERSION = "0.0.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -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