snl-peddler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|