ruby-mws 0.0.2
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.
- 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
|