peddler 0.1.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.
- data/.gitignore +1 -0
- data/History.txt +8 -0
- data/LICENSE +22 -0
- data/README.rdoc +118 -0
- data/Rakefile +18 -0
- data/VERSION.yml +4 -0
- data/init.rb +1 -0
- data/lib/peddler/client.rb +215 -0
- data/lib/peddler/feeds.rb +184 -0
- data/lib/peddler/handlers.rb +58 -0
- data/lib/peddler/inventory.rb +108 -0
- data/lib/peddler/legacy_reports.rb +107 -0
- data/lib/peddler/refunds.rb +54 -0
- data/lib/peddler/reports.rb +94 -0
- data/lib/peddler/transport.rb +135 -0
- data/lib/peddler.rb +25 -0
- data/peddler.gemspec +80 -0
- data/spec/peddler/client_spec.rb +47 -0
- data/spec/peddler/feeds_spec.rb +80 -0
- data/spec/peddler/handlers_spec.rb +19 -0
- data/spec/peddler/inventory_spec.rb +74 -0
- data/spec/peddler/legacy_reports_spec.rb +86 -0
- data/spec/peddler/refunds_spec.rb +35 -0
- data/spec/peddler/reports_spec.rb +19 -0
- data/spec/peddler/transport_spec.rb +58 -0
- data/spec/spec_helper.rb +3 -0
- metadata +108 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
module Peddler
|
2
|
+
module Inventory
|
3
|
+
module Queue
|
4
|
+
# Returns number of inventory uploads queued at Amazon
|
5
|
+
def self.count(transport)
|
6
|
+
transport.legacize_request
|
7
|
+
transport.path << "manual-reports/get-pending-uploads-count"
|
8
|
+
res = transport.execute_request
|
9
|
+
Peddler::Handlers::XMLHandler.decode_response(res).to_i
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# This is an inventory batch.
|
14
|
+
class Batch
|
15
|
+
attr_reader :id
|
16
|
+
attr_accessor :batch
|
17
|
+
|
18
|
+
def initialize(transport)
|
19
|
+
@transport = transport
|
20
|
+
@batch = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Uploads batch to Amazon.
|
24
|
+
def upload(params={})
|
25
|
+
raise PeddlerError.new("Batch already uploaded") unless @id.nil?
|
26
|
+
@transport.legacize_request
|
27
|
+
@transport.path << "catalog-upload/"
|
28
|
+
case params[:method].to_s
|
29
|
+
when "modify"
|
30
|
+
@transport.path << "modify-only"
|
31
|
+
@transport.body = file_content(:short)
|
32
|
+
when "purge"
|
33
|
+
@transport.path << "purge-replace"
|
34
|
+
@transport.body = file_content
|
35
|
+
else
|
36
|
+
@transport.path << "add-modify-delete"
|
37
|
+
@transport.body = file_content
|
38
|
+
end
|
39
|
+
params.delete(:method)
|
40
|
+
params = defaultize(params)
|
41
|
+
@transport.headers.merge!(params)
|
42
|
+
res = @transport.execute_request
|
43
|
+
if res =~ /^<BatchID>(.*)<\/BatchID>$/
|
44
|
+
@id = $1
|
45
|
+
else
|
46
|
+
@id = 0
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Reformats parameters and mixes in some defaults.
|
52
|
+
def defaultize(params)
|
53
|
+
{ :upload_for => "Marketplace",
|
54
|
+
:file_format =>"TabDelimited",
|
55
|
+
:asin_match_create => "Y",
|
56
|
+
:asinate => "Y",
|
57
|
+
:batch_id => "Y",
|
58
|
+
:email => "Y"}.each_pair{ |key, value| params[key] = value unless params[key] }
|
59
|
+
# Some Amazon dimwit figured he'd spell this differently
|
60
|
+
if params[:enable_expedited_shipping]
|
61
|
+
params["enable-expedited-shipping"] = params[:enable_expedited_shipping]
|
62
|
+
params.delete(:enable_expedited_shipping)
|
63
|
+
else
|
64
|
+
params["enable-expedited-shipping"] = "Y"
|
65
|
+
end
|
66
|
+
params
|
67
|
+
end
|
68
|
+
|
69
|
+
def file_content(type=:long)
|
70
|
+
return @file_content if @file_content
|
71
|
+
case type
|
72
|
+
when :long
|
73
|
+
out = "product-id\tproduct-id-type\titem-condition\tprice\tsku\tquantity\tadd-delete\twill-ship-internationally\texpedited-shipping\titem-note\titem-is-marketplace\tfulfillment-center-id\titem-name\titem-description\tcategory1\timage-url\tshipping-fee\tbrowse-path\tstorefront-feature\tboldface\tasin1\tasin2\tasin3\r\n"
|
74
|
+
@batch.each{ |item| out << item.to_s }
|
75
|
+
when :short
|
76
|
+
out = "sku\tprice\tquantity\r\n"
|
77
|
+
@batch.each{ |item| out << item.to_s(:short) }
|
78
|
+
end
|
79
|
+
@file_content = out
|
80
|
+
end
|
81
|
+
|
82
|
+
def file_content=(file_content)
|
83
|
+
@file_content = file_content
|
84
|
+
end
|
85
|
+
|
86
|
+
def <<(item)
|
87
|
+
@batch << item
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Item
|
92
|
+
attr_accessor :product_id, :product_id_type, :item_condition, :price, :sku, :quantity, :add_delete, :will_ship_internationally, :expedited_shipping, :item_note, :item_is_marketplace, :fulfillment_center_id, :item_name, :item_description, :category1, :image_url, :shipping_fee, :browse_path, :storefront_feature, :boldface, :asin1, :asin2, :asin3
|
93
|
+
|
94
|
+
def initialize(params={})
|
95
|
+
params.each_pair{ |key, value| send("#{key.to_s}=", value) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s(type=:long)
|
99
|
+
case type
|
100
|
+
when :long
|
101
|
+
"#{self.product_id}\t#{self.product_id_type}\t#{self.item_condition}\t#{self.price}\t#{self.sku}\t#{self.quantity}\t#{self.add_delete}\t#{self.will_ship_internationally}\t#{self.expedited_shipping}\t#{self.item_note}\t#{self.item_is_marketplace}\t#{self.fulfillment_center_id}\t#{self.item_name}\t#{self.item_description}\t#{self.category1}\t#{self.image_url}\t#{self.shipping_fee}\t#{self.browse_path}\t#{self.storefront_feature}\t#{self.boldface}\t#{self.asin1}\t#{self.asin2}\t#{self.asin3}\r\n"
|
102
|
+
when :short
|
103
|
+
"#{self.sku}\t#{self.price}\t#{self.quantity}\r\n"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Peddler
|
2
|
+
# This module contains methods to manage legacy reports -- anything that comes before section 7 in the API docs.
|
3
|
+
module LegacyReports
|
4
|
+
# Returns statuses of most recent reports in an array of OpenStructs.
|
5
|
+
def self.latest(transport,name,params={})
|
6
|
+
transport.legacize_request
|
7
|
+
if name == :upload
|
8
|
+
transport.path << "catalog-upload/get-batches"
|
9
|
+
transport.headers[:number_of_batches] = params[:count] if params[:count]
|
10
|
+
else
|
11
|
+
transport.path << "manual-reports/get-report-status"
|
12
|
+
transport.headers[:report_name] = name.to_s.camelize
|
13
|
+
transport.headers[:number_of_reports] = params[:count] if params[:count]
|
14
|
+
end
|
15
|
+
res = transport.execute_request
|
16
|
+
xml = Peddler::Handlers::XMLHandler.decode_response(res)
|
17
|
+
Peddler::Handlers::XMLHandler.parse_legacy(xml)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Requests a report to be generated and returns the report instance if request is successful.
|
21
|
+
def self.generate(transport,name,params={})
|
22
|
+
transport.legacize_request
|
23
|
+
transport.path << "manual-reports/generate-report-now"
|
24
|
+
transport.headers[:report_name] = name.to_s.camelize
|
25
|
+
transport.headers.merge!(params)
|
26
|
+
res = transport.execute_request
|
27
|
+
res =~ /SUCCESS/ ? Peddler::LegacyReports::Report.new(transport, name) : false
|
28
|
+
end
|
29
|
+
|
30
|
+
# A legacy report
|
31
|
+
class Report
|
32
|
+
attr_accessor :name, :id, :product_line, :frequency
|
33
|
+
|
34
|
+
def initialize(transport, name=nil, params={})
|
35
|
+
@transport, @name = transport, name
|
36
|
+
params.each_pair{ |key, value| self.send "#{key}=", value }
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
return nil if @name == :upload && @id.nil?
|
41
|
+
@body ||= download
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def download
|
46
|
+
return false if @name.nil? && @id.nil?
|
47
|
+
case @name.to_s
|
48
|
+
when "upload"
|
49
|
+
@transport.legacize_request
|
50
|
+
@transport.path << "download/errorlog"
|
51
|
+
@transport.headers["BatchID"] = @id
|
52
|
+
@transport.execute_request
|
53
|
+
else
|
54
|
+
@transport.legacize_request
|
55
|
+
@transport.path << "download/report"
|
56
|
+
if @id.nil?
|
57
|
+
@transport.headers[:report_name] = @name.to_s.camelize
|
58
|
+
if @name == :preorder
|
59
|
+
@transport.headers["productline"] = @product_line if @product_line
|
60
|
+
@transport.headers["frequency"] = @frequency if @frequency
|
61
|
+
end
|
62
|
+
else
|
63
|
+
@transport.headers["ReportID"] = @id
|
64
|
+
end
|
65
|
+
@transport.execute_request
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Status < OpenStruct
|
71
|
+
def initialize(input)
|
72
|
+
if input.kind_of? String
|
73
|
+
hash = input.scan(/([a-z]+)=([^=]+)($| )/).inject({}){ |memo, value| memo.merge( { @keymap[value[0]] => value[1].strip }) }
|
74
|
+
end
|
75
|
+
super(hash)
|
76
|
+
end
|
77
|
+
|
78
|
+
def id
|
79
|
+
@table[:id] || self.object_id
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class ReportStatus < Status
|
84
|
+
def initialize(input)
|
85
|
+
@keymap = {
|
86
|
+
"reportstarttime" => "starts_at",
|
87
|
+
"reportendtime" => "ends_at",
|
88
|
+
"reportid" => "id"}
|
89
|
+
super(input)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class UploadStatus < Status
|
94
|
+
def initialize(input)
|
95
|
+
@keymap = {
|
96
|
+
"status" => "status",
|
97
|
+
"batchid" => "id",
|
98
|
+
"numberofwarnings" => "number_of_warnings",
|
99
|
+
"activateditems" => "activated_items",
|
100
|
+
"itemsnotacivated" => "items_not_activated",
|
101
|
+
"itemsnotactivated" => "items_not_activated",
|
102
|
+
"dateandtime" => "datetime"}
|
103
|
+
super(input)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Peddler
|
2
|
+
module Refunds
|
3
|
+
# This is a refund batch.
|
4
|
+
class Batch
|
5
|
+
attr_accessor :batch
|
6
|
+
|
7
|
+
def initialize(transport)
|
8
|
+
@transport = transport
|
9
|
+
@batch = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_content
|
13
|
+
out = "order-id\tpayments-transaction-id\trefund-amount\treason\tmessage\r\n"
|
14
|
+
@file_content = @batch.inject(out){ |memo, item| memo << item.to_s }
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(item)
|
18
|
+
@batch << item
|
19
|
+
end
|
20
|
+
|
21
|
+
def upload
|
22
|
+
raise PeddlerError.new("Batch already uploaded") if @completed
|
23
|
+
@transport.legacize_request
|
24
|
+
@transport.path << "catalog-upload/batch-refund"
|
25
|
+
@transport.body = file_content
|
26
|
+
res = @transport.execute_request
|
27
|
+
@completed = true if res == "<Success>SUCCESS</Success>"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is a refund.
|
33
|
+
class Item
|
34
|
+
REFUND_REASONS = %w{ GeneralAdjustment CouldNotShip DifferentItem MerchandiseNotReceived MerchandiseNotAsDescribed }
|
35
|
+
|
36
|
+
attr_accessor :order_id, :payments_transaction_id, :refund_amount, :message
|
37
|
+
attr_reader :reason
|
38
|
+
|
39
|
+
def initialize(options={})
|
40
|
+
options.each_pair{ |key, value| send("#{key.to_s}=", value) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def reason=(reason)
|
44
|
+
@reason = reason if REFUND_REASONS.include?(reason)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"#{self.order_id}\t#{self.payments_transaction_id}\t#{self.refund_amount}\t#{self.reason}\t#{self.message}\r\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Peddler
|
2
|
+
# This module generates and downloads unshipped order reports.
|
3
|
+
# I decided to keep this out of Peddler::LegacyReports because the API is quite different.
|
4
|
+
module Reports
|
5
|
+
# This is an unshipped orders report. It's very similar to the feed objects so I'm just porting over the class.
|
6
|
+
# It will have a few stray attributes, but whatever.
|
7
|
+
class UnshippedOrdersReport < Peddler::Feeds::Feed
|
8
|
+
alias :unshipped_orders :batch
|
9
|
+
attr_accessor :starts_at, :ends_at, :scheduled
|
10
|
+
|
11
|
+
# Creates new unshipped order report. It literally sends a request to Amazon to generate the
|
12
|
+
# report if the report ID is not already set.
|
13
|
+
def initialize(transport, params={})
|
14
|
+
super(transport)
|
15
|
+
@mapped_params = {
|
16
|
+
"ReportID" => "id",
|
17
|
+
"StartDate" => "starts_at",
|
18
|
+
"EndDate" => "ends_at",
|
19
|
+
"DownloadType" => "type",
|
20
|
+
"Scheduled" => "scheduled",
|
21
|
+
"ReportStatus" => "status",
|
22
|
+
"SubmittedDate" => "submitted_at",
|
23
|
+
"StartedProcessingDate" => "started_processing_at",
|
24
|
+
"CompletedProcessingDate" => "completed_processing_at",
|
25
|
+
"CompletedProcesssingDate" => "completed_processing_at"}
|
26
|
+
params.each_pair{ |key, value| self.send "#{key}=", value }
|
27
|
+
@starts_at ||= (Date.today - 7).strftime("%Y-%m-%dT00:00:00-00:00")
|
28
|
+
@ends_at ||= (Date.today + 1).strftime("%Y-%m-%dT00:00:00-00:00")
|
29
|
+
#@type ||= "_GET_CONVERGED_FLAT_FILE_ACTIONABLE_ORDER_DATA_"
|
30
|
+
@type ||= "_GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_"
|
31
|
+
if @id.nil?
|
32
|
+
generate_report
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
private
|
37
|
+
def refresh_status
|
38
|
+
@transport.modernize_request
|
39
|
+
@transport.query_params.merge!({
|
40
|
+
"Action" => "reportStatus",
|
41
|
+
"reportId" => @id})
|
42
|
+
res = @transport.execute_request
|
43
|
+
process_response(res)
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_report
|
47
|
+
@transport.modernize_request
|
48
|
+
@transport.query_params.merge!({
|
49
|
+
"Action" => "generateReport",
|
50
|
+
"startDate" => @starts_at,
|
51
|
+
"endDate" => @ends_at,
|
52
|
+
"downloadType" => @type})
|
53
|
+
res = @transport.execute_request
|
54
|
+
process_response(res)
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_response(res)
|
58
|
+
xml = Peddler::Handlers::XMLHandler.decode_response(res)
|
59
|
+
params = Peddler::Handlers::XMLHandler.parse(:report, xml)
|
60
|
+
if params[0]
|
61
|
+
params[0].each_pair do |key, value|
|
62
|
+
if key == "ListOfDownloads"
|
63
|
+
params = Peddler::Handlers::XMLHandler.parse(:download, value)
|
64
|
+
@download = Peddler::Feeds::Download.new(@transport, params[0])
|
65
|
+
@batch = Peddler::Handlers::TabDelimitedHandler.decode_response(@download.to_s)
|
66
|
+
else
|
67
|
+
self.send "#{@mapped_params[key]}=", value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# This is an unshipped order.
|
75
|
+
class Item
|
76
|
+
attr_accessor :order_id, :order_item_id, :quantity, :ship_date, :carrier_name, :tracking_number, :ship_method
|
77
|
+
attr_reader :carrier_code
|
78
|
+
|
79
|
+
def initialize(params={})
|
80
|
+
params.each_pair{ |key, value| send("#{key}=", value) }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Validates when setting carrier code.
|
84
|
+
def carrier_code=(carrier_code)
|
85
|
+
@carrier_code = carrier_code if %w{USPS UPS FedEx other}.include?(carrier_code)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Outputs a formatted line for the tab-delimited upload file.
|
89
|
+
def to_s
|
90
|
+
"#{@order_id}\t#{@order_item_id}\t#{@quantity}\t#{@ship_date}\t#{@carrier_code}\t#{@carrier_name}\t#{@tracking_number}\t#{@ship_method}\r\n"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Peddler
|
2
|
+
class PeddlerError < StandardError
|
3
|
+
def initialize(msg)
|
4
|
+
super("#{msg}")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Our work horse. Runs on top of Net::HTTP.
|
9
|
+
class Transport
|
10
|
+
API_HOSTS = {:us => "secure.amazon.com",
|
11
|
+
:uk => "secure.amazon.co.uk",
|
12
|
+
:de => "secure.amazon.de",
|
13
|
+
:ca => "secure.amazon.ca",
|
14
|
+
:fr => "secure.amazon.fr",
|
15
|
+
:jp => "vendornet.amazon.co.jp" }
|
16
|
+
BASE_HEADERS = {"User-Agent" => "Peddler/#{Peddler::VERSION}",
|
17
|
+
"Content-Type" => "text/xml;charset=utf-8",
|
18
|
+
"Cookie" => "x-main=YvjPkwfntqDKun0QEmVRPcTTZDMe?Tn?; ubid-main=002-8989859-9917520; ubid-tacbus=019-5423258-4241018;x-tacbus=vtm4d53DvX@Sc9LxTnAnxsFL3DorwxJa; ubid-tcmacb=087-8055947-0795529; ubid-ty2kacbus=161-5477122-2773524; session-id=087-178254-5924832;session-id-time=950660664"}
|
19
|
+
|
20
|
+
BASE_PARAMS = {
|
21
|
+
"Service" => "MerchantQueryService"
|
22
|
+
}
|
23
|
+
|
24
|
+
attr_writer :username, :password
|
25
|
+
attr_accessor :path, :query_params, :headers, :body
|
26
|
+
|
27
|
+
#Returns request instance
|
28
|
+
def request
|
29
|
+
req = request_method.new("#{self.path.gsub(/\/$/, "")}/#{self.query_string}")
|
30
|
+
self.headers.each do |header, value|
|
31
|
+
if header.kind_of? Symbol
|
32
|
+
req[header.to_s.gsub(/_/, "")] = value
|
33
|
+
else
|
34
|
+
req[header] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
req.basic_auth(@username, @password) if @username && @password
|
38
|
+
req.body = self.body unless self.body.empty?
|
39
|
+
req
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute_request
|
43
|
+
begin
|
44
|
+
self.conn.start do |http|
|
45
|
+
res = http.request(self.request)
|
46
|
+
case res
|
47
|
+
when Net::HTTPSuccess
|
48
|
+
res.body
|
49
|
+
else
|
50
|
+
raise PeddlerError.new(res.body)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear_request
|
57
|
+
self.headers = BASE_HEADERS.dup
|
58
|
+
self.body = ""
|
59
|
+
end
|
60
|
+
|
61
|
+
def legacize_request
|
62
|
+
self.clear_request
|
63
|
+
self.path = "/exec/panama/seller-admin/"
|
64
|
+
self.query_params = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def modernize_request
|
68
|
+
self.clear_request
|
69
|
+
self.path = "/query/"
|
70
|
+
self.query_params = BASE_PARAMS.dup
|
71
|
+
end
|
72
|
+
|
73
|
+
def region=(region)
|
74
|
+
@conn = nil
|
75
|
+
region = region.to_sym
|
76
|
+
if API_HOSTS.has_key?(region)
|
77
|
+
@region = region
|
78
|
+
else
|
79
|
+
raise PeddlerError.new("Region not recognized")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def url
|
84
|
+
URI.parse("https://#{self.host}#{self.path.gsub(/\/$/, "")}/#{self.query_string}")
|
85
|
+
end
|
86
|
+
|
87
|
+
def dump_headers(msg)
|
88
|
+
msg.each_header do |key, value|
|
89
|
+
p "#{key}=#{value}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
#Returns the Net::HTTP instance.
|
95
|
+
def conn
|
96
|
+
if @conn
|
97
|
+
@conn
|
98
|
+
else
|
99
|
+
conn = Net::HTTP.new(host, 443)
|
100
|
+
conn.use_ssl = true
|
101
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
102
|
+
@conn = conn
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def request_method
|
107
|
+
if !self.body.empty? || !self.query_params.empty?
|
108
|
+
Net::HTTP::Post
|
109
|
+
else
|
110
|
+
Net::HTTP::Get
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def host
|
115
|
+
API_HOSTS[@region]
|
116
|
+
end
|
117
|
+
|
118
|
+
def query_string
|
119
|
+
unless query_params.empty?
|
120
|
+
query_params.inject("?") do |out, pair|
|
121
|
+
key, value = pair
|
122
|
+
key = key.to_s.gsub(/_([a-z])/) { $1.upcase } if key.kind_of? Symbol
|
123
|
+
value = value.httpdate if value.respond_to? :httpdate
|
124
|
+
out += "&" if out.size > 1
|
125
|
+
"#{out}#{url_encode(key)}=#{url_encode(value)}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def url_encode(value)
|
131
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
132
|
+
CGI.escape(value.to_s)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/peddler.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Peddler is a Ruby wrapper to the Amazon Inventory management API.
|
2
|
+
module Peddler
|
3
|
+
VERSION = "0.1"
|
4
|
+
end
|
5
|
+
|
6
|
+
class String #:nodoc: all
|
7
|
+
def camelize
|
8
|
+
self.gsub(/(^|_)(.)/) { $2.upcase }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require "net/https"
|
13
|
+
require "ostruct"
|
14
|
+
require "tempfile"
|
15
|
+
require "time"
|
16
|
+
require "xmlsimple"
|
17
|
+
|
18
|
+
require File.dirname(__FILE__) + "/peddler/client"
|
19
|
+
require File.dirname(__FILE__) + "/peddler/handlers"
|
20
|
+
require File.dirname(__FILE__) + "/peddler/feeds"
|
21
|
+
require File.dirname(__FILE__) + "/peddler/inventory"
|
22
|
+
require File.dirname(__FILE__) + "/peddler/legacy_reports"
|
23
|
+
require File.dirname(__FILE__) + "/peddler/refunds"
|
24
|
+
require File.dirname(__FILE__) + "/peddler/reports"
|
25
|
+
require File.dirname(__FILE__) + "/peddler/transport"
|
data/peddler.gemspec
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{peddler}
|
8
|
+
s.version = "0.1.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Hakan Senol Ensari"]
|
12
|
+
s.date = %q{2009-11-06}
|
13
|
+
s.description = %q{Peddler is a Ruby wrapper to the Amazon Inventory Management API.}
|
14
|
+
s.email = %q{hakan.ensari@papercavalier.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"History.txt",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION.yml",
|
26
|
+
"init.rb",
|
27
|
+
"lib/peddler.rb",
|
28
|
+
"lib/peddler/client.rb",
|
29
|
+
"lib/peddler/feeds.rb",
|
30
|
+
"lib/peddler/handlers.rb",
|
31
|
+
"lib/peddler/inventory.rb",
|
32
|
+
"lib/peddler/legacy_reports.rb",
|
33
|
+
"lib/peddler/refunds.rb",
|
34
|
+
"lib/peddler/reports.rb",
|
35
|
+
"lib/peddler/transport.rb",
|
36
|
+
"peddler.gemspec",
|
37
|
+
"spec/peddler/client_spec.rb",
|
38
|
+
"spec/peddler/feeds_spec.rb",
|
39
|
+
"spec/peddler/handlers_spec.rb",
|
40
|
+
"spec/peddler/inventory_spec.rb",
|
41
|
+
"spec/peddler/legacy_reports_spec.rb",
|
42
|
+
"spec/peddler/refunds_spec.rb",
|
43
|
+
"spec/peddler/reports_spec.rb",
|
44
|
+
"spec/peddler/transport_spec.rb",
|
45
|
+
"spec/spec_helper.rb"
|
46
|
+
]
|
47
|
+
s.homepage = %q{http://snl.github.com/peddler}
|
48
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = %q{1.3.5}
|
51
|
+
s.summary = %q{A Ruby wrapper to the Amazon Inventory Management API}
|
52
|
+
s.test_files = [
|
53
|
+
"spec/peddler/client_spec.rb",
|
54
|
+
"spec/peddler/feeds_spec.rb",
|
55
|
+
"spec/peddler/handlers_spec.rb",
|
56
|
+
"spec/peddler/inventory_spec.rb",
|
57
|
+
"spec/peddler/legacy_reports_spec.rb",
|
58
|
+
"spec/peddler/refunds_spec.rb",
|
59
|
+
"spec/peddler/reports_spec.rb",
|
60
|
+
"spec/peddler/transport_spec.rb",
|
61
|
+
"spec/spec_helper.rb"
|
62
|
+
]
|
63
|
+
|
64
|
+
if s.respond_to? :specification_version then
|
65
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
66
|
+
s.specification_version = 3
|
67
|
+
|
68
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
69
|
+
s.add_runtime_dependency(%q<xml-simple>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<xml-simple>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
74
|
+
end
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<xml-simple>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../spec_helper")
|
2
|
+
|
3
|
+
module Peddler
|
4
|
+
|
5
|
+
describe Client do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@client = Peddler::Client.new :username => "seller@example.com",
|
9
|
+
:password => "secret",
|
10
|
+
:region => "us"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return a new inventory batch" do
|
14
|
+
@client.new_inventory_batch.should be_an_instance_of(Peddler::Inventory::Batch)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return a new inventory item" do
|
18
|
+
@client.new_inventory_item.should be_an_instance_of(Peddler::Inventory::Item)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return a new order fulfillment feed" do
|
22
|
+
@client.new_order_fulfillment_feed.should be_an_instance_of(Peddler::Feeds::OrderFulfillment::Batch)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return a new fulfilled order" do
|
26
|
+
@client.new_fulfilled_order.should be_an_instance_of(Peddler::Feeds::OrderFulfillment::Item)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return a new order cancellation feed" do
|
30
|
+
@client.new_order_cancellation_feed.should be_an_instance_of(Peddler::Feeds::OrderCancellation::Batch)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return a new cancelled order" do
|
34
|
+
@client.new_cancelled_order.should be_an_instance_of(Peddler::Feeds::OrderCancellation::Item)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return a new refund batch" do
|
38
|
+
@client.new_refund_batch.should be_an_instance_of(Peddler::Refunds::Batch)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return a new report" do
|
42
|
+
@client.new_report(:foo).should be_an_instance_of(Peddler::LegacyReports::Report)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|