peddler 1.6.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -67
  3. data/lib/mws/feeds/client.rb +15 -12
  4. data/lib/mws/finances/client.rb +12 -11
  5. data/lib/mws/fulfillment_inbound_shipment/client.rb +79 -107
  6. data/lib/mws/fulfillment_inventory/client.rb +5 -5
  7. data/lib/mws/fulfillment_outbound_shipment/client.rb +36 -44
  8. data/lib/mws/merchant_fulfillment/client.rb +11 -17
  9. data/lib/mws/off_amazon_payments/client.rb +38 -68
  10. data/lib/mws/orders/client.rb +28 -24
  11. data/lib/mws/products/client.rb +118 -153
  12. data/lib/mws/recommendations/client.rb +13 -17
  13. data/lib/mws/reports/client.rb +24 -23
  14. data/lib/mws/sellers/client.rb +5 -5
  15. data/lib/mws/subscriptions/client.rb +25 -36
  16. data/lib/peddler/client.rb +47 -124
  17. data/lib/peddler/errors/builder.rb +40 -14
  18. data/lib/peddler/errors/class_generator.rb +34 -0
  19. data/lib/peddler/errors/error.rb +13 -3
  20. data/lib/peddler/headers.rb +27 -11
  21. data/lib/peddler/marketplace.rb +30 -11
  22. data/lib/peddler/vcr_matcher.rb +11 -1
  23. data/lib/peddler/version.rb +1 -1
  24. data/lib/peddler/xml_parser.rb +4 -2
  25. data/lib/peddler/xml_response_parser.rb +1 -1
  26. data/test/helper.rb +0 -1
  27. data/test/integration/test_errors.rb +2 -14
  28. data/test/integration/test_feeds.rb +0 -3
  29. data/test/integration/test_multibyte_queries.rb +1 -1
  30. data/test/integration/test_mws_headers.rb +3 -2
  31. data/test/integration/test_orders.rb +2 -1
  32. data/test/integration/test_products.rb +9 -9
  33. data/test/integration/test_recommendations.rb +1 -1
  34. data/test/integration/test_subscriptions.rb +2 -2
  35. data/test/integration_helper.rb +1 -1
  36. data/test/mws.yml +36 -0
  37. data/test/mws.yml.example +8 -12
  38. data/test/null_client.rb +10 -8
  39. data/test/unit/mws/test_feeds_client.rb +1 -2
  40. data/test/unit/mws/test_fulfillment_outbound_shipment_client.rb +1 -1
  41. data/test/unit/mws/test_off_amazon_payments_client.rb +1 -1
  42. data/test/unit/mws/test_orders_client.rb +7 -6
  43. data/test/unit/mws/test_products_client.rb +13 -28
  44. data/test/unit/mws/test_recommendations_client.rb +1 -2
  45. data/test/unit/mws/test_reports_client.rb +1 -1
  46. data/test/unit/mws/test_subscriptions_client.rb +1 -130
  47. data/test/unit/peddler/errors/test_builder.rb +54 -7
  48. data/test/unit/peddler/errors/test_class_generator.rb +18 -0
  49. data/test/unit/peddler/errors/test_error.rb +7 -2
  50. data/test/unit/peddler/test_client.rb +136 -190
  51. data/test/unit/peddler/test_flat_file_parser.rb +2 -2
  52. data/test/unit/peddler/test_headers.rb +19 -9
  53. data/test/unit/peddler/test_marketplace.rb +18 -5
  54. data/test/unit/peddler/test_vcr_matcher.rb +3 -1
  55. data/test/vcr_cassettes/Feeds.yml +4816 -5224
  56. data/test/vcr_cassettes/Reports.yml +3278 -2604
  57. metadata +8 -9
  58. data/lib/peddler/errors.rb +0 -12
  59. data/lib/peddler/errors/handler.rb +0 -59
  60. data/test/unit/peddler/errors/test_handler.rb +0 -62
  61. data/test/unit/peddler/test_errors.rb +0 -26
@@ -4,15 +4,62 @@ require 'helper'
4
4
  require 'peddler/errors/builder'
5
5
 
6
6
  class TestPeddlerErrorsBuilder < MiniTest::Test
7
- def test_builds_error_class
8
- Peddler::Errors::Builder.build('Foo')
9
- assert Peddler::Errors::Foo
7
+ def setup
8
+ @error = Peddler::Errors::Builder.call(@cause)
10
9
  end
