mws-connect 0.0.1
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 +19 -0
- data/.sublime-project +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +111 -0
- data/Rakefile +1 -0
- data/lib/mws.rb +34 -0
- data/lib/mws/apis.rb +6 -0
- data/lib/mws/apis/feeds.rb +20 -0
- data/lib/mws/apis/feeds/api.rb +103 -0
- data/lib/mws/apis/feeds/distance.rb +23 -0
- data/lib/mws/apis/feeds/feed.rb +114 -0
- data/lib/mws/apis/feeds/image_listing.rb +44 -0
- data/lib/mws/apis/feeds/inventory.rb +77 -0
- data/lib/mws/apis/feeds/measurement.rb +32 -0
- data/lib/mws/apis/feeds/money.rb +31 -0
- data/lib/mws/apis/feeds/price_listing.rb +48 -0
- data/lib/mws/apis/feeds/product.rb +173 -0
- data/lib/mws/apis/feeds/sale_price.rb +31 -0
- data/lib/mws/apis/feeds/shipping.rb +160 -0
- data/lib/mws/apis/feeds/submission_info.rb +45 -0
- data/lib/mws/apis/feeds/submission_result.rb +87 -0
- data/lib/mws/apis/feeds/transaction.rb +37 -0
- data/lib/mws/apis/feeds/weight.rb +19 -0
- data/lib/mws/apis/orders.rb +23 -0
- data/lib/mws/connection.rb +84 -0
- data/lib/mws/enum.rb +81 -0
- data/lib/mws/errors.rb +32 -0
- data/lib/mws/query.rb +45 -0
- data/lib/mws/serializer.rb +81 -0
- data/lib/mws/signer.rb +20 -0
- data/lib/mws/utils.rb +50 -0
- data/mws.gemspec +25 -0
- data/scripts/catalog-workflow +136 -0
- data/spec/mws/apis/feeds/api_spec.rb +229 -0
- data/spec/mws/apis/feeds/distance_spec.rb +43 -0
- data/spec/mws/apis/feeds/feed_spec.rb +92 -0
- data/spec/mws/apis/feeds/image_listing_spec.rb +109 -0
- data/spec/mws/apis/feeds/inventory_spec.rb +135 -0
- data/spec/mws/apis/feeds/measurement_spec.rb +84 -0
- data/spec/mws/apis/feeds/money_spec.rb +43 -0
- data/spec/mws/apis/feeds/price_listing_spec.rb +90 -0
- data/spec/mws/apis/feeds/product_spec.rb +264 -0
- data/spec/mws/apis/feeds/shipping_spec.rb +78 -0
- data/spec/mws/apis/feeds/submission_info_spec.rb +111 -0
- data/spec/mws/apis/feeds/submission_result_spec.rb +157 -0
- data/spec/mws/apis/feeds/transaction_spec.rb +64 -0
- data/spec/mws/apis/feeds/weight_spec.rb +43 -0
- data/spec/mws/apis/orders_spec.rb +9 -0
- data/spec/mws/connection_spec.rb +331 -0
- data/spec/mws/enum_spec.rb +166 -0
- data/spec/mws/query_spec.rb +104 -0
- data/spec/mws/serializer_spec.rb +187 -0
- data/spec/mws/signer_spec.rb +67 -0
- data/spec/mws/utils_spec.rb +147 -0
- data/spec/spec_helper.rb +10 -0
- metadata +220 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
describe Distance do
|
6
|
+
|
7
|
+
context '.new' do
|
8
|
+
|
9
|
+
it 'should default to feet' do
|
10
|
+
distance = Distance.new 40
|
11
|
+
distance.amount.should == 40
|
12
|
+
distance.unit.should == :feet
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should accept a valid unit override' do
|
16
|
+
distance = Distance.new 0, :meters
|
17
|
+
distance.amount.should == 0
|
18
|
+
distance.unit.should == :meters
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should validate the unit override' do
|
22
|
+
expect {
|
23
|
+
Distance.new 40, :acres
|
24
|
+
}.to raise_error Mws::Errors::ValidationError, "Invalid unit of measure 'acres'"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#to_xml' do
|
30
|
+
|
31
|
+
it 'should properly serialize to XML' do
|
32
|
+
distance = Distance.new 25, :inches
|
33
|
+
expected = Nokogiri::XML::Builder.new do
|
34
|
+
Distance 25, unitOfMeasure: 'inches'
|
35
|
+
end.doc.root.to_xml
|
36
|
+
distance.to_xml.should == expected
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
describe Feed do
|
6
|
+
|
7
|
+
let(:merchant) { 'GSWCJ4UBA31UTJ' }
|
8
|
+
let(:message_type) { :image }
|
9
|
+
|
10
|
+
context '.new' do
|
11
|
+
|
12
|
+
it 'should require a merchant identifier' do
|
13
|
+
expect { Feed.new(nil, message_type) }.to raise_error Mws::Errors::ValidationError,
|
14
|
+
'Merchant identifier is required.'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should require a valid message type' do
|
18
|
+
expect { Feed.new(merchant, nil) }.to raise_error Mws::Errors::ValidationError,
|
19
|
+
'A valid message type is required.'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'shoud default purge and replace to false' do
|
23
|
+
Feed.new(merchant, message_type).purge_and_replace.should be false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should accept overrides to purge and replace' do
|
27
|
+
Feed.new(merchant, message_type, true).purge_and_replace.should be true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should accept a block to append messages to the feed' do
|
31
|
+
feed = Feed.new(merchant, message_type) do
|
32
|
+
message ImageListing.new('1', 'http://foo.com/bar.jpg'), :delete
|
33
|
+
message ImageListing.new('1', 'http://bar.com/foo.jpg')
|
34
|
+
end
|
35
|
+
feed.messages.size.should == 2
|
36
|
+
first = feed.messages.first
|
37
|
+
first.id.should == 1
|
38
|
+
first.type.should == :image
|
39
|
+
first.operation_type.should == :delete
|
40
|
+
first.resource.should == ImageListing.new('1', 'http://foo.com/bar.jpg')
|
41
|
+
second = feed.messages.last
|
42
|
+
second.id.should == 2
|
43
|
+
second.type.should == :image
|
44
|
+
second.operation_type.should == :update
|
45
|
+
second.resource.should == ImageListing.new('1', 'http://bar.com/foo.jpg')
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context '#to_xml' do
|
51
|
+
|
52
|
+
it 'shoud properly serialize to xml' do
|
53
|
+
expected = Nokogiri::XML::Builder.new {
|
54
|
+
AmazonEnvelope('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'amznenvelope.xsd') {
|
55
|
+
Header {
|
56
|
+
DocumentVersion '1.01'
|
57
|
+
MerchantIdentifier 'GSWCJ4UBA31UTJ'
|
58
|
+
}
|
59
|
+
MessageType 'ProductImage'
|
60
|
+
PurgeAndReplace false
|
61
|
+
Message {
|
62
|
+
MessageID 1
|
63
|
+
OperationType 'Delete'
|
64
|
+
ProductImage {
|
65
|
+
SKU 1
|
66
|
+
ImageType 'Main'
|
67
|
+
ImageLocation 'http://foo.com/bar.jpg'
|
68
|
+
}
|
69
|
+
}
|
70
|
+
Message {
|
71
|
+
MessageID 2
|
72
|
+
OperationType 'Update'
|
73
|
+
ProductImage {
|
74
|
+
SKU 1
|
75
|
+
ImageType 'Main'
|
76
|
+
ImageLocation 'http://bar.com/foo.jpg'
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}.to_xml
|
81
|
+
actual = Feed.new(merchant, message_type) do
|
82
|
+
message ImageListing.new('1', 'http://foo.com/bar.jpg'), :delete
|
83
|
+
message ImageListing.new('1', 'http://bar.com/foo.jpg')
|
84
|
+
end.to_xml
|
85
|
+
actual.should == expected
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
describe ImageListing do
|
6
|
+
|
7
|
+
let(:sku) { '987612345' }
|
8
|
+
let(:url) { 'http://domain.com/images/foo.png' }
|
9
|
+
let(:type) { :alt1 }
|
10
|
+
let(:listing) { ImageListing.new sku, url, type }
|
11
|
+
|
12
|
+
context '.new' do
|
13
|
+
|
14
|
+
it 'should construct an image listing with url and type' do
|
15
|
+
listing.sku.should == sku
|
16
|
+
listing.url.should == url
|
17
|
+
listing.type.should == type
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should default the image listing type to main' do
|
21
|
+
listing = ImageListing.new sku, url
|
22
|
+
listing.sku.should == sku
|
23
|
+
listing.url.should == url
|
24
|
+
listing.type.should == :main
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should require non-nil sku' do
|
28
|
+
expect { ImageListing.new(nil, url) }.to raise_error Mws::Errors::ValidationError,
|
29
|
+
'SKU is required.'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should require a non-empty sku' do
|
33
|
+
expect { ImageListing.new('', url) }.to raise_error Mws::Errors::ValidationError,
|
34
|
+
'SKU is required.'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should require a sku that is not all whitespace' do
|
38
|
+
expect { ImageListing.new(' ', url) }.to raise_error Mws::Errors::ValidationError,
|
39
|
+
'SKU is required.'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should require a non-nil url' do
|
43
|
+
expect { ImageListing.new(sku, nil) }.to raise_error Mws::Errors::ValidationError,
|
44
|
+
'URL must be an unsecured http address.'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should require a valid url' do
|
48
|
+
expect { ImageListing.new(sku, 'this is not a url') }.to raise_error Mws::Errors::ValidationError,
|
49
|
+
'URL must be an unsecured http address.'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should require an http url' do
|
53
|
+
expect { ImageListing.new(sku, 'ftp://domain.com/images/foo.png') }.to raise_error Mws::Errors::ValidationError,
|
54
|
+
'URL must be an unsecured http address.'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should require an unsecure url' do
|
58
|
+
expect { ImageListing.new(sku, 'https://domain.com/images/foo.png') }.to raise_error Mws::Errors::ValidationError,
|
59
|
+
'URL must be an unsecured http address.'
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
context '#==' do
|
65
|
+
|
66
|
+
it 'should be reflexive' do
|
67
|
+
(listing == listing).should be true
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should be symmetric' do
|
71
|
+
a = listing
|
72
|
+
b = ImageListing.new(sku, url, type)
|
73
|
+
c = ImageListing.new(sku, url)
|
74
|
+
(a == b).should == (b == a)
|
75
|
+
(a == c).should == (c == a)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should be transitive' do
|
79
|
+
a = listing
|
80
|
+
b = ImageListing.new(sku, url, type)
|
81
|
+
c = ImageListing.new(sku, url, type)
|
82
|
+
(a == c).should == (a == b && b == c)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should handle comparison to nil' do
|
86
|
+
(listing == nil).should be false
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#to_xml' do
|
92
|
+
|
93
|
+
it 'shoud properly serialize to xml' do
|
94
|
+
expected = Nokogiri::XML::Builder.new {
|
95
|
+
ProductImage {
|
96
|
+
SKU sku
|
97
|
+
ImageType 'PT1'
|
98
|
+
ImageLocation url
|
99
|
+
}
|
100
|
+
}.doc.root.to_xml
|
101
|
+
actual = listing.to_xml
|
102
|
+
actual.should == expected
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
describe Inventory do
|
6
|
+
|
7
|
+
context '.new' do
|
8
|
+
|
9
|
+
it 'should require non-nil sku' do
|
10
|
+
expect { Inventory.new(nil, quantity: 1) }.to raise_error Mws::Errors::ValidationError,
|
11
|
+
'SKU is required.'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should require a non-empty sku' do
|
15
|
+
expect { Inventory.new('', quantity: 1) }.to raise_error Mws::Errors::ValidationError,
|
16
|
+
'SKU is required.'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should require a sku that is not all whitespace' do
|
20
|
+
expect { Inventory.new(' ', quantity: 1) }.to raise_error Mws::Errors::ValidationError,
|
21
|
+
'SKU is required.'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should accept a valid value for sku' do
|
25
|
+
Inventory.new('987612345', quantity: 1).sku.should == '987612345'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should require one of available, quantity or lookup' do
|
29
|
+
expect { Inventory.new('987612345', {}) }.to raise_error Mws::Errors::ValidationError,
|
30
|
+
"One and only one of 'available', 'quantity' or 'lookup' must be specified."
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should require a boolean value for available' do
|
34
|
+
expect { Inventory.new('987612345', available: 1) }.to raise_error Mws::Errors::ValidationError,
|
35
|
+
'Available must be either true or false.'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should accept a valid value for available' do
|
39
|
+
Inventory.new('98712345', available: true).available.should be true
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should require quantity to be a whole number greater than or equal to zero' do
|
43
|
+
expect { Inventory.new('987612345', quantity: -1) }.to raise_error Mws::Errors::ValidationError,
|
44
|
+
'Quantity must be a whole number greater than or equal to zero.'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should accept a valid value for quantity' do
|
48
|
+
Inventory.new('987612345', quantity: 1).quantity.should == 1
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should require a boolean value for lookup' do
|
52
|
+
expect { Inventory.new('987612345', lookup: 'Yes') }.to raise_error Mws::Errors::ValidationError,
|
53
|
+
'Lookup must be either true or false.'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should accept a valid value for lookup' do
|
57
|
+
Inventory.new('987612345', lookup: true).lookup.should be true
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should accept only one of available, quantity or lookup' do
|
61
|
+
expect {
|
62
|
+
Inventory.new('987612345', available: true, quantity: 1, lookup: 'FulfillmentNetwork')
|
63
|
+
}.to raise_error Mws::Errors::ValidationError,
|
64
|
+
"One and only one of 'available', 'quantity' or 'lookup' must be specified."
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should accept a valid fulfillment center' do
|
68
|
+
Inventory.new('987612345', quantity: 1, fulfillment_center: 'foo').fulfillment.center.should == 'foo'
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should require fulfillment latency to be a whole number greater than zero' do
|
72
|
+
expect {
|
73
|
+
Inventory.new('987612345', quantity: 1, fulfillment_latency: 0)
|
74
|
+
}.to raise_error Mws::Errors::ValidationError,
|
75
|
+
'Fulfillment latency must be a whole number greater than zero.'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should accept a valid fulfillment latency' do
|
79
|
+
Inventory.new('987612345', quantity: 1, fulfillment_latency: 1).fulfillment.latency.should == 1
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should require fulfillment type to be either AFN or MFN' do
|
83
|
+
expect {
|
84
|
+
Inventory.new('987612345', quantity: 1, fulfillment_type: 'foo')
|
85
|
+
}.to raise_error Mws::Errors::ValidationError,
|
86
|
+
"Fulfillment type must be either 'AFN' or 'MFN'."
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should accept a valid fulfillment type' do
|
90
|
+
Inventory.new('987612345', quantity: 1, fulfillment_type: :mfn).fulfillment.type.should == :mfn
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should require the restock date to be in the future' do
|
94
|
+
expect {
|
95
|
+
Inventory.new('987612345', quantity: 0, restock: Time.now)
|
96
|
+
}.to raise_error Mws::Errors::ValidationError,
|
97
|
+
'Restock date must be in the future.'
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should accept a valid restock date' do
|
101
|
+
restock = 4.days.from_now
|
102
|
+
Inventory.new('987612345', quantity: 0, restock: restock).restock.should == restock
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
context '#xml_for' do
|
108
|
+
|
109
|
+
it 'should properly serialize to XML' do
|
110
|
+
restock = 4.days.from_now
|
111
|
+
inventory = Inventory.new('987612345',
|
112
|
+
quantity: 5,
|
113
|
+
fulfillment_center: 'A1B2C3D4E5',
|
114
|
+
fulfillment_latency: 3,
|
115
|
+
fulfillment_type: :mfn,
|
116
|
+
restock: restock
|
117
|
+
)
|
118
|
+
expected = Nokogiri::XML::Builder.new do
|
119
|
+
Inventory {
|
120
|
+
SKU '987612345'
|
121
|
+
FulfillmentCenterID 'A1B2C3D4E5'
|
122
|
+
Quantity 5
|
123
|
+
RestockDate restock.iso8601
|
124
|
+
FulfillmentLatency 3
|
125
|
+
SwitchFulfillmentTo 'MFN'
|
126
|
+
}
|
127
|
+
end.doc.root.to_xml
|
128
|
+
inventory.to_xml.should == expected
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Mws::Apis::Feeds
|
4
|
+
|
5
|
+
class Temperature < Measurement
|
6
|
+
|
7
|
+
Unit = Mws::Enum.for(
|
8
|
+
fahrenheit: 'Fahrenheight',
|
9
|
+
celcius: 'Celcius',
|
10
|
+
kelvin: 'Kelvin'
|
11
|
+
)
|
12
|
+
|
13
|
+
def initialize(amount, unit=:fahrenheit)
|
14
|
+
super amount, unit
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Measurement do
|
20
|
+
|
21
|
+
context '.new' do
|
22
|
+
|
23
|
+
it 'should default to fahrenheit' do
|
24
|
+
temp = Temperature.new 40
|
25
|
+
temp.amount.should == 40
|
26
|
+
temp.unit.should == :fahrenheit
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should accept a valid unit override' do
|
30
|
+
temp = Temperature.new 0, :kelvin
|
31
|
+
temp.amount.should == 0
|
32
|
+
temp.unit.should == :kelvin
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should validate the unit override' do
|
36
|
+
expect {
|
37
|
+
Temperature.new 40, :ounces
|
38
|
+
}.to raise_error Mws::Errors::ValidationError, "Invalid unit of measure 'ounces'"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
context '#==' do
|
44
|
+
|
45
|
+
it 'should be reflexive' do
|
46
|
+
a = Temperature.new 25, :celcius
|
47
|
+
(a == a).should be true
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be symmetric' do
|
51
|
+
a = Temperature.new 25, :celcius
|
52
|
+
b = Temperature.new 25, :celcius
|
53
|
+
(a == b).should == (b == a)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should be transitive' do
|
57
|
+
a = Temperature.new 25, :celcius
|
58
|
+
b = Temperature.new 25, :celcius
|
59
|
+
c = Temperature.new 25, :celcius
|
60
|
+
(a == c).should == (a == b && b == c)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should handle comparison to nil' do
|
64
|
+
a = Temperature.new 25, :celcius
|
65
|
+
(a == nil).should be false
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
context '#to_xml' do
|
71
|
+
|
72
|
+
it 'should properly serialize to XML' do
|
73
|
+
temp = Temperature.new 25, :celcius
|
74
|
+
expected = Nokogiri::XML::Builder.new do
|
75
|
+
Temperature 25, unitOfMeasure: 'Celcius'
|
76
|
+
end.doc.root.to_xml
|
77
|
+
temp.to_xml.should == expected
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|