peddler 2.1.1 → 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -41
  3. data/lib/mws.rb +2 -1
  4. data/lib/mws/easy_ship.rb +3 -0
  5. data/lib/mws/easy_ship/client.rb +93 -0
  6. data/lib/mws/fulfillment_outbound_shipment/client.rb +1 -1
  7. data/lib/mws/orders/client.rb +7 -9
  8. data/lib/mws/shipment_invoicing.rb +3 -0
  9. data/lib/mws/shipment_invoicing/client.rb +74 -0
  10. data/lib/mws/subscriptions/client.rb +2 -2
  11. data/lib/peddler/client.rb +17 -13
  12. data/lib/peddler/errors/builder.rb +7 -2
  13. data/lib/peddler/errors/class_generator.rb +9 -4
  14. data/lib/peddler/errors/error.rb +6 -2
  15. data/lib/peddler/errors/parser.rb +1 -1
  16. data/lib/peddler/flat_file_parser.rb +5 -3
  17. data/lib/peddler/headers.rb +6 -1
  18. data/lib/peddler/marketplace.rb +25 -19
  19. data/lib/peddler/operation.rb +17 -11
  20. data/lib/peddler/parser.rb +1 -1
  21. data/lib/peddler/structured_list.rb +1 -1
  22. data/lib/peddler/vcr_matcher.rb +7 -7
  23. data/lib/peddler/version.rb +1 -1
  24. data/lib/peddler/xml_parser.rb +1 -1
  25. data/lib/peddler/xml_response_parser.rb +3 -2
  26. metadata +15 -152
  27. data/lib/mws/off_amazon_payments/client.rb +0 -362
  28. data/lib/peddler/content.rb +0 -69
  29. data/lib/peddler/mws.rb +0 -58
  30. data/test/credentials.rb +0 -23
  31. data/test/helper.rb +0 -15
  32. data/test/integration/internals/test_errors.rb +0 -17
  33. data/test/integration/internals/test_multibyte_queries.rb +0 -17
  34. data/test/integration/internals/test_mws_headers.rb +0 -21
  35. data/test/integration/internals/test_string_encodings.rb +0 -38
  36. data/test/integration/test_feeds.rb +0 -37
  37. data/test/integration/test_fulfillment_inbound_shipment.rb +0 -35
  38. data/test/integration/test_fulfillment_inventory.rb +0 -20
  39. data/test/integration/test_fulfillment_outbound_shipment.rb +0 -13
  40. data/test/integration/test_merchant_fulfillment.rb +0 -69
  41. data/test/integration/test_off_amazon_payments.rb +0 -13
  42. data/test/integration/test_orders.rb +0 -30
  43. data/test/integration/test_products.rb +0 -97
  44. data/test/integration/test_recommendations.rb +0 -20
  45. data/test/integration/test_reports.rb +0 -51
  46. data/test/integration/test_sellers.rb +0 -13
  47. data/test/integration/test_subscriptions.rb +0 -27
  48. data/test/integration_helper.rb +0 -53
  49. data/test/mws.yml +0 -36
  50. data/test/mws.yml.example +0 -32
  51. data/test/null_client.rb +0 -25
  52. data/test/recorder.rb +0 -39
  53. data/test/unit/mws/test_feeds_client.rb +0 -103
  54. data/test/unit/mws/test_finances_client.rb +0 -74
  55. data/test/unit/mws/test_fulfillment_inbound_shipment_client.rb +0 -426
  56. data/test/unit/mws/test_fulfillment_inventory_client.rb +0 -48
  57. data/test/unit/mws/test_fulfillment_outbound_shipment_client.rb +0 -202
  58. data/test/unit/mws/test_merchant_fulfillment_client.rb +0 -109
  59. data/test/unit/mws/test_off_amazon_payments_client.rb +0 -286
  60. data/test/unit/mws/test_orders_client.rb +0 -111
  61. data/test/unit/mws/test_products_client.rb +0 -248
  62. data/test/unit/mws/test_recommendations_client.rb +0 -62
  63. data/test/unit/mws/test_reports_client.rb +0 -209
  64. data/test/unit/mws/test_sellers_client.rb +0 -47
  65. data/test/unit/mws/test_subscriptions_client.rb +0 -182
  66. data/test/unit/peddler/errors/test_builder.rb +0 -65
  67. data/test/unit/peddler/errors/test_class_generator.rb +0 -18
  68. data/test/unit/peddler/errors/test_error.rb +0 -33
  69. data/test/unit/peddler/errors/test_parser.rb +0 -44
  70. data/test/unit/peddler/test_client.rb +0 -203
  71. data/test/unit/peddler/test_flat_file_parser.rb +0 -111
  72. data/test/unit/peddler/test_headers.rb +0 -103
  73. data/test/unit/peddler/test_marketplace.rb +0 -47
  74. data/test/unit/peddler/test_operation.rb +0 -87
  75. data/test/unit/peddler/test_parser.rb +0 -36
  76. data/test/unit/peddler/test_structured_list.rb +0 -39
  77. data/test/unit/peddler/test_vcr_matcher.rb +0 -55
  78. data/test/unit/peddler/test_xml_parser.rb +0 -37
  79. data/test/unit/peddler/test_xml_response_parser.rb +0 -39
  80. data/test/unit/test_mws.rb +0 -11
  81. data/test/vcr_cassettes/CartInformation.yml +0 -433
  82. data/test/vcr_cassettes/CustomerInformation.yml +0 -433
  83. data/test/vcr_cassettes/Errors.yml +0 -444
  84. data/test/vcr_cassettes/Feeds.yml +0 -9957
  85. data/test/vcr_cassettes/FulfillmentInboundShipment.yml +0 -6166
  86. data/test/vcr_cassettes/FulfillmentInventory.yml +0 -850
  87. data/test/vcr_cassettes/FulfillmentOutboundShipment.yml +0 -433
  88. data/test/vcr_cassettes/MWSHeaders.yml +0 -2964
  89. data/test/vcr_cassettes/MerchantFulfillment.yml +0 -753
  90. data/test/vcr_cassettes/MultibyteQueries.yml +0 -437
  91. data/test/vcr_cassettes/OffAmazonPayments.yml +0 -433
  92. data/test/vcr_cassettes/Orders.yml +0 -4740
  93. data/test/vcr_cassettes/PeddlerVCRMatcher.yml +0 -41
  94. data/test/vcr_cassettes/Products.yml +0 -7139
  95. data/test/vcr_cassettes/Recommendations.yml +0 -3145
  96. data/test/vcr_cassettes/Reports.yml +0 -5549
  97. data/test/vcr_cassettes/Sellers.yml +0 -433
  98. data/test/vcr_cassettes/Subscriptions.yml +0 -1529
