ruby-mws 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rvmrc +1 -0
- data/Gemfile +14 -0
- data/LICENSE +20 -0
- data/README.markdown +98 -0
- data/Rakefile +1 -0
- data/lib/ruby-mws.rb +58 -0
- data/lib/ruby-mws/api/base.rb +80 -0
- data/lib/ruby-mws/api/inventory.rb +20 -0
- data/lib/ruby-mws/api/order.rb +36 -0
- data/lib/ruby-mws/api/query.rb +73 -0
- data/lib/ruby-mws/api/response.rb +9 -0
- data/lib/ruby-mws/base.rb +26 -0
- data/lib/ruby-mws/connection.rb +52 -0
- data/lib/ruby-mws/exceptions.rb +12 -0
- data/lib/ruby-mws/version.rb +3 -0
- data/ruby-mws.gemspec +30 -0
- data/spec/fixtures/list_orders_canonical.txt +4 -0
- data/spec/mws_spec.rb +11 -0
- data/spec/ruby-mws/api/base_spec.rb +42 -0
- data/spec/ruby-mws/api/inventory_spec.rb +30 -0
- data/spec/ruby-mws/api/order_spec.rb +61 -0
- data/spec/ruby-mws/api/query_spec.rb +43 -0
- data/spec/ruby-mws/base_spec.rb +18 -0
- data/spec/ruby-mws/connection_spec.rb +26 -0
- data/spec/spec_helper.rb +34 -0
- metadata +158 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.3-p0@ruby-mws
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Erik Lyngved
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
ruby-mws
|
2
|
+
========
|
3
|
+
|
4
|
+
Under development
|
5
|
+
-----------------
|
6
|
+
|
7
|
+
This is a Ruby gem that wraps the Amazon Marketplace Web Service (MWS) API. It is still missing many features, but some basic requests are available.
|
8
|
+
|
9
|
+
Please use at your own risk. This has not been tested thoroughly and is not guaranteed to work in any capacity. See the LICENSE file for more details.
|
10
|
+
|
11
|
+
Quick Start
|
12
|
+
-----------
|
13
|
+
|
14
|
+
### Initialize the connection object
|
15
|
+
|
16
|
+
Pass in your developer account credentials. All four params below are required.
|
17
|
+
|
18
|
+
mws = MWS.new {:aws_access_key_id => "AKIAIFKEXAMPLE4WZHHA",
|
19
|
+
:secret_access_key => "abc123def456/SECRET/+ghi789jkl",
|
20
|
+
:seller_id => "A27WEXAMPLEBXY",
|
21
|
+
:marketplace_id => "ATVPDKIKX0DER"}
|
22
|
+
|
23
|
+
### Make a request
|
24
|
+
|
25
|
+
Let's use the Orders API to retrieve recently updated orders.
|
26
|
+
|
27
|
+
# Retrieve all orders updated within the last 4 hours
|
28
|
+
response = mws.orders.list_orders :last_updated_after => Time.now-4.hours # Rails helper used
|
29
|
+
|
30
|
+
(All datetime fields accept Time or DateTime objects, as well as strings in iso8601 format.)
|
31
|
+
|
32
|
+
### Parse the response
|
33
|
+
|
34
|
+
We can parse our response to view the orders and any other data returned.
|
35
|
+
|
36
|
+
response.orders.first # => { "amazon_order_id" => "002-EXAMPLE-0031387",
|
37
|
+
"purchase_date" => "2012-01-13T19:11:46.000Z",
|
38
|
+
... }
|
39
|
+
|
40
|
+
Response objects are accessible in Hash or method notation.
|
41
|
+
|
42
|
+
response.orders == response[:orders] # => true
|
43
|
+
|
44
|
+
Use `keys` and `has_key?` to discover what's in the response.
|
45
|
+
|
46
|
+
response.keys # => ["last_updated_before", "orders"]
|
47
|
+
response.has_key? :last_updated_before # => true
|
48
|
+
|
49
|
+
### NextToken requests
|
50
|
+
|
51
|
+
For responses with long lists of data, results are returned from the service in pages (usually 100 per page). Example:
|
52
|
+
|
53
|
+
response = mws.orders.list_orders :last_updated_after => Time.now-1.week # returns 100 orders
|
54
|
+
|
55
|
+
Here, there are more orders to be returned. You can call `has_next?` on the same API instance to see if the last response returned has a next page. If so, calling `next` will make the request for the next page.
|
56
|
+
|
57
|
+
mws.orders.has_next? # => true
|
58
|
+
next_response = mws.orders.next # returns next page of orders
|
59
|
+
|
60
|
+
You can keep calling `next` on the API instance as long as `has_next?` returns true.
|
61
|
+
|
62
|
+
You can always go about the manual way as per Amazon's docs:
|
63
|
+
|
64
|
+
next_response = mws.orders.list_orders_by_next_response :next_token => response.next_token
|
65
|
+
|
66
|
+
### Underscore notation
|
67
|
+
|
68
|
+
ruby-mws wraps Amazon's CamelCase convention with Ruby-friendly underscore notation. This goes for request names and params, as well as response field names.
|
69
|
+
|
70
|
+
Available Requests
|
71
|
+
------------------
|
72
|
+
|
73
|
+
@mws = MWS.new(authentication_hash) # initialize the connection object
|
74
|
+
|
75
|
+
This object can be used to access all API services. Below are examples on how to make the different requests that are available so far. Refer to the [Amazon MWS Reference Docs](https://developer.amazonservices.com/) for available fields for each request.
|
76
|
+
|
77
|
+
### Orders API
|
78
|
+
|
79
|
+
* ListOrders - gets orders by time frame and other parameters
|
80
|
+
|
81
|
+
`@mws.orders.list_orders :last_updated_after => Time.now-4.hours`
|
82
|
+
|
83
|
+
* GetOrder - gets orders by Amazon order ID
|
84
|
+
|
85
|
+
`@mws.orders.get_order :amazon_order_id => "002-EXAMPLE-0031387"`
|
86
|
+
|
87
|
+
`:amazon_order_id` can be an array to retrieve multiple orders.
|
88
|
+
|
89
|
+
* ListOrderItems - gets order items for one order ID
|
90
|
+
|
91
|
+
`@mws.orders.list_order_items :amazon_order_id => "002-EXAMPLE-0031387"`
|
92
|
+
|
93
|
+
### Fulfillment Inventory API
|
94
|
+
|
95
|
+
* ListInventorySupply - returns availability of inventory, only returns items based on list of SKUs or last change date
|
96
|
+
|
97
|
+
`@mws.inventory.list_inventory_supply :seller_skus => %w[PF-5VZN-04XR V4-03EY-LAL1 OC-TUKC-031P`
|
98
|
+
`@mws.inventory.list_inventory_supply :query_start_date_time => Time.now-1.day`
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/ruby-mws.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'httparty'
|
3
|
+
require 'base64'
|
4
|
+
require 'cgi'
|
5
|
+
require 'openssl'
|
6
|
+
# require 'hashie/mash'
|
7
|
+
require 'hashie'
|
8
|
+
require 'rash'
|
9
|
+
|
10
|
+
module MWS
|
11
|
+
def self.new(options={})
|
12
|
+
MWS::Base.new(options.symbolize_keys!)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Some convenience methods randomly put here. Thanks, Rails
|
17
|
+
|
18
|
+
class Hash
|
19
|
+
def stringify_keys!
|
20
|
+
keys.each do |key|
|
21
|
+
self[key.to_s] = delete(key)
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def symbolize_keys!
|
27
|
+
self.replace(self.symbolize_keys)
|
28
|
+
end
|
29
|
+
|
30
|
+
def symbolize_keys
|
31
|
+
inject({}) do |options, (key, value)|
|
32
|
+
options[(key.to_sym rescue key) || key] = value
|
33
|
+
options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class String
|
39
|
+
|
40
|
+
def camelize(first_letter_in_uppercase = true)
|
41
|
+
if first_letter_in_uppercase
|
42
|
+
self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
43
|
+
else
|
44
|
+
self.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
require 'ruby-mws/base'
|
50
|
+
require 'ruby-mws/connection'
|
51
|
+
require 'ruby-mws/exceptions'
|
52
|
+
require 'ruby-mws/version'
|
53
|
+
|
54
|
+
require 'ruby-mws/api/base'
|
55
|
+
require 'ruby-mws/api/inventory'
|
56
|
+
require 'ruby-mws/api/order'
|
57
|
+
require 'ruby-mws/api/query'
|
58
|
+
require 'ruby-mws/api/response'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# This class serves as a parent class to the API classes.
|
2
|
+
# It shares connection handling, query string building, ?? among the models.
|
3
|
+
|
4
|
+
module MWS
|
5
|
+
module API
|
6
|
+
|
7
|
+
class Base
|
8
|
+
include HTTParty
|
9
|
+
# debug_output $stderr # only in development
|
10
|
+
format :xml
|
11
|
+
headers "User-Agent" => "ruby-mws/#{MWS::VERSION} (Language=Ruby/1.9.3-p0)"
|
12
|
+
headers "Content-Type" => "text/xml"
|
13
|
+
|
14
|
+
attr_accessor :response
|
15
|
+
|
16
|
+
def initialize(connection)
|
17
|
+
@connection = connection
|
18
|
+
@saved_options = {}
|
19
|
+
@next = {}
|
20
|
+
self.class.base_uri "https://#{connection.host}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.def_request(requests, *options)
|
24
|
+
[requests].flatten.each do |name|
|
25
|
+
# class variable = a way to store options splat to pass into pseudomethod
|
26
|
+
self.class_variable_set("@@#{name}_options", options.first)
|
27
|
+
self.class_eval %Q{
|
28
|
+
def #{name}(params={})
|
29
|
+
send_request(:#{name}, params, @@#{name}_options)
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def send_request(name, params, options)
|
36
|
+
# prepare all required params...
|
37
|
+
params = [params, options, @connection.to_hash].inject :merge
|
38
|
+
|
39
|
+
# default/common params
|
40
|
+
params[:action] ||= name.to_s.camelize
|
41
|
+
params[:signature_method] ||= 'HmacSHA256'
|
42
|
+
params[:signature_version] ||= '2'
|
43
|
+
params[:timestamp] ||= Time.now.iso8601
|
44
|
+
params[:version] ||= '2009-01-01'
|
45
|
+
|
46
|
+
params[:lists] ||= {}
|
47
|
+
params[:lists][:marketplace_id] = "MarketplaceId.Id"
|
48
|
+
|
49
|
+
query = Query.new params
|
50
|
+
@response = Response.new self.class.send(params[:verb], query.request_uri)
|
51
|
+
|
52
|
+
begin
|
53
|
+
res = @response.send("#{name}_response").send("#{name}_result")
|
54
|
+
if @next[:token] = res.next_token # modifying, not comparing
|
55
|
+
@next[:action] = params[:next_action] || (name.match(/_by_next_token/) ? name : "#{name}_by_next_token")
|
56
|
+
end
|
57
|
+
params[:mods].each {|mod| mod.call(res) } if params[:mods]
|
58
|
+
res
|
59
|
+
rescue NoMethodError
|
60
|
+
@response
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def has_next?
|
65
|
+
not @next[:token].nil?
|
66
|
+
end
|
67
|
+
alias :has_next :has_next?
|
68
|
+
|
69
|
+
def next
|
70
|
+
self.send(@next[:action], :next_token => @next[:token]) unless @next[:token].nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def inspect
|
74
|
+
"#<#{self.class.to_s}:#{object_id}>"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MWS
|
2
|
+
module API
|
3
|
+
|
4
|
+
class Inventory < Base
|
5
|
+
|
6
|
+
def_request [:list_inventory_supply, :list_inventory_supply_by_next_token],
|
7
|
+
:verb => :get,
|
8
|
+
:uri => '/FulfillmentInventory/2010-10-01',
|
9
|
+
:version => '2010-10-01',
|
10
|
+
:lists => {
|
11
|
+
:seller_skus => "SellerSkus.member"
|
12
|
+
},
|
13
|
+
:mods => [
|
14
|
+
lambda {|r| r.inventory_supply_list = [r.inventory_supply_list.member].flatten}
|
15
|
+
]
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MWS
|
2
|
+
module API
|
3
|
+
|
4
|
+
class Order < Base
|
5
|
+
|
6
|
+
|
7
|
+
def_request [:list_orders, :list_orders_by_next_token],
|
8
|
+
:verb => :get,
|
9
|
+
:uri => '/Orders/2011-01-01',
|
10
|
+
:version => '2011-01-01',
|
11
|
+
:mods => [
|
12
|
+
lambda {|r| r.orders = r.orders.order}
|
13
|
+
]
|
14
|
+
|
15
|
+
def_request [:list_order_items, :list_order_items_by_next_token],
|
16
|
+
:verb => :get,
|
17
|
+
:uri => '/Orders/2011-01-01',
|
18
|
+
:version => '2011-01-01',
|
19
|
+
:mods => [
|
20
|
+
lambda {|r| r.order_items = [r.order_items.order_item].flatten}
|
21
|
+
]
|
22
|
+
|
23
|
+
def_request :get_order,
|
24
|
+
:verb => :get,
|
25
|
+
:uri => '/Orders/2011-01-01',
|
26
|
+
:version => '2011-01-01',
|
27
|
+
:lists => {
|
28
|
+
:amazon_order_id => "AmazonOrderId.Id"
|
29
|
+
},
|
30
|
+
:mods => [
|
31
|
+
lambda {|r| r.orders = [r.orders.order].flatten}
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module MWS
|
2
|
+
module API
|
3
|
+
|
4
|
+
class Query
|
5
|
+
|
6
|
+
def initialize(params)
|
7
|
+
@params = params
|
8
|
+
params[:lists].each do |field,label|
|
9
|
+
[params.delete(field)].compact.flatten.each_with_index do |item,i|
|
10
|
+
params["#{label}.#{i+1}"] = item
|
11
|
+
end
|
12
|
+
end unless params[:lists].nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def canonical
|
16
|
+
[@params[:verb].to_s.upcase, @params[:host], @params[:uri], build_sorted_query].join("\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def signature
|
20
|
+
digest = OpenSSL::Digest::Digest.new('sha256')
|
21
|
+
key = @params[:secret_access_key]
|
22
|
+
Base64.encode64(OpenSSL::HMAC.digest(digest, key, canonical)).chomp
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_uri
|
26
|
+
"https://" << @params[:host] << @params[:uri] << '?' << build_sorted_query(signature)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def build_sorted_query(signature=nil)
|
31
|
+
params = @params.dup.delete_if {|k,v| exclude_from_query.include? k}
|
32
|
+
params[:signature] = signature if signature
|
33
|
+
params.stringify_keys!
|
34
|
+
|
35
|
+
# hack to capitalize AWS in param names
|
36
|
+
# TODO: Allow for multiple marketplace ids
|
37
|
+
params = Hash[params.map{|k,v| [k.camelize.sub(/Aws/,'AWS'), v]}]
|
38
|
+
|
39
|
+
params = params.sort.map! { |p| "#{p[0]}=#{process_param(p[1])}" }
|
40
|
+
params.join('&')
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_param(param)
|
44
|
+
case param
|
45
|
+
when Time, DateTime
|
46
|
+
escape(param.iso8601)
|
47
|
+
else
|
48
|
+
escape(param.to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def escape(value)
|
53
|
+
value.gsub(/([^a-zA-Z0-9_.~-]+)/) do
|
54
|
+
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def exclude_from_query
|
59
|
+
[
|
60
|
+
:verb,
|
61
|
+
:host,
|
62
|
+
:uri,
|
63
|
+
:secret_access_key,
|
64
|
+
:return,
|
65
|
+
:lists,
|
66
|
+
:mods
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MWS
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_accessor :connection
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
@connection = MWS::Connection.new(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def orders
|
12
|
+
@orders ||= MWS::API::Order.new(@connection)
|
13
|
+
end
|
14
|
+
|
15
|
+
def inventory
|
16
|
+
@inventory ||= MWS::API::Inventory.new(@connection)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# serves as a server ping
|
21
|
+
def self.server_time
|
22
|
+
MWS::Connection.server_time
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MWS
|
2
|
+
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
DEFAULT_HOST = "mws.amazonservices.com"
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
attrs.each do |a|
|
9
|
+
self.class.send(:attr_reader, a)
|
10
|
+
instance_variable_set("@#{a}", options[a])
|
11
|
+
end
|
12
|
+
|
13
|
+
@host ||= DEFAULT_HOST
|
14
|
+
|
15
|
+
attrs.each { |a| raise MissingConnectionOptions, ":#{a} is required" if instance_variable_get("@#{a}").nil?}
|
16
|
+
end
|
17
|
+
|
18
|
+
def public_attrs
|
19
|
+
[:aws_access_key_id, :seller_id, :marketplace_id, :host]
|
20
|
+
end
|
21
|
+
|
22
|
+
def private_attrs
|
23
|
+
[:secret_access_key]
|
24
|
+
end
|
25
|
+
|
26
|
+
def attrs
|
27
|
+
public_attrs + private_attrs
|
28
|
+
end
|
29
|
+
|
30
|
+
# an attempt to hide sensitive login credentials in logs, just being paranoid
|
31
|
+
def inspect
|
32
|
+
"#<MWS::Connection:#{object_id}>"
|
33
|
+
end
|
34
|
+
|
35
|
+
def server_time
|
36
|
+
self.class.server_time @host
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
hsh = {}
|
41
|
+
attrs.each { |a| hsh[a] = instance_variable_get("@#{a}")}
|
42
|
+
hsh
|
43
|
+
end
|
44
|
+
|
45
|
+
# No connection needs to be initialized for this call
|
46
|
+
def self.server_time(host=DEFAULT_HOST)
|
47
|
+
response = HTTParty.get("https://#{host}")
|
48
|
+
Time.parse(response['PingResponse']['Timestamp']['timestamp'])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/ruby-mws.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ruby-mws/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ruby-mws"
|
7
|
+
s.version = MWS::VERSION
|
8
|
+
s.authors = ["Erik Lyngved"]
|
9
|
+
s.email = ["elyngved@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{RubyMWS Gem}
|
12
|
+
s.description = %q{Under development!! This gem will serve as a wrapper for Amazon.com's Marketplace Web Service (MWS) API.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "ruby-mws"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "ephemeral_response"
|
24
|
+
|
25
|
+
s.add_runtime_dependency "httparty"
|
26
|
+
s.add_runtime_dependency "nokogiri"
|
27
|
+
s.add_runtime_dependency "ruby-hmac"
|
28
|
+
s.add_runtime_dependency "hashie"
|
29
|
+
s.add_runtime_dependency "rash"
|
30
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
GET
|
2
|
+
mws.amazonservices.com
|
3
|
+
/Orders/2011-01-01
|
4
|
+
AWSAccessKeyId=access&Action=ListOrders&LastUpdatedAfter=2012-01-13T15%3A48%3A55-05%3A00&MarketplaceId.Id.1=ATVPDKIKX0DER&SellerId=A27WNMSA8OPBXY&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-01-14T15%3A48%3A55-05%3A00&Version=2011-01-01
|
data/spec/mws_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::API::Base do
|
4
|
+
class FakeApi < MWS::API::Base
|
5
|
+
def self.test_params
|
6
|
+
{
|
7
|
+
:verb => :get,
|
8
|
+
:uri => '/FakeApi/2011-01-01',
|
9
|
+
:version => '2011-01-01'
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def_request [:list_fake_objects, :list_fake_objects_by_next_token], self.test_params
|
14
|
+
end
|
15
|
+
|
16
|
+
before :all do
|
17
|
+
@api = FakeApi.new(mws_object.connection)
|
18
|
+
end
|
19
|
+
|
20
|
+
context "def_request" do
|
21
|
+
it "should generate methods for each request defined" do
|
22
|
+
@api.respond_to?(:list_fake_objects).should be_true
|
23
|
+
@api.respond_to?(:list_fake_objects_by_next_token).should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should store request options as a class variable" do
|
27
|
+
FakeApi.class_variable_get('@@list_fake_objects_options').should be
|
28
|
+
FakeApi.class_variable_get('@@list_fake_objects_by_next_token_options').should be
|
29
|
+
FakeApi.class_variable_get('@@list_fake_objects_options').should == FakeApi.test_params
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "methods generated by def_request" do
|
34
|
+
it "should call send_request with the right params" do
|
35
|
+
@api.should_receive(:send_request).
|
36
|
+
with(:list_fake_objects, {}, FakeApi.test_params).
|
37
|
+
and_raise(TestWorksError)
|
38
|
+
lambda{ @api.list_fake_objects }.should raise_error TestWorksError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::API::Inventory do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
EphemeralResponse.activate
|
7
|
+
@mws = MWS.new(auth_params)
|
8
|
+
@timestamp = "2012-01-15T18:07:48-05:00"
|
9
|
+
end
|
10
|
+
|
11
|
+
context "requests" do
|
12
|
+
|
13
|
+
describe "list_inventory_supply" do
|
14
|
+
it "should return items based on seller SKUs" do
|
15
|
+
items = @mws.inventory.list_inventory_supply :timestamp => @timestamp,
|
16
|
+
:seller_skus => %w[PF-5VZN-04XR V4-03EY-LAL1 OC-TUKC-031P]
|
17
|
+
items.should have_key :inventory_supply_list
|
18
|
+
items.inventory_supply_list.should be_an_instance_of Array
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return items with inventory changes since a certain time" do
|
22
|
+
items = @mws.inventory.list_inventory_supply :timestamp => @timestamp,
|
23
|
+
:query_start_date_time => "2012-01-15T10:04:01-05:00"
|
24
|
+
items.should have_key :inventory_supply_list
|
25
|
+
items.inventory_supply_list.should be_an_instance_of Array
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::API::Order do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
EphemeralResponse.activate
|
7
|
+
@mws = MWS.new(auth_params)
|
8
|
+
@timestamp = "2012-01-15T17:28:17-05:00"
|
9
|
+
end
|
10
|
+
|
11
|
+
context "requests" do
|
12
|
+
describe "list_orders" do
|
13
|
+
it "should receive a list_orders_result" do
|
14
|
+
orders = @mws.orders.list_orders :last_updated_after => "2012-01-15T13:07:26-05:00" ,
|
15
|
+
:timestamp => @timestamp
|
16
|
+
orders.orders.should be_an_instance_of Array
|
17
|
+
orders.should == @mws.orders.response.list_orders_response.list_orders_result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "list_orders_by_next_token" do
|
22
|
+
it "should receive a list_orders_by_next_token_result" do
|
23
|
+
orders = @mws.orders.list_orders_by_next_token :timestamp => @timestamp,
|
24
|
+
:next_token => "zwR7fTkqiKp/YYuZQbKv1QafEPREmauvizt1MIhPYZZl3LSdPSOgN1byEfyVqilNBpcRD1uxgRxTg2dsYWmzKd8ytOVgN7d/KyNtf5fepe2OEd7gVZif6X81/KsTyqd1e64rGQd1TyCS68vI7Bqws+weLxD7b1CVZciW6QBe7o2FizcdzSXGSiKsUkM4/6QGllhc4XvGqg5e0zIeaOVNezxWEXvdeDL7eReo//Hc3LMRF18hF5ZYNntoCB8irbDGcVkxA+q0fZYwE7+t4NjazyEZY027dXAVTSGshRBy6ZTthhfBGj6Dwur8WCwcU8Vc25news0zC1w6gK1h3EdXw7a3u+Q12Uw9ZROnI57RGr4CrtRODNGKSRdGvNrxcHpI2aLZKrJa2MgKRa+KsojCckrDiHqb8mBEJ88g6izJN42dQcLTGQRwBej+BBOOHYP4"
|
25
|
+
orders.orders.should be_an_instance_of Array
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "get_order" do
|
30
|
+
it "should return one order for one order id" do
|
31
|
+
order = @mws.orders.get_order :amazon_order_id => "102-4850183-7065809",
|
32
|
+
:timestamp => @timestamp
|
33
|
+
order.should have_key :orders
|
34
|
+
order.orders.should be_an_instance_of Array
|
35
|
+
order.orders.first.should have_key :amazon_order_id
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return multiple orders for multiple order ids" do
|
39
|
+
orders = @mws.orders.get_order :amazon_order_id => ["102-4850183-7065809", "002-3400187-5292203"],
|
40
|
+
:timestamp => @timestamp
|
41
|
+
orders.orders.should be_an_instance_of Array
|
42
|
+
orders.orders.size.should == 2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "list_order_items" do
|
47
|
+
it "should return a list of items for an order" do
|
48
|
+
order = @mws.orders.list_order_items :amazon_order_id => "102-4850183-7065809",
|
49
|
+
:timestamp => @timestamp
|
50
|
+
order.should have_key :amazon_order_id
|
51
|
+
order.should have_key :order_items
|
52
|
+
order.order_items.should be_an_instance_of Array
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "list_order_items_by_next_token" do
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::API::Query do
|
4
|
+
before do
|
5
|
+
@query = MWS::API::Query.new(default_params)
|
6
|
+
@query.stub!(:signature).and_return("SIGNATURE")
|
7
|
+
end
|
8
|
+
|
9
|
+
context ".query" do
|
10
|
+
it "should assemble the canonical query" do
|
11
|
+
@query.canonical.should == %Q{GET
|
12
|
+
mws.amazonservices.com
|
13
|
+
/Orders/2011-01-01
|
14
|
+
AWSAccessKeyId=#{default_params[:aws_access_key_id]}&Action=ListOrders&LastUpdatedAfter=2012-01-13T15%3A48%3A55-05%3A00&MarketplaceId.Id.1=#{default_params[:marketplace_id]}&SellerId=#{default_params[:seller_id]}&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-01-14T15%3A48%3A55-05%3A00&Version=2011-01-01}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context ".request_uri" do
|
19
|
+
it "should assemble the request uri" do
|
20
|
+
@query.request_uri.should == "https://mws.amazonservices.com/Orders/2011-01-01?AWSAccessKeyId=#{default_params[:aws_access_key_id]}&Action=ListOrders&LastUpdatedAfter=2012-01-13T15%3A48%3A55-05%3A00&MarketplaceId.Id.1=#{default_params[:marketplace_id]}&SellerId=#{default_params[:seller_id]}&Signature=SIGNATURE&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2012-01-14T15%3A48%3A55-05%3A00&Version=2011-01-01"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_params
|
25
|
+
# some random keys
|
26
|
+
params = {
|
27
|
+
:last_updated_after => "2012-01-13T15:48:55-05:00",
|
28
|
+
:verb => :get,
|
29
|
+
:uri => "/Orders/2011-01-01",
|
30
|
+
:version => "2011-01-01",
|
31
|
+
:host => "mws.amazonservices.com",
|
32
|
+
:action => "ListOrders",
|
33
|
+
:signature_method => "HmacSHA256",
|
34
|
+
:signature_version => "2",
|
35
|
+
:timestamp => "2012-01-14T15:48:55-05:00",
|
36
|
+
:lists => {
|
37
|
+
:marketplace_id => "MarketplaceId.Id"
|
38
|
+
}
|
39
|
+
}.merge(auth_params)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::Base do
|
4
|
+
|
5
|
+
it "should connect and get a timestamp" do
|
6
|
+
MWS::Base.server_time.class.should == Time
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'initialize' do
|
10
|
+
it "should create a connection object" do
|
11
|
+
mws = MWS::Base.new(auth_params)
|
12
|
+
mws.should be
|
13
|
+
mws.connection.should be
|
14
|
+
mws.connection.class.should == MWS::Connection
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MWS::Connection do
|
4
|
+
|
5
|
+
context "initialize" do
|
6
|
+
it "should intialize with all requires params" do
|
7
|
+
conn = MWS::Connection.new auth_params
|
8
|
+
conn.class.should == MWS::Connection
|
9
|
+
auth_params.each do |k,v|
|
10
|
+
conn.send(k).should == v
|
11
|
+
end
|
12
|
+
conn.host.should == "mws.amazonservices.com"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise an exception when a required param is missing" do
|
16
|
+
auth_params.each do |k,v|
|
17
|
+
lambda { MWS::Connection.new auth_params.delete(k) }.should raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should override host when sent a host param" do
|
22
|
+
conn = MWS::Connection.new auth_params.merge({host: 'newhost.com'})
|
23
|
+
conn.host.should == 'newhost.com'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'ephemeral_response'
|
5
|
+
|
6
|
+
require 'ruby-mws'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
def fixture(path)
|
10
|
+
File.open(File.join(File.expand_path(File.dirname(__FILE__)), 'fixtures', path)).read
|
11
|
+
end
|
12
|
+
|
13
|
+
def mws_object
|
14
|
+
@mws_object ||= MWS.new(auth_params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_params
|
18
|
+
@auth_params ||=
|
19
|
+
begin
|
20
|
+
hsh = YAML.load(File.open(File.join(File.expand_path(File.dirname(__FILE__)), 'credentials.yml'))).symbolize_keys!
|
21
|
+
rescue
|
22
|
+
# some fake auth values
|
23
|
+
{
|
24
|
+
:aws_access_key_id => 'access',
|
25
|
+
:secret_access_key => 'super_secret',
|
26
|
+
:seller_id => 'doma',
|
27
|
+
:marketplace_id => '123'
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class TestWorksError < StandardError
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-mws
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Erik Lyngved
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70093283680840 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70093283680840
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ephemeral_response
|
27
|
+
requirement: &70093283680380 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70093283680380
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httparty
|
38
|
+
requirement: &70093283679760 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70093283679760
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: nokogiri
|
49
|
+
requirement: &70093283679200 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70093283679200
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: ruby-hmac
|
60
|
+
requirement: &70093283678440 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70093283678440
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hashie
|
71
|
+
requirement: &70093283678000 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70093283678000
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rash
|
82
|
+
requirement: &70093283677580 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70093283677580
|
91
|
+
description: Under development!! This gem will serve as a wrapper for Amazon.com's
|
92
|
+
Marketplace Web Service (MWS) API.
|
93
|
+
email:
|
94
|
+
- elyngved@gmail.com
|
95
|
+
executables: []
|
96
|
+
extensions: []
|
97
|
+
extra_rdoc_files: []
|
98
|
+
files:
|
99
|
+
- .gitignore
|
100
|
+
- .rvmrc
|
101
|
+
- Gemfile
|
102
|
+
- LICENSE
|
103
|
+
- README.markdown
|
104
|
+
- Rakefile
|
105
|
+
- lib/ruby-mws.rb
|
106
|
+
- lib/ruby-mws/api/base.rb
|
107
|
+
- lib/ruby-mws/api/inventory.rb
|
108
|
+
- lib/ruby-mws/api/order.rb
|
109
|
+
- lib/ruby-mws/api/query.rb
|
110
|
+
- lib/ruby-mws/api/response.rb
|
111
|
+
- lib/ruby-mws/base.rb
|
112
|
+
- lib/ruby-mws/connection.rb
|
113
|
+
- lib/ruby-mws/exceptions.rb
|
114
|
+
- lib/ruby-mws/version.rb
|
115
|
+
- ruby-mws.gemspec
|
116
|
+
- spec/fixtures/list_orders_canonical.txt
|
117
|
+
- spec/mws_spec.rb
|
118
|
+
- spec/ruby-mws/api/base_spec.rb
|
119
|
+
- spec/ruby-mws/api/inventory_spec.rb
|
120
|
+
- spec/ruby-mws/api/order_spec.rb
|
121
|
+
- spec/ruby-mws/api/query_spec.rb
|
122
|
+
- spec/ruby-mws/base_spec.rb
|
123
|
+
- spec/ruby-mws/connection_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
homepage: ''
|
126
|
+
licenses: []
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project: ruby-mws
|
145
|
+
rubygems_version: 1.8.15
|
146
|
+
signing_key:
|
147
|
+
specification_version: 3
|
148
|
+
summary: RubyMWS Gem
|
149
|
+
test_files:
|
150
|
+
- spec/fixtures/list_orders_canonical.txt
|
151
|
+
- spec/mws_spec.rb
|
152
|
+
- spec/ruby-mws/api/base_spec.rb
|
153
|
+
- spec/ruby-mws/api/inventory_spec.rb
|
154
|
+
- spec/ruby-mws/api/order_spec.rb
|
155
|
+
- spec/ruby-mws/api/query_spec.rb
|
156
|
+
- spec/ruby-mws/base_spec.rb
|
157
|
+
- spec/ruby-mws/connection_spec.rb
|
158
|
+
- spec/spec_helper.rb
|