11
10
 
12
- def test_thread_safety
13
- Peddler::Errors::Builder.build('Foo')
14
- assert_output '', '' do
15
- Peddler::Errors::Builder.build('Foo')
11
+ class CausedByHTTPStatusError < TestPeddlerErrorsBuilder
12
+ def setup
13
+ @code = 'FeedProcessingResultNotReady'
14
+ @message = 'Feed Submission Result is not ready for Feed 123'
15
+ body = <<-XML
16
+ <ErrorResponse>
17
+ <Error>
18
+ <Code>#{@code}</Code>
19
+ <Message>#{@message}</Message>
20
+ </Error>
21
+ </ErrorResponse>
22
+ XML
23
+ @cause = Excon::Error::NotFound.new(
24
+ 'Expected(200) <=> Actual(404 Not Found)',
25
+ nil,
26
+ OpenStruct.new(body: body)
27
+ )
28
+ super
29
+ end
30
+
31
+ def test_generates_custom_error
32
+ assert_includes @error.class.name, @code
33
+ end
34
+
35
+ def test_provides_message
36
+ assert_equal @message, @error.message
37
+ end
38
+
39
+ def test_provides_cause
40
+ assert_equal @cause, @error.cause
41
+ end
42
+ end
43
+
44
+ class CausedByInternalServerError < TestPeddlerErrorsBuilder
45
+ def setup
46
+ body = <<-XML
47
+ <ErrorResponse>
48
+ <Error>
49
+ <Code>500</Code>
50
+ </Error>
51
+ </ErrorResponse>
52
+ XML
53
+ @cause = Excon::Error::InternalServerError.new(
54
+ nil,
55
+ nil,
56
+ OpenStruct.new(body: body)
57
+ )
58
+ super
59
+ end
60
+
61
+ def test_returns_nothing
62
+ assert_nil @error
16
63
  end
17
64
  end
18
65
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+ require 'peddler/errors/class_generator'
5
+
6
+ class TestPeddlerErrorsClassGenerator < MiniTest::Test
7
+ def test_builds_error_class
8
+ Peddler::Errors::ClassGenerator.call('Foo')
9
+ assert Peddler::Errors::Foo
10
+ end
11
+
12
+ def test_thread_safety
13
+ Peddler::Errors::ClassGenerator.call('Foo')
14
+ assert_output '', '' do
15
+ Peddler::Errors::ClassGenerator.call('Foo')
16
+ end
17
+ end
18
+ end
@@ -5,7 +5,8 @@ require 'peddler/errors/error'
5
5
 
6
6
  class TestPeddlerErrorsError < MiniTest::Test
7
7
  def setup
8
- @error = Peddler::Errors::Error.new('message', 'cause')
8
+ @cause = OpenStruct.new(response: 'response')
9
+ @error = Peddler::Errors::Error.new('message', @cause)
9
10
  end
10
11
 
11
12
  def test_sets_message
@@ -13,7 +14,7 @@ class TestPeddlerErrorsError < MiniTest::Test
13
14
  end
14
15
 
15
16
  def test_sets_cause
16
- assert_equal 'cause', @error.cause
17
+ assert_equal @cause, @error.cause
17
18
  end
18
19
 
19
20
  def test_defines_common_errors
@@ -25,4 +26,8 @@ class TestPeddlerErrorsError < MiniTest::Test
25
26
  def test_allows_nil_arguments
26
27
  Peddler::Errors::Error.new
27
28
  end
29
+
30
+ def test_delegates_response_to_cause
31
+ assert_equal @cause.response, @error.response
32
+ end
28
33
  end
@@ -5,10 +5,7 @@ require 'null_client'
5
5
 
6
6
  class TestPeddlerClient < MiniTest::Test
7
7
  def setup
8
- @response_body = 'foo'
9
8
  Excon.defaults[:mock] = true
10
- Excon.stub({}, body: @response_body, status: 200)
11
-
12
9
  @klass = Class.new(Null::Client)
13
10
  @client = @klass.new
14
11
  @client.configure_with_mock_data!
@@ -20,238 +17,187 @@ class TestPeddlerClient < MiniTest::Test
20
17
  Excon.defaults.delete(:mock)
21
18
  end
22
19
 
