peddler 2.0.4 → 2.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.
- checksums.yaml +4 -4
- data/README.md +12 -18
- data/lib/mws/fulfillment_inbound_shipment/client.rb +18 -0
- data/lib/mws/fulfillment_outbound_shipment/client.rb +0 -11
- data/lib/mws/reports/parser.rb +35 -0
- data/lib/peddler/errors/builder.rb +2 -1
- data/lib/peddler/errors/class_generator.rb +1 -0
- data/lib/peddler/flat_file_parser.rb +1 -0
- data/lib/peddler/headers.rb +4 -0
- data/lib/peddler/marketplace.rb +1 -0
- data/lib/peddler/operation.rb +12 -2
- data/lib/peddler/version.rb +1 -1
- data/lib/peddler/xml_parser.rb +1 -0
- data/lib/peddler/xml_response_parser.rb +1 -1
- data/test/helper.rb +1 -0
- data/test/integration/test_feeds.rb +3 -3
- data/test/integration/test_fulfillment_inbound_shipment.rb +3 -3
- data/test/integration/test_fulfillment_inventory.rb +2 -2
- data/test/integration/test_fulfillment_outbound_shipment.rb +1 -1
- data/test/integration/test_merchant_fulfillment.rb +57 -1
- data/test/integration/test_multibyte_queries.rb +1 -1
- data/test/integration/test_mws_headers.rb +1 -1
- data/test/integration/test_off_amazon_payments.rb +1 -1
- data/test/integration/test_orders.rb +2 -2
- data/test/integration/test_products.rb +12 -12
- data/test/integration/test_recommendations.rb +2 -2
- data/test/integration/test_reports.rb +6 -6
- data/test/integration/test_sellers.rb +1 -1
- data/test/integration/test_subscriptions.rb +3 -3
- data/test/integration_helper.rb +9 -7
- data/test/unit/mws/test_feeds_client.rb +6 -6
- data/test/unit/mws/test_finances_client.rb +5 -5
- data/test/unit/mws/test_fulfillment_inbound_shipment_client.rb +40 -24
- data/test/unit/mws/test_fulfillment_inventory_client.rb +3 -3
- data/test/unit/mws/test_fulfillment_outbound_shipment_client.rb +11 -11
- data/test/unit/mws/test_merchant_fulfillment_client.rb +5 -5
- data/test/unit/mws/test_off_amazon_payments_client.rb +17 -17
- data/test/unit/mws/test_orders_client.rb +7 -7
- data/test/unit/mws/test_products_client.rb +15 -15
- data/test/unit/mws/test_recommendations_client.rb +4 -4
- data/test/unit/mws/test_reports_client.rb +14 -14
- data/test/unit/mws/test_sellers_client.rb +3 -3
- data/test/unit/mws/test_subscriptions_client.rb +11 -11
- data/test/unit/peddler/errors/test_builder.rb +4 -4
- data/test/unit/peddler/errors/test_class_generator.rb +1 -1
- data/test/unit/peddler/errors/test_error.rb +5 -5
- data/test/unit/peddler/errors/test_parser.rb +4 -4
- data/test/unit/peddler/test_client.rb +20 -20
- data/test/unit/peddler/test_flat_file_parser.rb +11 -11
- data/test/unit/peddler/test_headers.rb +1 -1
- data/test/unit/peddler/test_marketplace.rb +1 -1
- data/test/unit/peddler/test_operation.rb +18 -12
- data/test/unit/peddler/test_parser.rb +3 -3
- data/test/unit/peddler/test_structured_list.rb +5 -5
- data/test/unit/peddler/test_vcr_matcher.rb +4 -4
- data/test/unit/peddler/test_xml_parser.rb +3 -3
- data/test/unit/peddler/test_xml_response_parser.rb +4 -4
- data/test/unit/test_mws.rb +1 -1
- data/test/vcr_cassettes/FulfillmentInboundShipment.yml +36 -36
- data/test/vcr_cassettes/MerchantFulfillment.yml +309 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f41b36a62c193efceb4d464ce3447e4934c8ca97907ca6940c431914f0b1406
|
4
|
+
data.tar.gz: f28498baf2e3c8aa67bd983e74f9c4ca4daffcb15424a25ededee6b72fa3606a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b62d1f3d5eec15b6e2a7d3fee02445a3edc2596a6c8bafbf25deac633a01f401467cbe7d4214a4e1bf6a3b8958ec437b4ac1ed1f7d5a1c7f3638cdfcb80815ba
|
7
|
+
data.tar.gz: d5c7e57ed43bdf7f1aeba013a499d10b5bdf18361b1f5edf2e25eb82703ddec81eafe596aa0ae57fc920a1591d5ab399ef2c7093bb350c1f319e3e3709a857f8
|
data/README.md
CHANGED
@@ -8,9 +8,7 @@
|
|
8
8
|
|
9
9
|
To use Amazon MWS, you must have an eligible seller account and register as an [application developer](https://docs.developer.amazonservices.com/en_US/dev_guide/DG_Registering.html#DG_Registering__RegisteringAsADeveloper).
|
10
10
|
|
11
|
-
Amazon has [multiple regions](https://docs.developer.amazonservices.com/en_US/dev_guide/DG_Endpoints.html). Each region requires application developers to register individually.
|
12
|
-
|
13
|
-
Some MWS API sections may require additional authorisation from Amazon.
|
11
|
+
Amazon has [multiple regions](https://docs.developer.amazonservices.com/en_US/dev_guide/DG_Endpoints.html). Each region requires application developers to register individually. Some MWS API sections may require additional authorisation from Amazon.
|
14
12
|
|
15
13
|

|
16
14
|
|
@@ -22,7 +20,7 @@ Require the library.
|
|
22
20
|
require "peddler"
|
23
21
|
```
|
24
22
|
|
25
|
-
A client requires the AWS credentials of the application developer. If you are working
|
23
|
+
A client requires the AWS credentials of the application developer. If you are working in a single MWS region, you can set them globally.
|
26
24
|
|
27
25
|
|
28
26
|
```bash
|
@@ -30,32 +28,28 @@ export AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID
|
|
30
28
|
export AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
|
31
29
|
```
|
32
30
|
|
33
|
-
Now,
|
31
|
+
Now, create a client with the Amazon marketplace you signed up on and a merchant ID. Peddler provides a class for each API section under an eponymous namespace.
|
34
32
|
|
35
33
|
```ruby
|
36
|
-
MWS
|
37
|
-
|
38
|
-
|
39
|
-
# or the shorthand
|
40
|
-
MWS.orders(marketplace: "ATVPDKIKX0DER",
|
41
|
-
merchant_id: "123")
|
34
|
+
client = MWS.orders(marketplace: "ATVPDKIKX0DER",
|
35
|
+
merchant_id: "123")
|
42
36
|
|
43
|
-
#
|
44
|
-
MWS.orders(marketplace: "US",
|
45
|
-
|
37
|
+
# or a shorthand
|
38
|
+
client = MWS.orders(marketplace: "US",
|
39
|
+
merchant_id: "123")
|
46
40
|
```
|
47
41
|
|
48
42
|
If you are creating a [client for another seller](https://developer.amazonservices.com/gp/mws/faq.html#developForSeller), pass an MWS Auth Token as well.
|
49
43
|
|
50
44
|
```ruby
|
51
|
-
MWS.orders(marketplace: "ATVPDKIKX0DER",
|
52
|
-
|
53
|
-
|
45
|
+
client = MWS.orders(marketplace: "ATVPDKIKX0DER",
|
46
|
+
merchant_id: "123",
|
47
|
+
auth_token: "123")
|
54
48
|
```
|
55
49
|
|
56
50
|
You won't be able to create a client for another seller if you are in different regions.
|
57
51
|
|
58
|
-
If you are working with sellers across multiple regions, a single set of credentials will not be enough. In that case,
|
52
|
+
If you are working with sellers across multiple regions, a single set of credentials will not be enough. In that case, do not use global environment variables and pass your AWS credentials when creating the client.
|
59
53
|
|
60
54
|
```ruby
|
61
55
|
client = MWS.orders(marketplace: "ATVPDKIKX0DER",
|
@@ -152,6 +152,14 @@ module MWS
|
|
152
152
|
.structure!('ASINList', 'Id')
|
153
153
|
|
154
154
|
run
|
155
|
+
# Work around a bug upstream
|
156
|
+
#
|
157
|
+
# @see https://github.com/hakanensari/peddler/issues/122
|
158
|
+
rescue Peddler::Errors::Error => error
|
159
|
+
raise unless error.message.include?("Value null at 'asinList'")
|
160
|
+
|
161
|
+
get_prep_instructions_for_asin_with_bad_params(ship_to_country_code,
|
162
|
+
*asin_list)
|
155
163
|
end
|
156
164
|
|
157
165
|
# Sends transportation information to Amazon about an inbound shipment
|
@@ -365,6 +373,16 @@ module MWS
|
|
365
373
|
.structure!('InboundShipmentItems', 'member')
|
366
374
|
.structure!('PrepDetailsList', 'member')
|
367
375
|
end
|
376
|
+
|
377
|
+
def get_prep_instructions_for_asin_with_bad_params(_ship_to_country_code,
|
378
|
+
*asin_list)
|
379
|
+
operation
|
380
|
+
.add('AsinList' => asin_list)
|
381
|
+
.structure!('AsinList', 'Id')
|
382
|
+
.delete_if { |key, _val| key.include?('ASINList') }
|
383
|
+
|
384
|
+
run
|
385
|
+
end
|
368
386
|
end
|
369
387
|
end
|
370
388
|
end
|
@@ -28,12 +28,6 @@ module MWS
|
|
28
28
|
# @option opts [Boolean] :include_cod_fulfillment_preview
|
29
29
|
# @return [Peddler::XMLParser]
|
30
30
|
def get_fulfillment_preview(address, items, opts = {})
|
31
|
-
opts = opts.dup
|
32
|
-
if opts.key?(:include_cod_fulfillment_preview)
|
33
|
-
opts['IncludeCODFulfillmentPreview'] =
|
34
|
-
opts.delete(:include_cod_fulfillment_preview)
|
35
|
-
end
|
36
|
-
|
37
31
|
operation('GetFulfillmentPreview')
|
38
32
|
.add(opts)
|
39
33
|
.add('Address' => address, 'Items' => items)
|
@@ -66,11 +60,6 @@ module MWS
|
|
66
60
|
displayable_order_comment,
|
67
61
|
shipping_speed_category,
|
68
62
|
destination_address, items, opts = {})
|
69
|
-
opts = opts.dup
|
70
|
-
if opts.key?(:cod_settings)
|
71
|
-
opts['CODSettings'] = opts.delete(:cod_settings)
|
72
|
-
end
|
73
|
-
|
74
63
|
operation('CreateFulfillmentOrder')
|
75
64
|
.add(opts)
|
76
65
|
.add('SellerFulfillmentOrderId' => seller_fulfillment_order_id,
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'peddler/flat_file_parser'
|
4
|
+
require 'peddler/xml_response_parser'
|
5
|
+
|
6
|
+
module MWS
|
7
|
+
module Reports
|
8
|
+
# @api private
|
9
|
+
module Parser
|
10
|
+
class << self
|
11
|
+
# We're massaging data produced by a motley army of developers. It's
|
12
|
+
# messy.
|
13
|
+
def new(res, encoding)
|
14
|
+
# Don't parse if there's no body
|
15
|
+
return res unless res.body
|
16
|
+
|
17
|
+
if xml?(res)
|
18
|
+
XMLResponseParser.new(res)
|
19
|
+
else
|
20
|
+
# Amazon returns a variety of content types for flat files. I simply
|
21
|
+
# assume anything not XML is a flat file.
|
22
|
+
FlatFileParser.new(res, encoding)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def xml?(res)
|
27
|
+
return true if res.headers['Content-Type'].start_with?('text/xml')
|
28
|
+
return true if res.body.start_with?('<?xml')
|
29
|
+
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -11,7 +11,7 @@ module Peddler
|
|
11
11
|
class Builder
|
12
12
|
extend Forwardable
|
13
13
|
|
14
|
-
DIGIT_AS_FIRST_CHAR = /^\d
|
14
|
+
DIGIT_AS_FIRST_CHAR = /^\d/.freeze
|
15
15
|
private_constant :DIGIT_AS_FIRST_CHAR
|
16
16
|
|
17
17
|
def_delegator :error, :response
|
@@ -29,6 +29,7 @@ module Peddler
|
|
29
29
|
def build
|
30
30
|
parse_original_response
|
31
31
|
return if bad_class_name?
|
32
|
+
|
32
33
|
error_class.new(error_message, error)
|
33
34
|
end
|
34
35
|
|
data/lib/peddler/headers.rb
CHANGED
@@ -7,6 +7,7 @@ module Peddler
|
|
7
7
|
# @return [Integer, nil]
|
8
8
|
def mws_quota_max
|
9
9
|
return unless headers['x-mws-quota-max']
|
10
|
+
|
10
11
|
headers['x-mws-quota-max'].to_i
|
11
12
|
end
|
12
13
|
|
@@ -14,6 +15,7 @@ module Peddler
|
|
14
15
|
# @return [Integer, nil]
|
15
16
|
def mws_quota_remaining
|
16
17
|
return unless headers['x-mws-quota-remaining']
|
18
|
+
|
17
19
|
headers['x-mws-quota-remaining'].to_i
|
18
20
|
end
|
19
21
|
|
@@ -21,6 +23,7 @@ module Peddler
|
|
21
23
|
# @return [Time, nil]
|
22
24
|
def mws_quota_resets_on
|
23
25
|
return unless headers['x-mws-quota-resetsOn']
|
26
|
+
|
24
27
|
Time.parse(headers['x-mws-quota-resetsOn'])
|
25
28
|
end
|
26
29
|
|
@@ -34,6 +37,7 @@ module Peddler
|
|
34
37
|
# @return [Time, nil]
|
35
38
|
def mws_timestamp
|
36
39
|
return unless headers['x-mws-timestamp']
|
40
|
+
|
37
41
|
Time.parse(headers['x-mws-timestamp'])
|
38
42
|
end
|
39
43
|
|
data/lib/peddler/marketplace.rb
CHANGED
@@ -63,6 +63,7 @@ module Peddler
|
|
63
63
|
['A13V1IB3VIYZZH', 'FR', 'mws-eu.amazonservices.com'],
|
64
64
|
['APJ6JRA9NG5V4', 'IT', 'mws-eu.amazonservices.com'],
|
65
65
|
['A1F83G8C2ARO7P', 'GB', 'mws-eu.amazonservices.com'],
|
66
|
+
['A33AVAJ2PDY3EV', 'TR', 'mws-eu.amazonservices.com'],
|
66
67
|
['A21TJRUUN4KGV', 'IN', 'mws.amazonservices.in'],
|
67
68
|
['A39IBJ37TRP1C6', 'AU', 'mws.amazonservices.com.au'],
|
68
69
|
['A1VC38T7YXB528', 'JP', 'mws.amazonservices.jp'],
|
data/lib/peddler/operation.rb
CHANGED
@@ -7,7 +7,9 @@ require 'peddler/structured_list'
|
|
7
7
|
module Peddler
|
8
8
|
# @api private
|
9
9
|
class Operation < SimpleDelegator
|
10
|
-
CAPITAL_LETTERS = /[A-Z]
|
10
|
+
CAPITAL_LETTERS = /[A-Z]/.freeze
|
11
|
+
ALL_CAPS = %w[sku cod].freeze
|
12
|
+
private_constant :CAPITAL_LETTERS, :ALL_CAPS
|
11
13
|
|
12
14
|
def initialize(action)
|
13
15
|
super('Action' => action)
|
@@ -55,8 +57,16 @@ module Peddler
|
|
55
57
|
sym
|
56
58
|
.to_s
|
57
59
|
.split('_')
|
58
|
-
.map { |token| token
|
60
|
+
.map { |token| capitalize(token) }
|
59
61
|
.join
|
60
62
|
end
|
63
|
+
|
64
|
+
def capitalize(word)
|
65
|
+
if ALL_CAPS.any? { |val| val == word }
|
66
|
+
word.upcase
|
67
|
+
else
|
68
|
+
word.capitalize
|
69
|
+
end
|
70
|
+
end
|
61
71
|
end
|
62
72
|
end
|
data/lib/peddler/version.rb
CHANGED
data/lib/peddler/xml_parser.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -4,21 +4,21 @@ require 'integration_helper'
|
|
4
4
|
require 'mws/feeds'
|
5
5
|
|
6
6
|
class TestFeeds < IntegrationTest
|
7
|
-
def
|
7
|
+
def test_getting_feed_submission_count
|
8
8
|
clients.each do |client|
|
9
9
|
res = client.get_feed_submission_count
|
10
10
|
refute_empty res.parse
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def test_getting_feed_submission_list
|
15
15
|
clients.each do |client|
|
16
16
|
res = client.get_feed_submission_list
|
17
17
|
refute_empty res.parse
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def test_submitting_feeds
|
22
22
|
feed_content = "sku\tprice\tquantity\nwidget\t\t0\n"
|
23
23
|
feed_type = '_POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA_'
|
24
24
|
|
@@ -8,7 +8,7 @@ class TestFulfillmentInboundShipment < IntegrationTest
|
|
8
8
|
:postal_code, :country_code)
|
9
9
|
Item = Struct.new(:seller_sku, :quantity)
|
10
10
|
|
11
|
-
def
|
11
|
+
def test_creating_inbound_shipment_plan
|
12
12
|
address = Address.new('John', '1 Main St', 'New York', 'NY', '10001', 'US')
|
13
13
|
item = Item.new('123', 1)
|
14
14
|
clients.each do |client|
|
@@ -17,14 +17,14 @@ class TestFulfillmentInboundShipment < IntegrationTest
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def test_getting_service_status
|
21
21
|
clients.each do |client|
|
22
22
|
res = client.get_service_status
|
23
23
|
refute_empty res.parse
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
27
|
+
def test_handling_large_requests
|
28
28
|
address = Address.new('John', '1 Main St', 'New York', 'NY', '10001', 'US')
|
29
29
|
items = Array.new(100) { |i| Item.new(i, 1) }
|
30
30
|
clients.each do |client|
|
@@ -4,14 +4,14 @@ require 'integration_helper'
|
|
4
4
|
require 'mws/fulfillment_inventory'
|
5
5
|
|
6
6
|
class TestFulfillmentInventory < IntegrationTest
|
7
|
-
def
|
7
|
+
def test_listing_inventory_supply
|
8
8
|
clients.each do |client|
|
9
9
|
res = client.list_inventory_supply(query_start_date_time: Date.today - 30)
|
10
10
|
refute_empty res.parse
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def test_getting_service_status
|
15
15
|
clients.each do |client|
|
16
16
|
res = client.get_service_status
|
17
17
|
refute_empty res.parse
|
@@ -4,7 +4,7 @@ require 'integration_helper'
|
|
4
4
|
require 'mws/fulfillment_outbound_shipment'
|
5
5
|
|
6
6
|
class TestFulfillmentOutboundShipment < IntegrationTest
|
7
|
-
def
|
7
|
+
def test_getting_service_status
|
8
8
|
clients.each do |client|
|
9
9
|
res = client.get_service_status
|
10
10
|
refute_empty res.parse
|
@@ -4,10 +4,66 @@ require 'integration_helper'
|
|
4
4
|
require 'mws/merchant_fulfillment'
|
5
5
|
|
6
6
|
class TestMerchantFulfillment < IntegrationTest
|
7
|
-
def
|
7
|
+
def test_getting_eligible_shipments_in_the_us
|
8
|
+
client = clients.us
|
9
|
+
res = client.get_eligible_shipping_services(shipment_request_details)
|
10
|
+
refute res.dig('ShippingServiceList', 'ShippingService').count.zero?
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_shipping_in_the_us
|
14
|
+
client = clients.us
|
15
|
+
res = client.create_shipment(shipment_request_details, 'UPS_PTP_GND')
|
16
|
+
label = res.dig('Shipment', 'Label')
|
17
|
+
data_compressed = Base64.decode64(label['FileContents']['Contents'])
|
18
|
+
# data = Zlib.gunzip(data_compressed)
|
19
|
+
data = Zlib::GzipReader.new(StringIO.new(data_compressed)).read
|
20
|
+
assert_equal label['FileContents']['Checksum'], Digest::MD5.base64digest(data)
|
21
|
+
res = client.cancel_shipment(res.dig('Shipment', 'ShipmentId'))
|
22
|
+
assert_equal 'RefundPending', res.dig('Shipment', 'Status')
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_getting_service_status
|
8
26
|
clients.each do |client|
|
9
27
|
res = client.get_service_status
|
10
28
|
refute_empty res.parse
|
11
29
|
end
|
12
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def shipment_request_details
|
35
|
+
{
|
36
|
+
amazon_order_id: '123-1234567-1234567',
|
37
|
+
item_list: [
|
38
|
+
{
|
39
|
+
order_item_id: '12345678901234',
|
40
|
+
quantity: 1
|
41
|
+
}
|
42
|
+
],
|
43
|
+
ship_from_address: {
|
44
|
+
name: 'John Doe',
|
45
|
+
address_line_1: '10 Jay St',
|
46
|
+
email: 'john@example.com',
|
47
|
+
city: 'Brooklyn',
|
48
|
+
state_or_province_code: 'NY',
|
49
|
+
postal_code: '11201',
|
50
|
+
country_code: 'US',
|
51
|
+
phone: '7181231234'
|
52
|
+
},
|
53
|
+
package_dimensions: {
|
54
|
+
length: 40,
|
55
|
+
width: 30,
|
56
|
+
height: 10,
|
57
|
+
unit: 'centimeters'
|
58
|
+
},
|
59
|
+
weight: {
|
60
|
+
value: 1000,
|
61
|
+
unit: 'grams'
|
62
|
+
},
|
63
|
+
shipping_service_options: {
|
64
|
+
carrier_will_pick_up: false,
|
65
|
+
delivery_experience: 'DeliveryConfirmationWithoutSignature'
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
13
69
|
end
|