kounta_rest 0.0.1

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.
@@ -0,0 +1,10 @@
1
+ module Kounta
2
+
3
+ class PriceList < Kounta::Resource
4
+ property :parent_id
5
+ property :name
6
+ property :products
7
+ coerce_key :products, Product
8
+ end
9
+
10
+ end
@@ -0,0 +1,30 @@
1
+ module Kounta
2
+
3
+ class Product < Kounta::Resource
4
+ property :company_id
5
+ property :code
6
+ property :barcode
7
+ property :stock
8
+ property :name
9
+ property :description
10
+ property :tags
11
+ property :image
12
+ property :unit_price
13
+ property :cost_price
14
+ property :taxes
15
+ property :sites
16
+
17
+ has_many :categories, Kounta::Category, {:company_id => :company_id}, lambda { |klass| {companies: klass.company_id, products: klass.company_id, categories: nil} }
18
+ coerce_key :taxes, Kounta::Tax
19
+
20
+ def tags_include?(name)
21
+ tags.any?{ |s| s.casecmp(name) == 0 }
22
+ end
23
+
24
+ def resource_path
25
+ {companies: company_id, products: id}
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,91 @@
1
+ require 'hashie'
2
+ require_relative "rest/client"
3
+
4
+ module Kounta
5
+
6
+ class Resource < Hashie::Dash
7
+ include Hashie::Extensions::Coercion
8
+
9
+ property :id
10
+ property :created_at
11
+ property :updated_at
12
+
13
+ def self.coerce(data)
14
+ if data.kind_of? Array
15
+ data.map { |item| self.new(item) }
16
+ else
17
+ self.new(data)
18
+ end
19
+ end
20
+
21
+ def self.has_one(sym, klass, assignments, route)
22
+ define_method(sym) do |item_id=nil, *args|
23
+ if item_id
24
+ assign_into(client.object_from_response(klass, :get, route.call(self, item_id), {:params => args[0]}), self, assignments)
25
+ else
26
+ assign_into(klass.new, self, assignments)
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.has_many(sym, klass, assignments, route)
32
+ define_method(sym) do |*args|
33
+ client.objects_from_response(klass, :get, route.call(self), {:params => args[0]}).map {|returned_klass| assign_into(returned_klass, self, assignments) }
34
+ end
35
+ end
36
+
37
+ def initialize(hash={})
38
+ if hash
39
+ hash.each_pair do |k,v|
40
+ begin
41
+ self[k] = v
42
+ rescue NoMethodError => e
43
+ raise Kounta::Errors::UnknownResourceAttribute.new("Unknown attribute: #{k} on resource #{self.class}")
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def client
50
+ @@client ||= Kounta::REST::Client.new
51
+ end
52
+
53
+ def to_hash(hash={})
54
+ {}.tap do |returning|
55
+ self.class.properties.each do |property|
56
+ next if ignored_properties.include?(property)
57
+ returning[property] = self[property] if self[property]
58
+ end
59
+ end.merge(hash)
60
+ end
61
+
62
+ def save!
63
+ response = new? ? client.perform(resource_path, :post, {:body => to_hash}) : client.perform(resource_path, :put, {:body => to_hash})
64
+ if response
65
+ response.each_pair do |k,v|
66
+ self[k] = v
67
+ end
68
+ end
69
+ self
70
+ end
71
+
72
+ def new?
73
+ !id
74
+ end
75
+
76
+ def ignored_properties(array=[])
77
+ array + [:created_at, :updated_at, :id, :company_id, :site_id]
78
+ end
79
+
80
+ private
81
+
82
+ def assign_into(klass, assigner, assignments)
83
+ assignments.each_pair do |k,v|
84
+ klass[k] = assigner[v]
85
+ end
86
+ klass
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,65 @@
1
+ require 'oauth2'
2
+ require 'yell'
3
+
4
+ module Kounta
5
+ module REST
6
+ class Client
7
+
8
+ def initialize
9
+ raise Kounta::Errors::MissingOauthDetails unless has_required_oauth_details?
10
+ @logger = Yell.new STDOUT
11
+ @conn = OAuth2::AccessToken.new(client, Kounta.client_token, {:refresh_token => Kounta.client_refresh_token})
12
+
13
+ end
14
+
15
+ def client
16
+ @oauth_client ||= OAuth2::Client.new(Kounta.client_id, Kounta.client_secret, {
17
+ :site => Kounta::SITE_URI,
18
+ :authorize_url => Kounta::AUTHORIZATION_URI,
19
+ :token_url => Kounta::TOKEN_URI
20
+ })
21
+ end
22
+
23
+ def path_from_hash(url_hash)
24
+ url_hash.map{ |key, value| value ? "#{key}/#{value}" : "#{key}" }.join('/')
25
+ end
26
+
27
+ def perform(url_hash, request_method, options={})
28
+ begin
29
+ log("#{request_method}: #{Kounta::SITE_URI}#{path_from_hash(url_hash)}.#{FORMAT.to_s} (#{options})")
30
+ response = @conn.request(request_method, "#{path_from_hash(url_hash)}.#{FORMAT.to_s}", options)
31
+ rescue OAuth2::Error => ex
32
+ if ex.message.include? 'The access token provided has expired'
33
+ log('oauth2 token expired, refreshing it...')
34
+ @conn = refreshed_token
35
+ retry
36
+ end
37
+ end
38
+ response.parsed if response
39
+ end
40
+
41
+ def objects_from_response(klass, request_method, url_hash, options={})
42
+ perform(url_hash, request_method, options).map { |response| klass.new(response) }
43
+ end
44
+
45
+ def object_from_response(klass, request_method, url_hash, options={})
46
+ klass.new( perform(url_hash, request_method, options) )
47
+ end
48
+
49
+ private
50
+
51
+ def has_required_oauth_details?
52
+ Kounta.client_id && Kounta.client_secret && Kounta.client_token && Kounta.client_refresh_token
53
+ end
54
+
55
+ def log(message)
56
+ @logger.info(message) unless !Kounta.enable_logging
57
+ end
58
+
59
+ def refreshed_token
60
+ OAuth2::AccessToken.from_hash(client, :refresh_token => Kounta.client_refresh_token).refresh!
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,42 @@
1
+ module Kounta
2
+
3
+ class Site < Kounta::Resource
4
+ property :company_id
5
+ property :name
6
+ property :code
7
+ property :business_number
8
+ property :contact_person
9
+ property :shipping_address
10
+ property :postal_address
11
+ property :email
12
+ property :mobile
13
+ property :phone
14
+ property :fax
15
+ property :location
16
+ property :image
17
+ property :website
18
+ property :register_level_reconciliation
19
+ property :products
20
+ property :price_list
21
+
22
+ coerce_key :shipping_address, Kounta::Address
23
+ coerce_key :postal_address, Kounta::Address
24
+ coerce_key :price_list, Kounta::PriceList
25
+
26
+ has_one :product, Kounta::Product, {:company_id => :company_id}, lambda { |klass, item_id| {companies: klass.company_id, sites: klass.id, products: item_id} }
27
+ has_one :category, Kounta::Category, {:company_id => :company_id}, lambda { |klass, item_id| {companies: klass.company_id, sites: klass.id, categories: item_id} }
28
+ has_one :address, Kounta::Address, {:company_id => :company_id}, lambda { |klass, item_id| {companies: klass.company_id, sites: klass.id, addresses: item_id} }
29
+ has_one :order, Kounta::Order, {:company_id => :company_id}, lambda { |klass, item_id| {companies: klass.company_id, sites: klass.id, orders: item_id} }
30
+
31
+ has_many :products, Kounta::Product, {:company_id => :company_id}, lambda { |klass| {companies: klass.company_id, sites: klass.id, products: nil} }
32
+ has_many :categories, Kounta::Category, {:company_id => :company_id}, lambda { |klass| {companies: klass.company_id, sites: klass.id, categories: nil} }
33
+ has_many :addresses, Kounta::Address, {:company_id => :company_id}, lambda { |klass| {companies: klass.company_id, sites: klass.id, addresses: nil} }
34
+ has_many :orders, Kounta::Order, {:company_id => :company_id}, lambda { |klass| {companies: klass.company_id, sites: klass.id, orders: nil} }
35
+
36
+ def resource_path
37
+ {companies: company_id, sites: id}
38
+ end
39
+
40
+ end
41
+
42
+ end
data/lib/kounta/tax.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Kounta
2
+
3
+ class Tax < Kounta::Resource
4
+ property :company_id
5
+ property :code
6
+ property :name
7
+ property :rate
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ module Kounta
2
+ VERSION = "0.0.1"
3
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+ require 'webmock/rspec'
6
+ require 'kounta'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
11
+
12
+ RSpec.configure do |config|
13
+ config.include Helpers
14
+
15
+ config.before(:each) do
16
+
17
+ stub_endpoints
18
+
19
+ Kounta.configure do |config|
20
+ config.client_id = "1234"
21
+ config.client_secret = "5678"
22
+ config.client_token = "abcd"
23
+ config.client_refresh_token = "efgh"
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,11 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Address do
4
+
5
+ subject { Kounta::Company.new.customer(701892).address(198109) }
6
+
7
+ it "should have a resource path" do
8
+ subject.resource_path.should eq({people: 701892, addresses: 198109})
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Category do
4
+
5
+ subject { Kounta::Company.new.category(1) }
6
+
7
+ it "should have a resource path" do
8
+ subject.resource_path.should eq({companies: 162, categories: 1})
9
+ end
10
+
11
+ end
@@ -0,0 +1,24 @@
1
+ require "helper"
2
+ require 'oj'
3
+
4
+ describe Kounta::Company do
5
+
6
+ subject { Kounta::Company }
7
+
8
+ it "should be able to create a company from an existing hash" do
9
+ subject.new(Oj.load(File.read("#{Kounta.root}/spec/fixtures/companies_me.json")))
10
+ end
11
+
12
+ it "should be able to create a company without a hash" do
13
+ subject.new.should be_an_instance_of subject
14
+ end
15
+
16
+ it "should have a resource path" do
17
+ subject.new.resource_path.should eq({companies: 162})
18
+ end
19
+
20
+ it "should have a base price list" do
21
+ subject.new.base_price_list.should be_an_instance_of Kounta::PriceList
22
+ end
23
+
24
+ end
@@ -0,0 +1,15 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Customer do
4
+
5
+ subject { Kounta::Company.new.customer(389427) }
6
+
7
+ it "should have a name" do
8
+ subject.name.should eq("Samuel Richardson")
9
+ end
10
+
11
+ it "should have a resource path" do
12
+ subject.resource_path.should eq({companies: 162, customers: 389427})
13
+ end
14
+
15
+ end
@@ -0,0 +1,21 @@
1
+ require "helper"
2
+
3
+ describe Kounta do
4
+
5
+ subject { Kounta }
6
+
7
+ it "should be able to call a configure block" do
8
+ subject.configure do |config|
9
+ config.should be(Kounta)
10
+ end
11
+ end
12
+
13
+ it "should be able to configure loggin" do
14
+ Kounta.enable_logging.should be(false)
15
+ Kounta.enable_logging = true
16
+ Kounta.enable_logging.should be(true)
17
+ Kounta.enable_logging = false
18
+ Kounta.enable_logging.should be(false)
19
+ end
20
+
21
+ end
@@ -0,0 +1,14 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Line do
4
+
5
+ subject { Kounta::Line.new }
6
+
7
+ it "should have a resource path" do
8
+ subject.company_id = 162
9
+ subject.order_id = 6789
10
+ subject.id = 1
11
+ subject.resource_path.should eq({companies: 162, orders: 6789, lines: 1})
12
+ end
13
+
14
+ end
@@ -0,0 +1,37 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Order do
4
+
5
+ subject { Kounta::Company.new.customer(701892).order(93824701) }
6
+
7
+ it "should be able to add payments to new orders" do
8
+ new_order = Kounta::Order.new
9
+ new_order.payments << Kounta::Payment.new(:amount => 50.00)
10
+ new_order.to_hash[:payments].length.should be(1)
11
+ new_order.to_hash[:payments][0][:amount].should be(50.00)
12
+ end
13
+
14
+ it "should be able to add lines to new orders" do
15
+ new_order = Kounta::Order.new
16
+ new_order.lines << Kounta::Line.new(:quantity => 4)
17
+ new_order.to_hash[:lines].length.should be(1)
18
+ new_order.to_hash[:lines][0][:quantity].should be(4)
19
+ end
20
+
21
+ it "should be able to add and serialise payments to created orders" do
22
+ subject.payments << Kounta::Payment.new(:amount => 50.00)
23
+ subject.to_hash[:payments].length.should be(3)
24
+ subject.to_hash[:payments][2][:amount].should be(50.00)
25
+ end
26
+
27
+ it "should be able to add and serialise lines to created orders" do
28
+ subject.lines << Kounta::Line.new(:quantity => 4)
29
+ subject.to_hash[:lines].length.should be(3)
30
+ subject.to_hash[:lines][0][:quantity].should be(6)
31
+ end
32
+
33
+ it "should have a resource path" do
34
+ subject.resource_path.should eq({companies: 162, orders: 93824701})
35
+ end
36
+
37
+ end
@@ -0,0 +1,14 @@
1
+ require "helper"
2
+
3
+ describe Kounta::Payment do
4
+
5
+ subject { Kounta::Payment.new }
6
+
7
+ it "should have a resource path" do
8
+ subject.company_id = 162
9
+ subject.order_id = 6789
10
+ subject.id = 1
11
+ subject.resource_path.should eq({companies: 162, orders: 6789, payments: 1})
12
+ end
13
+
14
+ end