23
- def test_configures_path
24
- @klass.path('/Foo')
25
- assert @client.aws_endpoint.match(%r{/Foo$})
26
- end
27
-
28
- def test_instance_path_overrides_class_path
29
- @klass.path('/Foo')
30
-
31
- @client.path = '/Foo/Bar'
32
- assert @client.aws_endpoint.match(%r{/Foo/Bar$})
33
- end
34
-
35
- def test_default_path
36
- assert_equal '/', @klass.path
37
- end
38
-
39
- def test_has_user_agent
40
- assert @client.connection.data[:headers].key?('User-Agent')
41
- end
42
-
43
- def test_inherits_parents_params
44
- assert_equal Peddler::Client.params, @klass.params
45
- end
46
-
47
- def test_inherits_parents_path
48
- assert_equal @klass.path, Class.new(@klass).path
49
- end
20
+ class HappyPath < TestPeddlerClient
21
+ def setup
22
+ @response_body = 'foo'
23
+ Excon.stub({}, body: @response_body, status: 200)
24
+ super
25
+ end
50
26
 
51
- def test_inherits_parents_parser
52
- assert_equal @klass.parser, Class.new(@klass).parser
53
- end
27
+ def test_has_user_agent
28
+ assert @client.connection.data[:headers].key?('User-Agent')
29
+ end
54
30
 
55
- def test_params_include_seller_id
56
- assert @klass.params.key?('SellerId')
57
- end
31
+ def test_inherits_parents_params
32
+ assert_equal Peddler::Client.params, @klass.params
33
+ end
58
34
 
59
- def test_params_include_auth_token
60
- @klass.params.key?('MWSAuthToken')
61
- end
35
+ def test_params_include_seller_id
36
+ assert @klass.params.key?('SellerId')
37
+ end
62
38
 
63
- def test_configures
64
- @client.configure do |config|
65
- config.aws_access_key_id = '123'
39
+ def test_params_include_auth_token
40
+ @klass.params.key?('MWSAuthToken')
66
41
  end
67
42
 
68
- assert_equal '123', @client.aws_access_key_id
69
- end
43
+ def test_inherits_parents_parser
44
+ assert_equal @klass.parser, Class.new(@klass).parser
45
+ end
70
46
 
71
- def test_configures_when_initialising
72
- client = @klass.new(aws_access_key_id: '123')
73
- assert_equal '123', client.aws_access_key_id
74
- end
47
+ def test_sets_marketplace
48
+ marketplace = Peddler::Marketplace.find('US')
49
+ @client.marketplace = 'US'
50
+ assert_equal marketplace, @client.marketplace
51
+ @client.marketplace = marketplace
52
+ assert_equal marketplace, @client.marketplace
53
+ end
75
54
 
76
- def test_sets_content_type_header_for_latin_flat_file
77
- @client.body = 'foo'
78
- content_type = @client.headers.fetch('Content-Type')
55
+ def test_configures_when_initialising
56
+ client = @klass.new(aws_access_key_id: '123')
57
+ assert_equal '123', client.aws_access_key_id
58
+ end
79
59
 
80
- assert_equal 'text/tab-separated-values; charset=CP1252', content_type
81
- end
60
+ def test_sets_content_type_header_for_latin_flat_file
61
+ @client.body = 'foo'
62
+ content_type = @client.headers.fetch('Content-Type')
82
63
 
83
- def test_sets_content_type_header_for_chinese_flat_file
84
- @client.primary_marketplace_id = 'AAHKV2X7AFYLW'
85
- @client.body = 'foo'
86
- content_type = @client.headers.fetch('Content-Type')
64
+ assert_equal 'text/tab-separated-values; charset=CP1252', content_type
65
+ end
87
66
 
88
- assert_equal 'text/tab-separated-values; charset=UTF-16', content_type
89
- end
67
+ def test_sets_content_type_header_for_chinese_flat_file
68
+ @client.marketplace = 'CN'
69
+ @client.body = 'foo'
70
+ content_type = @client.headers.fetch('Content-Type')
90
71
 
91
- def test_sets_content_type_header_for_japanese_flat_file
92
- @client.primary_marketplace_id = 'A1VC38T7YXB528'
93
- @client.body = 'foo'
94
- content_type = @client.headers.fetch('Content-Type')
72
+ assert_equal 'text/tab-separated-values; charset=UTF-16', content_type
73
+ end
95
74
 
