mws-connect 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +19 -0
  2. data/.sublime-project +19 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +111 -0
  6. data/Rakefile +1 -0
  7. data/lib/mws.rb +34 -0
  8. data/lib/mws/apis.rb +6 -0
  9. data/lib/mws/apis/feeds.rb +20 -0
  10. data/lib/mws/apis/feeds/api.rb +103 -0
  11. data/lib/mws/apis/feeds/distance.rb +23 -0
  12. data/lib/mws/apis/feeds/feed.rb +114 -0
  13. data/lib/mws/apis/feeds/image_listing.rb +44 -0
  14. data/lib/mws/apis/feeds/inventory.rb +77 -0
  15. data/lib/mws/apis/feeds/measurement.rb +32 -0
  16. data/lib/mws/apis/feeds/money.rb +31 -0
  17. data/lib/mws/apis/feeds/price_listing.rb +48 -0
  18. data/lib/mws/apis/feeds/product.rb +173 -0
  19. data/lib/mws/apis/feeds/sale_price.rb +31 -0
  20. data/lib/mws/apis/feeds/shipping.rb +160 -0
  21. data/lib/mws/apis/feeds/submission_info.rb +45 -0
  22. data/lib/mws/apis/feeds/submission_result.rb +87 -0
  23. data/lib/mws/apis/feeds/transaction.rb +37 -0
  24. data/lib/mws/apis/feeds/weight.rb +19 -0
  25. data/lib/mws/apis/orders.rb +23 -0
  26. data/lib/mws/connection.rb +84 -0
  27. data/lib/mws/enum.rb +81 -0
  28. data/lib/mws/errors.rb +32 -0
  29. data/lib/mws/query.rb +45 -0
  30. data/lib/mws/serializer.rb +81 -0
  31. data/lib/mws/signer.rb +20 -0
  32. data/lib/mws/utils.rb +50 -0
  33. data/mws.gemspec +25 -0
  34. data/scripts/catalog-workflow +136 -0
  35. data/spec/mws/apis/feeds/api_spec.rb +229 -0
  36. data/spec/mws/apis/feeds/distance_spec.rb +43 -0
  37. data/spec/mws/apis/feeds/feed_spec.rb +92 -0
  38. data/spec/mws/apis/feeds/image_listing_spec.rb +109 -0
  39. data/spec/mws/apis/feeds/inventory_spec.rb +135 -0
  40. data/spec/mws/apis/feeds/measurement_spec.rb +84 -0
  41. data/spec/mws/apis/feeds/money_spec.rb +43 -0
  42. data/spec/mws/apis/feeds/price_listing_spec.rb +90 -0
  43. data/spec/mws/apis/feeds/product_spec.rb +264 -0
  44. data/spec/mws/apis/feeds/shipping_spec.rb +78 -0
  45. data/spec/mws/apis/feeds/submission_info_spec.rb +111 -0
  46. data/spec/mws/apis/feeds/submission_result_spec.rb +157 -0
  47. data/spec/mws/apis/feeds/transaction_spec.rb +64 -0
  48. data/spec/mws/apis/feeds/weight_spec.rb +43 -0
  49. data/spec/mws/apis/orders_spec.rb +9 -0
  50. data/spec/mws/connection_spec.rb +331 -0
  51. data/spec/mws/enum_spec.rb +166 -0
  52. data/spec/mws/query_spec.rb +104 -0
  53. data/spec/mws/serializer_spec.rb +187 -0
  54. data/spec/mws/signer_spec.rb +67 -0
  55. data/spec/mws/utils_spec.rb +147 -0
  56. data/spec/spec_helper.rb +10 -0
  57. metadata +220 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ coverage
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ *.sublime-workspace
data/.sublime-project ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": ".",
5
+ "folder_exclude_patterns": [
6
+ "pkg",
7
+ "coverage"
8
+ ],
9
+ "file_exclude_patterns": [
10
+ "*.sublime-project*",
11
+ ".gitignore"
12
+ ]
13
+ }
14
+ ],
15
+ "settings": {
16
+ "tab_size": 2,
17
+ "translate_tabs_to_spaces": true
18
+ }
19
+ }
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mws.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Sean M. Duncan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Mws
2
+
3
+ The goal of this gem is to facilities interactions with the Amazon Marketplace Web Services from Ruby clients.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mws'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install mws
18
+
19
+ ## Usage
20
+
21
+ Create Mws connection:
22
+
23
+ require 'mws'
24
+
25
+ mws = Mws.connect(
26
+ merchant: 'XXXXXXXXXXXXXX',
27
+ access: 'XXXXXXXXXXXXXXXXXXXXX',
28
+ secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
29
+ )
30
+
31
+ Access the Feeds Api:
32
+
33
+ feeds_api = mws.feeds
34
+
35
+ Access the Feeds Api wrappers:
36
+
37
+ products_api = mws.feeds.products
38
+ prices_api = mws.feeds.prices
39
+ images_api = mws.feeds.images
40
+ inventory_api = mws.feeds.inventory
41
+
42
+ Example: Add product details:
43
+
44
+ sku = '12345678'
45
+ product = Mws::Product sku {
46
+ upc '123435566654'
47
+ tax_code 'GEN_TAX_CODE'
48
+ name 'Some Product'
49
+ brand 'Some Brand'
50
+ msrp 19.99, 'USD'
51
+ manufacturer 'Some Manufacturer'
52
+ category :ce
53
+ details {
54
+ cable_or_adapter {
55
+ cable_length as_distance 5, :feet
56
+ }
57
+ }
58
+ }
59
+
60
+ submission_id = mws.feeds.products.add(product)
61
+
62
+ Example: Adding product images:
63
+
64
+ image_submission_id = mws.feeds.images.add(
65
+ Mws::ImageListing(sku, 'http://url.to.product.iamges/main.jpg', 'Main'),
66
+ Mws::ImageListing(sku, 'http://url.to.product.iamges/pt1.jpg', 'PT1')
67
+ )
68
+
69
+ Example: Setting product pricing:
70
+
71
+ price_submission_id = mws.feeds.prices.add(
72
+ Mws::PriceListing(sku, 14.99).on_sale(12.99, Time.now, 3.months.from_now)
73
+ )
74
+
75
+ Example: Overriding product shipping:
76
+
77
+ sku = '12345678'
78
+ shipping_submission_id = mws.feeds.shipping.add(
79
+ Mws::Shipping sku {
80
+ replace 'UPS Ground', 4.99
81
+ adjust '2nd-Day Air', 7.00
82
+ }
83
+ )
84
+
85
+ Example: Setting product inventory:
86
+
87
+ inventory_submission_id = mws.feeds.inventory.add(
88
+ Mws::Inventory(sku, quantity: 10, fulfillment_type: :mfn)
89
+ )
90
+
91
+ Example: Check the processing status of a feed:
92
+
93
+ mws.feeds.list(id: submission_id).each do | info |
94
+ puts "SubmissionId: #{info.id} Status: #{info.status}"
95
+ puts "Complete!" if [:cancelled, :done].include? info.status
96
+ end
97
+
98
+ Example: Get the results for a submission:
99
+
100
+ result = mws.feeds.get(submission_id)
101
+ puts "Submission: #{result.transaction_id} - #{result.status}"
102
+
103
+ _For an example of putting it all together check out the 'scripts/catalog-workflow'_
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/lib/mws.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ module Mws
5
+
6
+ autoload :Apis, 'mws/apis'
7
+ autoload :Connection, 'mws/connection'
8
+ autoload :Enum, 'mws/enum'
9
+ autoload :EnumEntry, 'mws/enum'
10
+ autoload :Errors, 'mws/errors'
11
+ autoload :Query, 'mws/query'
12
+ autoload :Serializer, 'mws/serializer'
13
+ autoload :Signer, 'mws/signer'
14
+ autoload :Utils, 'mws/utils'
15
+
16
+ # The current version of this ruby gem
17
+ VERSION = '0.0.1'
18
+
19
+ Utils.alias self, Apis::Feeds,
20
+ :Distance,
21
+ :Feed,
22
+ :ImageListing,
23
+ :Inventory,
24
+ :Money,
25
+ :PriceListing,
26
+ :Product,
27
+ :Shipping,
28
+ :Weight
29
+
30
+ def self.connect(options)
31
+ Connection.new options
32
+ end
33
+
34
+ end
data/lib/mws/apis.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Mws::Apis
2
+
3
+ autoload :Feeds, 'mws/apis/feeds'
4
+ autoload :Orders, 'mws/apis/orders'
5
+
6
+ end
@@ -0,0 +1,20 @@
1
+ module Mws::Apis::Feeds
2
+
3
+ autoload :Api, 'mws/apis/feeds/api'
4
+ autoload :Distance, 'mws/apis/feeds/distance'
5
+ autoload :Feed, 'mws/apis/feeds/feed'
6
+ autoload :ImageListing, 'mws/apis/feeds/image_listing'
7
+ autoload :Inventory, 'mws/apis/feeds/inventory'
8
+ autoload :Measurement, 'mws/apis/feeds/measurement'
9
+ autoload :Money, 'mws/apis/feeds/money'
10
+ autoload :PriceListing, 'mws/apis/feeds/price_listing'
11
+ autoload :Product, 'mws/apis/feeds/product'
12
+ autoload :SalePrice, 'mws/apis/feeds/sale_price'
13
+ autoload :Shipping, 'mws/apis/feeds/shipping'
14
+ autoload :SubmissionInfo, 'mws/apis/feeds/submission_info'
15
+ autoload :SubmissionResult, 'mws/apis/feeds/submission_result'
16
+ autoload :TargetedApi, 'mws/apis/feeds/api'
17
+ autoload :Transaction, 'mws/apis/feeds/transaction'
18
+ autoload :Weight, 'mws/apis/feeds/weight'
19
+
20
+ end
@@ -0,0 +1,103 @@
1
+ module Mws::Apis::Feeds
2
+
3
+ class Api
4
+
5
+ attr_reader :products, :images, :prices, :shipping, :inventory
6
+
7
+ def initialize(connection, defaults={})
8
+ raise Mws::Errors::ValidationError, 'A connection is required.' if connection.nil?
9
+ @connection = connection
10
+ defaults[:version] ||= '2009-01-01'
11
+ @defaults = defaults
12
+
13
+ @products = self.for :product
14
+ @images = self.for :image
15
+ @prices = self.for :price
16
+ @shipping = self.for :override
17
+ @inventory = self.for :inventory
18
+ end
19
+
20
+ def get(id)
21
+ node = @connection.get '/', { feed_submission_id: id }, @defaults.merge(
22
+ action: 'GetFeedSubmissionResult',
23
+ xpath: 'AmazonEnvelope/Message'
24
+ )
25
+ SubmissionResult.from_xml node
26
+ end
27
+
28
+ def submit(body, params)
29
+ params[:feed_type] = Feed::Type.for(params[:feed_type]).val
30
+ doc = @connection.post '/', params, body, @defaults.merge(action: 'SubmitFeed')
31
+ SubmissionInfo.from_xml doc.xpath('FeedSubmissionInfo').first
32
+ end
33
+
34
+ def cancel(options={})
35
+
36
+ end
37
+
38
+ def list(params={})
39
+ params[:feed_submission_id] ||= params.delete(:ids) || [ params.delete(:id) ].flatten.compact
40
+ doc = @connection.get('/', params, @defaults.merge(action: 'GetFeedSubmissionList'))
41
+ doc.xpath('FeedSubmissionInfo').map do | node |
42
+ SubmissionInfo.from_xml node
43
+ end
44
+ end
45
+
46
+ def count()
47
+ @connection.get('/', {}, @defaults.merge(action: 'GetFeedSubmissionCount')).xpath('Count').first.text.to_i
48
+ end
49
+
50
+ def for(type)
51
+ TargetedApi.new self, @connection.merchant, type
52
+ end
53
+
54
+ end
55
+
56
+ class TargetedApi
57
+
58
+ def initialize(feeds, merchant, type)
59
+ @feeds = feeds
60
+ @merchant = merchant
61
+ @message_type = Feed::Message::Type.for(type)
62
+ @feed_type = Feed::Type.for(type)
63
+ end
64
+
65
+ def add(*resources)
66
+ submit resources, :update, true
67
+ end
68
+
69
+ def update(*resources)
70
+ submit resources, :update
71
+ end
72
+
73
+ def patch(*resources)
74
+ raise 'Operation Type not supported.' unless @feed_type == Feed::Type.PRODUCT
75
+ submit resources, :partial_update
76
+ end
77
+
78
+ def delete(*resources)
79
+ submit resources, :delete
80
+ end
81
+
82
+ def submit(resources, def_operation_type=nil, purge_and_replace=false)
83
+ messages = []
84
+ feed = Feed.new @merchant, @message_type do
85
+ resources.each do | resource |
86
+ operation_type = def_operation_type
87
+ if resource.respond_to?(:operation_type) and resource.operation_type
88
+ operation_type = resource.operation_type
89
+ end
90
+ messages << message(resource, operation_type)
91
+ end
92
+ end
93
+ Transaction.new @feeds.submit(feed.to_xml, feed_type: @feed_type, purge_and_replace: purge_and_replace) do
94
+ messages.each do | message |
95
+ item message.id, message.resource.sku, message.operation_type,
96
+ message.resource.respond_to?(:type) ? message.resource.type : nil
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,23 @@
1
+ module Mws::Apis::Feeds
2
+
3
+ class Distance < Measurement
4
+
5
+ Unit = Mws::Enum.for(
6
+ inches: 'inches',
7
+ feet: 'feet',
8
+ meters: 'meters',
9
+ decimeters: 'decimeters',
10
+ centimeters:'centimeters',
11
+ millimeters:'millimeters',
12
+ micrometers: 'micrometers',
13
+ nanometers: 'nanometers',
14
+ picometers: 'picometers'
15
+ )
16
+
17
+ def initialize(amount, unit=nil)
18
+ super amount, unit || :feet
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,114 @@
1
+ require 'nokogiri'
2
+
3
+ module Mws::Apis::Feeds
4
+
5
+ class Feed
6
+
7
+ Type = Mws::Enum.for(
8
+ product: '_POST_PRODUCT_DATA_',
9
+ product_relationship: '_POST_PRODUCT_RELATIONSHIP_DATA_',
10
+ item: '_POST_ITEM_DATA_',
11
+ override: '_POST_PRODUCT_OVERRIDES_DATA_',
12
+ image: '_POST_PRODUCT_IMAGE_DATA_',
13
+ price: '_POST_PRODUCT_PRICING_DATA_',
14
+ inventory: '_POST_INVENTORY_AVAILABILITY_DATA_',
15
+ order_acknowledgement: '_POST_ORDER_ACKNOWLEDGEMENT_DATA_',
16
+ order_fufillment: '_POST_ORDER_FULFILLMENT_DATA_',
17
+ fulfillment_order_request: '_POST_FULFILLMENT_ORDER_REQUEST_DATA_',
18
+ fulfillment_order_cancellation: '_POST_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA'
19
+ )
20
+
21
+ attr_reader :merchant, :purge_and_replace
22
+
23
+ Mws::Enum.sym_reader self, :message_type
24
+
25
+ def initialize(merchant, message_type, purge_and_replace=false, &block)
26
+ @merchant = merchant
27
+ raise Mws::Errors::ValidationError, 'Merchant identifier is required.' if @merchant.nil?
28
+ @message_type = Message::Type.for(message_type)
29
+ raise Mws::Errors::ValidationError, 'A valid message type is required.' if @message_type.nil?
30
+ @purge_and_replace = purge_and_replace
31
+ @messages = []
32
+ Builder.new(self, @messages).instance_eval &block if block_given?
33
+ end
34
+
35
+ def messages
36
+ @messages.dup
37
+ end
38
+
39
+ def to_xml
40
+ Nokogiri::XML::Builder.new do | xml |
41
+ xml.AmazonEnvelope('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'amznenvelope.xsd') {
42
+ xml.Header {
43
+ xml.DocumentVersion '1.01'
44
+ xml.MerchantIdentifier @merchant
45
+ }
46
+ xml.MessageType @message_type.val
47
+ xml.PurgeAndReplace @purge_and_replace
48
+ @messages.each do | message |
49
+ message.to_xml xml
50
+ end
51
+ }
52
+ end.to_xml
53
+ end
54
+
55
+ class Builder
56
+
57
+ def initialize(feed, messages)
58
+ @feed = feed
59
+ @messages = messages
60
+ end
61
+
62
+ def message(resource, operation_type=nil)
63
+ (@messages << Message.new(@messages.length + 1, @feed.message_type, resource, operation_type)).last
64
+ end
65
+
66
+ end
67
+
68
+ class Message
69
+
70
+ Type = Mws::Enum.for(
71
+ fufillment_center: 'FulfillmentCenter',
72
+ inventory: 'Inventory',
73
+ listings: 'Listings',
74
+ order_acknowledgement: 'OrderAcknowledgement',
75
+ order_adjustment: 'OrderAdjustment',
76
+ order_fulfillment: 'OrderFulfillment',
77
+ override: 'Override',
78
+ price: 'Price',
79
+ processing_report: 'ProcessingReport',
80
+ product: 'Product',
81
+ image: 'ProductImage',
82
+ relationship: 'Relationship',
83
+ settlement_report: 'SettlementReport'
84
+ )
85
+
86
+ OperationType = Mws::Enum.for(
87
+ update: 'Update',
88
+ delete: 'Delete',
89
+ partial_update: 'PartialUpdate'
90
+ )
91
+
92
+ attr_reader :id, :resource
93
+
94
+ Mws::Enum.sym_reader self, :type, :operation_type
95
+
96
+ def initialize(id, type, resource, operation_type)
97
+ @id = id
98
+ @type = Type.for(type)
99
+ @resource = resource
100
+ @operation_type = OperationType.for(operation_type) || OperationType.UPDATE
101
+ end
102
+
103
+ def to_xml(parent)
104
+ Mws::Serializer.tree 'Message', parent do | xml |
105
+ xml.MessageID @id
106
+ xml.OperationType @operation_type.val
107
+ @resource.to_xml @type.val, xml
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end