snl-peddler 0.1.0
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 +2 -0
- data/LICENSE +22 -0
- data/README.rdoc +104 -0
- data/Rakefile +17 -0
- data/VERSION.yml +4 -0
- data/lib/peddler.rb +25 -0
- data/lib/peddler/client.rb +240 -0
- data/lib/peddler/feeds.rb +181 -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/peddler.gemspec +78 -0
- data/spec/peddler/client_spec.rb +47 -0
- data/spec/peddler/feeds_spec.rb +70 -0
- data/spec/peddler/handlers_spec.rb +15 -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 +106 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Peddler
|
2
|
+
module Handlers
|
3
|
+
class XMLHandler
|
4
|
+
# Decodes an XML response.
|
5
|
+
def self.decode_response(res)
|
6
|
+
XmlSimple.xml_in(res)
|
7
|
+
end
|
8
|
+
# Parses responses to uploads and status queries for feeds in Section 7 of the docs. Walks
|
9
|
+
# through lists and returns an array of hashes.
|
10
|
+
def self.parse(name, xml)
|
11
|
+
name = name.to_s.capitalize
|
12
|
+
list = xml["#{name}sStatusList"] || xml["#{name}sList"]
|
13
|
+
if list
|
14
|
+
list.collect { |s| parse_status(name, s) }
|
15
|
+
else
|
16
|
+
[ parse_status(name, xml) ]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Parses legacy responses to queries on statuses of generated reports and inventory uploads.
|
21
|
+
def self.parse_legacy(xml)
|
22
|
+
if xml["Batch"]
|
23
|
+
xml["Batch"].collect { |input| Peddler::LegacyReports::UploadStatus.new(input) }
|
24
|
+
elsif xml["Report"]
|
25
|
+
xml["Report"].collect { |input| Peddler::LegacyReports::ReportStatus.new(input) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
protected
|
29
|
+
def self.parse_status(name, xml)
|
30
|
+
if xml[name]
|
31
|
+
xml[name][0].inject({}) do |memo, pair|
|
32
|
+
key, value = pair
|
33
|
+
value[0] = Time.parse(value[0]) if key =~ /Date$/
|
34
|
+
memo.merge!({ key => value[0] })
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class TabDelimitedHandler
|
41
|
+
# Parses tab-delimited content, returning an array of OpenStruct objects corresponding to the rows in the former.
|
42
|
+
def self.decode_response(res)
|
43
|
+
lines = res.split("\n")
|
44
|
+
if lines.size > 1
|
45
|
+
params = lines[0].split("\t").collect{ |value| value.gsub(/-/, "_") }
|
46
|
+
params_size = params.size
|
47
|
+
(1..(lines.size - 1)).collect do |line_key|
|
48
|
+
values = lines[line_key].split("\t")
|
49
|
+
data = (0..(params_size - 1)).inject({}) { |memo, key| memo.merge( { params[key] => values[key] } ) }
|
50
|
+
OpenStruct.new(data)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
res
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -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
|