96
- assert_equal 'text/tab-separated-values; charset=Windows-31J', content_type
97
- end
75
+ def test_sets_content_type_header_for_japanese_flat_file
76
+ @client.marketplace = 'JP'
77
+ @client.body = 'foo'
78
+ content_type = @client.headers.fetch('Content-Type')
98
79
 
99
- def test_sets_content_type_header_for_xml
100
- @client.body = '<?xml version="1.0"?><Foo></Foo>'
101
- content_type = @client.headers.fetch('Content-Type')
80
+ assert_equal 'text/tab-separated-values; charset=Windows-31J', content_type
81
+ end
102
82
 
103
- assert_equal 'text/xml', content_type
104
- end
83
+ def test_sets_content_type_header_for_xml
84
+ @client.body = '<?xml version="1.0"?><Foo></Foo>'
85
+ content_type = @client.headers.fetch('Content-Type')
105
86
 
106
- def test_encodes_body_for_latin_flat_file
107
- @client.body = 'foo'
108
- assert_equal 'Windows-1252', @client.body.encoding.to_s
109
- end
87
+ assert_equal 'text/xml', content_type
88
+ end
110
89
 
111
- def test_encodes_body_for_chinese_flat_file
112
- @client.primary_marketplace_id = 'AAHKV2X7AFYLW'
113
- @client.body = 'foo'
114
- assert_equal 'UTF-16', @client.body.encoding.to_s
115
- end
90
+ def test_encodes_body_for_latin_flat_file
91
+ @client.body = 'foo'
92
+ assert_equal 'Windows-1252', @client.body.encoding.to_s
93
+ end
116
94
 
117
- def test_encodes_body_for_japanese_flat_file
118
- @client.primary_marketplace_id = 'A1VC38T7YXB528'
119
- @client.body = 'foo'
120
- assert_equal 'Windows-31J', @client.body.encoding.to_s
121
- end
95
+ def test_encodes_body_for_chinese_flat_file
96
+ @client.marketplace = 'CN'
97
+ @client.body = 'foo'
98
+ assert_equal 'UTF-16', @client.body.encoding.to_s
99
+ end
122
100
 
123
- def test_runs_a_request
124
- res = @client.run
125
- assert_equal @response_body, res.body
126
- end
101
+ def test_encodes_body_for_japanese_flat_file
102
+ @client.marketplace = 'JP'
103
+ @client.body = 'foo'
104
+ assert_equal 'Windows-31J', @client.body.encoding.to_s
105
+ end
127
106
 
128
- def test_clears_body_when_run_succeeds
129
- @client.body = 'foo'
130
- @client.run
131
- assert_nil @client.body
132
- end
107
+ def test_runs_a_request
108
+ res = @client.run
109
+ assert_equal @response_body, res.body
110
+ end
133
111
 
134
- def test_does_not_clear_body_when_run_fails
135
- Excon.stub({}, status: 503)
136
- @client.body = 'foo'
137
- assert_raises(Excon::Error::ServiceUnavailable) do
112
+ def test_clears_body_when_run_succeeds
113
+ @client.body = 'foo'
138
114
  @client.run
115
+ assert_nil @client.body
139
116
  end
140
- refute_nil @client.body
141
- end
142
-
143
- def test_streams_response
144
- chunks = ''
145
- streamer = ->(chunk, _, _) { chunks += chunk }
146
- @client.run(&streamer)
147
-
148
- assert_equal @response_body, chunks
149
- end
150
117
 
151
- class Instrumentor
152
- class << self
153
- attr_accessor :events
118
+ def test_streams_response
119
+ chunks = ''
120
+ streamer = ->(chunk, _, _) { chunks += chunk }
121
+ @client.run(&streamer)
154
122
 
155
- def instrument(name, params = {})
156
- events.update(name => params)
157
- yield if block_given?
158
- end
123
+ assert_equal @response_body, chunks
159
124
  end
160
125
 
161
- @events = {}
162
- end
126
+ class Instrumentor
127
+ class << self
128
+ attr_accessor :events
163
129
 
164
- def test_request_preserves_user_agent
165
- @client.defaults.update(instrumentor: Instrumentor)
166
- @client.run
167
- headers = Instrumentor.events['excon.request'][:headers]
168
-
169
- assert headers.key?('User-Agent')
170
- end
130
+ def instrument(name, params = {})
131
+ events.update(name => params)
132
+ yield if block_given?
133
+ end
134
+ end
171
135
 