@@ -139,7 +139,7 @@ module MWS
139
139
  marketplace_id)
140
140
  operation('UpdateSubscription')
141
141
  .add('MarketplaceId' => marketplace_id)
142
- .add(build_subscription(notification_type, sqs_queue_url, enabled))
142
+ .add(build_subscription(notification_type, sqs_queue_url, enabled: enabled))
143
143
 
144
144
  run
145
145
  end
@@ -163,7 +163,7 @@ module MWS
163
163
  }
164
164
  end
165
165
 
166
- def build_subscription(notification_type, sqs_queue_url, enabled = true)
166
+ def build_subscription(notification_type, sqs_queue_url, enabled: true)
167
167
  {
168
168
  'Subscription.IsEnabled' => enabled,
169
169
  'Subscription.NotificationType' => notification_type,
@@ -16,12 +16,13 @@ module Peddler
16
16
  include Jeff
17
17
 
18
18
  class << self
19
- # @api private
19
+ # @!visibility private
20
20
  attr_accessor :parser, :path, :version
21
21
 
22
22
  private
23
23
 
24
24
  def inherited(base)
25
+ super
25
26
  base.parser = parser
26
27
  base.params params
27
28
  end
@@ -52,6 +53,7 @@ module Peddler
52
53
  attr_accessor :auth_token
53
54
 
54
55
  # The seller's Merchant ID
56
+ # @note Amazon also refers to this as Seller ID or Merchant Token
55
57
  # @return [String]
56
58
  attr_accessor :merchant_id
57
59
 
@@ -80,38 +82,38 @@ module Peddler
80
82
  str ? add_content(str) : clear_content!
81
83
  end
82
84
 
83
- # @api private
85
+ # @!visibility private
84
86
  attr_writer :path
85
87
 
86
- # @api private
88
+ # @!visibility private
87
89
  def path
88
90
  @path ||= self.class.path
89
91
  end
90
92
 
91
- # @api private
93
+ # @!visibility private
92
94
  def defaults
93
95
  @defaults ||= { expects: 200 }
94
96
  end
95
97
 
96
- # @api private
98
+ # @!visibility private
97
99
  def headers
98
100
  @headers ||= {}
99
101
  end
100
102
 
101
- # @api private
103
+ # @!visibility private
102
104
  def aws_endpoint
103
105
  "https://#{host}#{path}"
104
106
  end
105
107
 
106
- # @api private
108
+ # @!visibility private
107
109
  def operation(action = nil)
108
110
  action ? @operation = Operation.new(action) : @operation
109
111
  end
110
112
 
111
- # @api private
112
- def run
113
+ # @!visibility private
114
+ def run(&block)
113
115
  opts = build_options
114
- opts.store(:response_block, Proc.new) if block_given?
116
+ opts.store(:response_block, block) if block_given?
115
117
  res = post(opts)
116
118
  self.body = nil if res.status == 200
117
119
 
@@ -128,13 +130,15 @@ module Peddler
128
130
  end
129
131
 
130
132
  def add_content(content)
131
- if content.start_with?('<?xml')
133
+ @body = content
134
+ if content.encoding.names.include?('BINARY')
135
+ headers['Content-Type'] = 'application/octet-stream'
136
+ elsif content.start_with?('<?xml')
132
137
  headers['Content-Type'] = 'text/xml'
133
- @body = content
134
138
  else
135
139
  headers['Content-Type'] =
136
140
  "text/tab-separated-values; charset=#{encoding}"
137
- @body = content.encode(encoding)
141
+ @body = @body.encode(encoding)
138
142
  end
139
143
  end
140
144
 
@@ -7,7 +7,7 @@ require 'peddler/errors/parser'
7
7
 
8
8
  module Peddler
9
9
  module Errors
10
- # @api private
10
+ # @!visibility private
11
11
  class Builder
12
12
  extend Forwardable
13
13
 
@@ -28,6 +28,7 @@ module Peddler
28
28
 
29
29
  def build
30
30
  parse_original_response
31
+ return if no_error_response?
31
32
  return if bad_class_name?
32
33
 
33
34
  error_class.new(error_message, error)
@@ -36,7 +37,11 @@ module Peddler
36
37
  private
37
38
 
38
39
  def bad_class_name?
39
- error_name =~ DIGIT_AS_FIRST_CHAR
40
+ error_name.match?(DIGIT_AS_FIRST_CHAR)
41
+ end
42
+
43
+ def no_error_response?
44
+ response.parse.nil?
40
45
  end
41
46
 
42
47
  def error_class
@@ -5,7 +5,7 @@ require 'peddler/errors/error'
5
5
 
6
6
  module Peddler
7
7
  module Errors
8
- # @api private
8
+ # @!visibility private
9
9
  class ClassGenerator
10
10
  include Singleton
11
11
 
@@ -19,7 +19,12 @@ module Peddler
19
19
 
20
20
  def generate(name)
21
21
  with_mutex do
22
- return Errors.const_get(name) if Errors.const_defined?(name)
22
+ if Errors.const_defined?(name)
23
+ error = Errors.const_get(name)
24
+ return error if error <= Error
25
+
26
+ raise TypeError, "#{name} must be a Peddler::Errors::Error"
27
+ end
23
28
 
24
29
  Errors.const_set(name, Class.new(Error))
25
30
  end
@@ -27,8 +32,8 @@ module Peddler
27
32
 
28
33
  private
29
34
 
30
- def with_mutex
31
- @mutex.synchronize { yield }
35
+ def with_mutex(&block)
36
+ @mutex.synchronize(&block)
32
37
  end
33
38
  end
34
39
  end
@@ -3,11 +3,15 @@
3
3
  require 'forwardable'
4
4
 
5
5
  module Peddler
6
- # @api private
6
+ # @!visibility private
7
7
  module Errors
8
8
  # These error codes are common to all Amazon MWS API sections.
9
9
  #
10
10
  # @see https://docs.developer.amazonservices.com/en_US/dev_guide/DG_Errors.html
11
+ #
12
+ # There are quite a few other error types they do not explicitly document
13
+ # above. I decided not to define any for the sake of consistency and future
14
+ # maintenance.
11
15
  CODES = %w[
12
16
  AccessDenied
13
17
  InputStreamDisconnected
@@ -20,7 +24,7 @@ module Peddler
20
24
  SignatureDoesNotMatch
21
25
  ].freeze
22
26
 
23
- # @api private
27
+ # @!visibility private
24
28
  class Error < StandardError
25
29
  extend Forwardable
26
30
 
@@ -4,7 +4,7 @@ require 'peddler/xml_parser'
4
4
 
5
5
  module Peddler
6
6
  module Errors
7
- # @api private
7
+ # @!visibility private
8
8
  class Parser < XMLParser
9
9
  def message
10
10
  parse['Message']
@@ -6,7 +6,7 @@ require 'digest/md5'
6
6
  require 'peddler/headers'
7
7
 
8
8
  module Peddler
9
- # @api private
9
+ # @!visibility private
10
10
  class FlatFileParser < SimpleDelegator
11
11
  include Headers
12
12
 
@@ -23,7 +23,7 @@ module Peddler
23
23
  end
24
24
 
25
25
  def parse(&blk)
26
- CSV.parse(content, OPTIONS, &blk) unless content.empty?
26
+ CSV.parse(content, **OPTIONS, &blk) unless content.empty?
27
27
  end
28
28
 
29
29
  def records_count
@@ -44,7 +44,9 @@ module Peddler
44
44
 
45
45
  def extract_content_and_summary
46
46
  @content = body.encode('UTF-8', invalid: :replace, undef: :replace)
47
- @summary, @content = @content.split("\n\n", 2) if @content =~ /\t\t.*\n\n/
47
+ return unless @content.match?(/\t\t.*\n\n/)
48
+
49
+ @summary, @content = @content.split("\n\n", 2)
48
50
  end
49
51
 
50
52
  def summarize
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'time'
4
+
3
5
  module Peddler
4
6
  # Parses useful metadata returned in response headers
5
7
  module Headers
@@ -47,7 +49,10 @@ module Peddler
47
49
  match_data = headers['Content-Type']&.match(/charset=(.*);?/)
48
50
  return unless match_data
49
51
 
50
- Encoding.find(match_data[1])
52
+ string = match_data[1].upcase
53
+ string = 'UTF-8' if string == 'UTF8'
54
+
55
+ Encoding.find(string)
51
56
  end
52
57
 
53
58
  # The max hourly request quota for the requested operation
@@ -1,29 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Peddler
4
- # @api private
4
+ # @!visibility private
5
5
  # @see https://docs.developer.amazonservices.com/en_US/dev_guide/DG_Endpoints.html
6
- Marketplace = Struct.new(:id, :country_code, :host) do
6
+ class Marketplace
7
7
  class << self
8
8
  attr_reader :all
9
9
 
10
10
  def find(key)
11
- marketplace = if key.nil?
12
- missing_key!
13
- elsif key.size == 2
14
- find_by_country_code(key)
15
- else
16
- find_by_id(key)
17
- end
11
+ missing_key! unless key
12
+ marketplace = key.size == 2 ? find_by_country(key) : find_by_id(key)
18
13
 
19
14
  marketplace || not_found!(key)
20
15
  end
21
16
 
22
17
  private
23
18
 
24
- def find_by_country_code(country_code)
25
- country_code = 'GB' if country_code == 'UK'
26
- all.find { |marketplace| marketplace.country_code == country_code }
19
+ def find_by_country(code)
20
+ code = 'GB' if code == 'UK'
21
+ all.find { |marketplace| marketplace.country_code == code }
27
22
  end
28
23
 
29
24
  def find_by_id(id)
@@ -39,32 +34,43 @@ module Peddler
39
34
  end
40
35
  end
41
36
 
37
+ attr_reader :id, :country_code, :host
38
+
39
+ def initialize(id, country_code, host)
40
+ @id = id
41
+ @country_code = country_code
42
+ @host = host
43
+ end
44
+
42
45
  def encoding
43
46
  case country_code
44
47
  when 'JP'
45
48
  'Windows-31J'
46
- when 'CN'
47
- 'UTF-16'
48
49
  else
49
50
  'CP1252'
50
51
  end
51
52
  end
52
53
 
53
54
  @all = [
55
+ ['A2Q3Y263D00KWC', 'BR', 'mws.amazonservices.com'],
54
56
  ['A2EUQ1WTGCTBG2', 'CA', 'mws.amazonservices.com'],
55
57
  ['A1AM78C64UM0Y8', 'MX', 'mws.amazonservices.com'],
56
58
  ['ATVPDKIKX0DER', 'US', 'mws.amazonservices.com'],
57
- ['A2Q3Y263D00KWC', 'BR', 'mws.amazonservices.com'],
59
+ ['A2VIGQ35RCS4UG', 'AE', 'mws.amazonservices.ae'],
58
60
  ['A1PA6795UKMFR9', 'DE', 'mws-eu.amazonservices.com'],
61
+ ['ARBP9OOSHTCHU', 'EG', 'mws-eu.amazonservices.com'],
59
62
  ['A1RKKUPIHCS9HS', 'ES', 'mws-eu.amazonservices.com'],
60
63
  ['A13V1IB3VIYZZH', 'FR', 'mws-eu.amazonservices.com'],
61
- ['APJ6JRA9NG5V4', 'IT', 'mws-eu.amazonservices.com'],
62
64
  ['A1F83G8C2ARO7P', 'GB', 'mws-eu.amazonservices.com'],
63
- ['A33AVAJ2PDY3EV', 'TR', 'mws-eu.amazonservices.com'],
64
65
  ['A21TJRUUN4KGV', 'IN', 'mws.amazonservices.in'],
66
+ ['APJ6JRA9NG5V4', 'IT', 'mws-eu.amazonservices.com'],
67
+ ['A1805IZSGTT6HS', 'NL', 'mws-eu.amazonservices.com'],
68
+ ['A17E79C6D8DWNP', 'SA', 'mws-eu.amazonservices.com'],
69
+ ['A33AVAJ2PDY3EV', 'TR', 'mws-eu.amazonservices.com'],
70
+ ['A2NODRKZP88ZB9', 'SE', 'mws-eu.amazonservices.com'],
71
+ ['A19VAU5U5O7RUS', 'SG', 'mws-fe.amazonservices.com'],
65
72
  ['A39IBJ37TRP1C6', 'AU', 'mws.amazonservices.com.au'],
66
- ['A1VC38T7YXB528', 'JP', 'mws.amazonservices.jp'],
67
- ['AAHKV2X7AFYLW', 'CN', 'mws.amazonservices.com.cn']
73
+ ['A1VC38T7YXB528', 'JP', 'mws.amazonservices.jp']
68
74
  ].map do |values|
69
75
  new(*values)
70
76
  end
@@ -5,7 +5,7 @@ require 'time'
5
5
  require 'peddler/structured_list'
6
6
 
7
7
  module Peddler
8
- # @api private
8
+ # @!visibility private
9
9
  class Operation < SimpleDelegator
10
10
  CAPITAL_LETTERS = /[A-Z]/.freeze
11
11
  ALL_CAPS = %w[sku cod].freeze
@@ -28,15 +28,12 @@ module Peddler
28
28
  self
29
29
  end
30
30
 
31
- def store(key, val, parent: '')
32
- key = camelize(key) if key.is_a?(Symbol)
33
- key = "#{parent}.#{key}" unless parent.empty?
34
-
35
- val = val.iso8601 if val.respond_to?(:iso8601)
36
- val = val.to_h if val.is_a?(Struct)
31
+ def store(key, val, parent = nil)
32
+ key = [parent, camelize(key)].compact.join('.')
33
+ val = format_known_types(val)
37
34
 
38
35
  if val.is_a?(Hash)
39
- val.each { |keyval| store(*keyval, parent: key) }
36
+ val.each { |keyval| store(*keyval, key) }
40
37
  else
41
38
  __getobj__.store(key, val)
42
39
  end
@@ -51,10 +48,11 @@ module Peddler
51
48
 
52
49
  private
53
50
 
54
- def camelize(sym)
55
- return sym.to_s if sym =~ CAPITAL_LETTERS
51
+ def camelize(key)
52
+ return key unless key.is_a?(Symbol)
53
+ return key.to_s if key.match?(CAPITAL_LETTERS)
56
54
 
57
- sym
55
+ key
58
56
  .to_s
59
57
  .split('_')
60
58
  .map { |token| capitalize(token) }
@@ -68,5 +66,13 @@ module Peddler
68
66
  word.capitalize
69
67
  end
70
68
  end
69
+
70
+ def format_known_types(val)
71
+ val = val.utc.iso8601(2) if val.is_a?(Time)
72
+ val = val.iso8601 if val.is_a?(Date)
73
+ val = val.to_h if val.is_a?(Struct)
74
+
75
+ val
76
+ end
71
77
  end
72
78
  end
@@ -4,7 +4,7 @@ require 'peddler/flat_file_parser'
4
4
  require 'peddler/xml_response_parser'
5
5
 
6
6
  module Peddler
7
- # @api private
7
+ # @!visibility private
8
8
  module Parser
9
9
  class << self
10
10
  # We're massaging data produced by a motley army of developers. It's
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Peddler
4
- # @api private
4
+ # @!visibility private
5
5
  class StructuredList
6
6
  def initialize(*keys)
7
7
  @keys = keys
@@ -4,23 +4,23 @@ module Peddler
4
4
  # A custom matcher that can be used to record MWS interactions when
5
5
  # writing integration tests
6
6
  class VCRMatcher
7
- # @api private
7
+ # @!visibility private
8
8
  TRANSIENT_PARAMS = %w[
9
9
  Signature Timestamp StartDate CreatedAfter QueryStartDateTime
10
10
  ].freeze
11
11
 
12
- # @api private
12
+ # @!visibility private
13
13
  SELLER_PARAMS = %w[
14
14
  AWSAccessKeyId SellerId
15
15
  ].freeze
16
16
 
17
17
  class << self
18
- # @api private
18
+ # @!visibility private
19
19
  def call(*requests)
20
20
  new(*requests).compare
21
21
  end
22
22
 
23
- # @api private
23
+ # @!visibility private
24
24
  def ignored_params
25
25
  @ignored_params ||= TRANSIENT_PARAMS.dup
26
26
  end
@@ -33,15 +33,15 @@ module Peddler
33
33
  end
34
34
  end
35
35
 
36
- # @api private
36
+ # @!visibility private
37
37
  attr_reader :requests
38
38
 
39
- # @api private
39
+ # @!visibility private
40
40
  def initialize(*requests)
41
41
  @requests = requests
42
42
  end
43
43
 
44
- # @api private
44
+ # @!visibility private
45
45
  def compare
46
46
  compare_uris && compare_bodies
47
47
  end