172
- def test_error_callback_on_class
173
- Excon.stub({}, status: 503)
136
+ @events = {}
137
+ end
174
138
 
175
- assert_raises(Excon::Error::ServiceUnavailable) do
139
+ def test_request_preserves_user_agent
140
+ @client.defaults.update(instrumentor: Instrumentor)
176
141
  @client.run
177
- end
142
+ headers = Instrumentor.events['excon.request'][:headers]
178
143
 
179
- @klass.on_error do |e|
180
- assert_equal 503, e.response.status
144
+ assert headers.key?('User-Agent')
181
145
  end
182
- @client.run # no longer raises
183
-
184
- Excon.stubs.clear
185
146
  end
186
147
 
187
- def test_error_callback_on_instance
188
- Excon.stub({}, status: 503)
189
-
190
- assert_raises(Excon::Error::ServiceUnavailable) do
191
- @client.run
148
+ class MWSErrorPath < TestPeddlerClient
149
+ def setup
150
+ body = <<-XML
151
+ <ErrorResponse>
152
+ <Error>
153
+ <Code>RequestThrottled</Code>
154
+ </Error>
155
+ </ErrorResponse>
156
+ XML
157
+ Excon.stub({}, body: body, status: 503)
158
+ super
192
159
  end
193
160
 
194
- @client.on_error do |e|
195
- assert_equal 503, e.response.status
161
+ def test_default_error_handling
162
+ assert_raises Peddler::Errors::RequestThrottled do
163
+ @client.run
164
+ end
196
165
  end
197
- @client.run
198
166
 
199
- Excon.stubs.clear
200
- end
201
-
202
- def test_error_callback_on_client_ancestor
203
- Excon.stub({}, status: 503)
204
-
205
- @klass.on_error do |e|
206
- assert_equal 503, e.response.status
207
- end
208
- @client.run # no longer raises
209
-
210
- klass = Class.new(Null::Client)
211
- other_client = klass.new
212
- other_client.configure_with_mock_data!
213
- other_client.operation('Foo')
214
- assert_raises(Excon::Error::ServiceUnavailable) do
215
- other_client.run
167
+ def test_does_not_clear_body_when_run_fails
168
+ @client.body = 'foo'
169
+ assert_raises Peddler::Errors::RequestThrottled do
170
+ @client.run
171
+ end
172
+ refute_nil @client.body
216
173
  end
217
-
218
- Excon.stubs.clear
219
174
  end
220
175
 
221
- def test_decorates_error_response
222
- res = {
223
- body: '<ErrorResponse><Error>Foo</Error></ErrorResponse>',
224
- status: 503
225
- }
226
- Excon.stub({}, res)
227
- e = nil
228
-
229
- begin
230
- @client.run
231
- rescue StandardError => e
232
- assert e.response.parse
176
+ class OtherHTTPStatusErrorPath < TestPeddlerClient
177
+ def setup
178
+ body = <<-XML
179
+ <ErrorResponse>
180
+ <Error>
181
+ <Code>500</Code>
182
+ </Error>
183
+ </ErrorResponse>
184
+ XML
185
+ Excon.stub({}, body: body, status: 500)
186
+ super
233
187
  end
234
188
 
235
- assert e
236
- end
237
-
238
- def test_deprecated_error_callback
239
- Excon.stub({}, status: 503)
240
-
241
- @client.on_error do |_, res|
242
- assert_equal 503, res.status
243
- end
244
- assert_output nil, /DEPRECATION/ do
245
- @client.run
189
+ def test_error_handling
190
+ assert_raises Excon::Error::InternalServerError do
191
+ @client.run
192
+ end
246
193
  end
247
194
 
248
- Excon.stubs.clear
249
- end
250
-
251
- def test_deprecated_marketplace_id_accessor
252
- refute_nil @client.marketplace_id
253
- @client.marketplace_id = '123'
254
- assert_equal '123', @client.marketplace_id
255
- assert_equal @client.primary_marketplace_id, @client.marketplace_id
195
+ def test_does_not_clear_body_when_run_fails
196
+ @client.body = 'foo'
197
+ assert_raises Excon::Error::InternalServerError do
198
+ @client.run
199
+ end
200
+ refute_nil @client.body
201
+ end
256
202
  end
257
203
  end