afterbuy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +47 -0
  7. data/Rakefile +6 -0
  8. data/afterbuy.gemspec +31 -0
  9. data/lib/afterbuy.rb +43 -0
  10. data/lib/afterbuy/api.rb +101 -0
  11. data/lib/afterbuy/errors.rb +21 -0
  12. data/lib/afterbuy/middleware/error_detector.rb +15 -0
  13. data/lib/afterbuy/representer/attribute_representer.rb +18 -0
  14. data/lib/afterbuy/representer/catalog_representer.rb +16 -0
  15. data/lib/afterbuy/representer/error_representer.rb +16 -0
  16. data/lib/afterbuy/representer/global_representer.rb +20 -0
  17. data/lib/afterbuy/representer/product_picture_representer.rb +18 -0
  18. data/lib/afterbuy/representer/product_representer.rb +134 -0
  19. data/lib/afterbuy/representer/request_representer.rb +14 -0
  20. data/lib/afterbuy/representer/response_representer.rb +17 -0
  21. data/lib/afterbuy/representer/result_representer.rb +15 -0
  22. data/lib/afterbuy/representer/shop_interface_line_item_representer.rb +22 -0
  23. data/lib/afterbuy/representer/shop_interface_request_representer.rb +97 -0
  24. data/lib/afterbuy/representer/shop_interface_response_representer.rb +33 -0
  25. data/lib/afterbuy/representer/time_response_representer.rb +15 -0
  26. data/lib/afterbuy/representer/update_shop_products_request_representer.rb +10 -0
  27. data/lib/afterbuy/representer/update_shop_products_response_representer.rb +16 -0
  28. data/lib/afterbuy/representer/warning_representer.rb +16 -0
  29. data/lib/afterbuy/version.rb +3 -0
  30. data/spec/api_spec.rb +184 -0
  31. data/spec/fixtures/vcr_cassettes/Afterbuy_API/valid_configuration/_call/GetAfterbuyTime/responds_with_timestamps.yml +57 -0
  32. data/spec/fixtures/vcr_cassettes/Afterbuy_API/valid_configuration/_call/invalid_method_call/responds_with_a_call_not_found_error.yml +58 -0
  33. data/spec/fixtures/vcr_cassettes/Afterbuy_API/valid_configuration/_shop_interface_call/invalid_parameters/fails_with_error.yml +42 -0
  34. data/spec/fixtures/vcr_cassettes/Afterbuy_API/valid_configuration/_shop_interface_call/valid_parameters_with_action_CheckUserId/succeeds_and_returns_the_user_id.yml +42 -0
  35. data/spec/fixtures/vcr_cassettes/Afterbuy_API/valid_configuration/_shop_interface_call/valid_parameters_with_action_new/succeeds.yml +43 -0
  36. data/spec/representer/shop_interface_request_representer_spec.rb +29 -0
  37. data/spec/spec_helper.rb +15 -0
  38. data/spec/support/vcr_config.rb +7 -0
  39. metadata +216 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fd4ef2553526d4db9632a8dae402be8d27019eb7
4
+ data.tar.gz: b6d383481f3035453d0dc1613eca028b05e8130c
5
+ SHA512:
6
+ metadata.gz: 18f4a5af38b82cf65c532a032f59812f90abf087b30b086ae19d9fe74f73255378ea1596eb7cba19a72ba84cfd86f7bdbab82c3eff07174b32ffc637f4df7f19
7
+ data.tar.gz: 439c55836afad19dd9442c163950dacf2f7aee0d8ebac894d8f6fc385237b2f263c942da77bb4a32ff11a8225ab461a4350eb12d3bb8d4ef67da02c685a25be5
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in afterbuy.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Salim Hbeiliny
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.
@@ -0,0 +1,47 @@
1
+ # Afterbuy
2
+
3
+ This is a wrapper for the afterbuy API.
4
+ API's definition is documented here: http://xmldoku.afterbuy.de/dokued/
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'afterbuy'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install afterbuy
21
+
22
+ ## Usage
23
+
24
+ Afterbuy.configure do |config|
25
+ config.partner_id = 'valid_partner_id'
26
+ config.partner_password = 'valid_partner_password'
27
+ config.user_id = 'valid_user_id'
28
+ config.user_password = 'valid_user_password'
29
+ end
30
+
31
+ afterbuy = Afterbuy::API.new
32
+ afterbuy.call('GetAfterbuyTime')
33
+ => #<Afterbuy::TimeResponse
34
+ call_status="Success",
35
+ call_name="GetAfterbuyTime",
36
+ version_id="8",
37
+ result=#<Afterbuy::Result
38
+ afterbuy_timestamp="01.04.2015 16:52:35",
39
+ afterbuy_universal_timestamp="01.04.2015 14:52:35">>
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it ( https://github.com/[my-github-username]/afterbuy/fork )
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'afterbuy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "afterbuy"
8
+ spec.version = Afterbuy::VERSION
9
+ spec.authors = ["Salim Hbeiliny"]
10
+ spec.email = ["salim@railslove.com"]
11
+ spec.summary = %q{A wrapper for the Afterbuy API}
12
+ spec.description = %q{This is a wrapper for the Afterbuy API documented here: http://xmldoku.afterbuy.de/dokued/}
13
+ spec.homepage = 'https://github.com/railslove/afterbuy'
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activesupport', '~> 4.2'
22
+ spec.add_dependency 'faraday'
23
+ spec.add_dependency 'roar'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.7'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3'
28
+ spec.add_development_dependency 'webmock'
29
+ spec.add_development_dependency 'vcr'
30
+ spec.add_development_dependency 'pry'
31
+ end
@@ -0,0 +1,43 @@
1
+ require 'active_support/configurable'
2
+ require 'active_support/inflector'
3
+ require 'faraday'
4
+ require 'ostruct'
5
+ require 'roar/decorator'
6
+ require 'roar/xml'
7
+
8
+ require 'afterbuy/version'
9
+ require 'afterbuy/errors'
10
+
11
+ require 'afterbuy/middleware/error_detector'
12
+
13
+ require 'afterbuy/representer/global_representer'
14
+ require 'afterbuy/representer/request_representer'
15
+
16
+ require 'afterbuy/representer/error_representer'
17
+ require 'afterbuy/representer/warning_representer'
18
+ require 'afterbuy/representer/result_representer'
19
+ require 'afterbuy/representer/response_representer'
20
+ require 'afterbuy/representer/time_response_representer'
21
+
22
+ require 'afterbuy/representer/catalog_representer'
23
+ require 'afterbuy/representer/product_picture_representer'
24
+ require 'afterbuy/representer/attribute_representer'
25
+ require 'afterbuy/representer/product_representer'
26
+ require 'afterbuy/representer/update_shop_products_request_representer'
27
+ require 'afterbuy/representer/update_shop_products_response_representer'
28
+ require 'afterbuy/representer/shop_interface_line_item_representer'
29
+ require 'afterbuy/representer/shop_interface_request_representer'
30
+ require 'afterbuy/representer/shop_interface_response_representer'
31
+
32
+ require 'afterbuy/api'
33
+
34
+ module Afterbuy
35
+ include ActiveSupport::Configurable
36
+
37
+ config_accessor(:partner_id)
38
+ config_accessor(:partner_password)
39
+ config_accessor(:user_id)
40
+ config_accessor(:user_password)
41
+ config_accessor(:afterbuy_api_url) { 'https://api.afterbuy.de/afterbuy/ABInterface.aspx' }
42
+ config_accessor(:afterbuy_shop_interface_url) { 'https://api.afterbuy.de/afterbuy/ShopInterfaceUTF8.aspx' }
43
+ end
@@ -0,0 +1,101 @@
1
+ module Afterbuy
2
+ class API
3
+
4
+ METHOD_REQUEST_MAPPING = {
5
+ 'GetAfterbuyTime' => '',
6
+ 'UpdateShopProducts' => 'UpdateShopProducts'
7
+ }
8
+
9
+ METHOD_RESPONSE_MAPPING = {
10
+ 'GetAfterbuyTime' => 'Time',
11
+ 'UpdateShopProducts' => 'UpdateShopProducts'
12
+ }
13
+
14
+ attr_accessor :debug_info
15
+
16
+ def initialize(partner_id: nil, partner_password: nil, user_id: nil, user_password: nil)
17
+ raise ConfigMissingPartnerIDError, 'You must provide an Afterbuy partner_id' unless Afterbuy.config.partner_id || partner_id
18
+ raise ConfigMissingPartnerPasswordError, 'You must provide an Afterbuy partner_password' unless Afterbuy.config.partner_password || partner_password
19
+ raise ConfigMissingUserIDError, 'You must provide an Afterbuy user_id' unless Afterbuy.config.user_id || user_id
20
+ raise ConfigMissingUserPasswordError, 'You must provide an Afterbuy user_password' unless Afterbuy.config.user_password || user_password
21
+
22
+ @partner_id = Afterbuy.config.partner_id || partner_id
23
+ @partner_password = Afterbuy.config.partner_password || partner_password
24
+ @user_id = Afterbuy.config.user_id || user_id
25
+ @user_password = Afterbuy.config.user_password || user_password
26
+ @api_url = Afterbuy.config.afterbuy_api_url
27
+ @shop_interface_url = Afterbuy.config.afterbuy_shop_interface_url
28
+ end
29
+
30
+ def call(method_name, global_params: {}, payload: {})
31
+ self.debug_info = { request_params: request_params(method_name, global_params, payload) }
32
+
33
+ response = connection.post do |req|
34
+ req.body = request_params(method_name, global_params, payload)
35
+ end
36
+
37
+ self.debug_info[:response_body] = response.body
38
+
39
+ "Afterbuy::Representer::#{METHOD_RESPONSE_MAPPING[method_name]}ResponseRepresenter".constantize.new("Afterbuy::#{METHOD_RESPONSE_MAPPING[method_name]}Response".constantize.new).from_xml(response.body)
40
+ end
41
+
42
+ def shop_interface_call(global_params: {}, request: Afterbuy::ShopInterfaceRequest.new)
43
+ self.debug_info = { request_params: shop_interface_request_params(global_params, request).to_hash }
44
+
45
+ response = shop_interface_connection.post do |req|
46
+ req.body = shop_interface_request_params(global_params, request).to_hash
47
+ end
48
+
49
+ self.debug_info[:response_body] = response.body
50
+
51
+ Afterbuy::Representer::ShopInterfaceResponseRepresenter.new(Afterbuy::ShopInterfaceResponse.new).from_xml(response.body)
52
+ end
53
+
54
+ def connection
55
+ @connection ||= Faraday.new(url: @api_url) do |faraday|
56
+ faraday.headers['Content-Type'] = 'application/xml'
57
+ faraday.adapter Faraday.default_adapter
58
+ faraday.use Afterbuy::Middleware::ErrorDetector
59
+ end
60
+ end
61
+
62
+ def shop_interface_connection
63
+ @shop_interface_connection ||= Faraday.new(url: @shop_interface_url) do |faraday|
64
+ faraday.request :url_encoded
65
+ faraday.adapter Faraday.default_adapter
66
+ faraday.use Afterbuy::Middleware::ErrorDetector
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def request_params(method_name, global_params={}, payload={})
73
+ request_params = payload.merge({
74
+ afterbuy_global: Global.new(
75
+ global_params.merge({
76
+ partner_id: @partner_id,
77
+ partner_password: @partner_password,
78
+ user_id: @user_id,
79
+ user_password: @user_password,
80
+ call_name: method_name,
81
+ detail_level: 0,
82
+ error_language: 'EN'
83
+ })
84
+ )
85
+ })
86
+
87
+ "Afterbuy::Representer::#{METHOD_REQUEST_MAPPING[method_name]}RequestRepresenter".constantize.new(
88
+ "Afterbuy::#{METHOD_REQUEST_MAPPING[method_name]}Request".constantize.new(request_params)
89
+ ).to_xml
90
+ end
91
+
92
+ def shop_interface_request_params(global_params={}, request=Afterbuy::ShopInterfaceRequest.new)
93
+ request.partner_id = global_params[:partner_id] || @partner_id
94
+ request.partner_pass = global_params[:partner_pass] || @partner_password
95
+ request.user_id = global_params[:user_id] || @user_id
96
+
97
+ Afterbuy::Representer::ShopInterfaceRequestRepresenter.new request
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,21 @@
1
+ module Afterbuy
2
+
3
+ class APIError < StandardError
4
+ end
5
+
6
+ class ConfigError < StandardError
7
+ end
8
+
9
+ class ConfigMissingPartnerIDError < ConfigError
10
+ end
11
+
12
+ class ConfigMissingPartnerPasswordError < ConfigError
13
+ end
14
+
15
+ class ConfigMissingUserIDError < ConfigError
16
+ end
17
+
18
+ class ConfigMissingUserPasswordError < ConfigError
19
+ end
20
+
21
+ end
@@ -0,0 +1,15 @@
1
+ module Afterbuy
2
+ module Middleware
3
+ class ErrorDetector < Faraday::Middleware
4
+
5
+ def call(env)
6
+ @app.call(env).on_complete do |env|
7
+ unless (200..299).include? env[:status]
8
+ raise APIError, env[:body]
9
+ end
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Afterbuy
2
+ class Attribute < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ module AttributeRepresenter
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :Attribut # german ftw
10
+
11
+ property :name, as: :AttributName
12
+ property :value, as: :AttributValue
13
+ property :type, as: :AttributTyp
14
+ property :position, as: :AttributPosition
15
+ property :required, as: :AttributRequired
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module Afterbuy
2
+ class Catalog < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ module CatalogRepresenter
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :Catalog
10
+
11
+ property :id, as: :CatalogID
12
+ property :name, as: :CatalogName
13
+ property :level, as: :CatalogLevel
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Afterbuy
2
+ class Error < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ class ErrorRepresenter < Roar::Decorator
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :Error
10
+
11
+ property :code, as: :ErrorCode
12
+ property :description, as: :ErrorDescription
13
+ property :long_description, as: :ErrorLongDescription
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Afterbuy
2
+ class Global < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ class GlobalRepresenter < Roar::Decorator
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :AfterbuyGlobal
10
+
11
+ property :partner_id, as: :PartnerID
12
+ property :partner_password, as: :PartnerPassword
13
+ property :user_id, as: :UserID
14
+ property :user_password, as: :UserPassword
15
+ property :call_name, as: :CallName
16
+ property :detail_level, as: :DetailLevel
17
+ property :error_language, as: :ErrorLanguage
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Afterbuy
2
+ class ProductPicture < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ class ProductPictureRepresenter < Roar::Decorator
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :ProductPicture
10
+
11
+ property :nr, as: :Nr
12
+ property :url, as: :Url
13
+ property :alt_text, as: :AltText
14
+ property :type, as: :Typ # used only in the children collection
15
+ collection :children, as: :ProductPicture, wrap: :Childs, extend: ProductPictureRepresenter, class: ProductPicture
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,134 @@
1
+ module Afterbuy
2
+ class Product < OpenStruct
3
+ end
4
+
5
+ module Representer
6
+ class ProductRepresenter < Roar::Decorator
7
+ include Roar::XML
8
+
9
+ self.representation_wrap = :Product
10
+
11
+ # properties sent with UpdateShopProducts Request
12
+ property :product_ident, as: :ProductIdent do
13
+ self.representation_wrap = :ProductIdent
14
+
15
+ property :product_insert, as: :ProductInsert
16
+ property :base_product_type, as: :BaseProductType
17
+ property :user_product_id, as: :UserProductID
18
+ property :product_id, as: :ProductID
19
+ property :anr, as: :Anr
20
+ property :ean, as: :EAN
21
+ end
22
+
23
+ # properties returned with UpdateShopProducts Response
24
+ property :product_id, as: :ProductID
25
+ property :product_id_requested, as: :ProductIDRequested
26
+ property :anr_requested, as: :AnrRequested
27
+ property :ean_requested, as: :EANRequested
28
+ property :user_product_id, as: :UserProductID
29
+
30
+ # other properties
31
+ property :anr, as: :Anr
32
+ property :ean, as: :EAN
33
+ property :footer_id, as: :FooterID
34
+ property :header_id, as: :HeaderID
35
+ property :name, as: :Name
36
+ property :manufacturer_part_number, as: :ManufacturerPartNumber
37
+ property :short_description, as: :ShortDescription
38
+ property :memo, as: :Memo
39
+ property :description, as: :Description
40
+ property :keywords, as: :Keywords
41
+ property :quantity, as: :Quantity
42
+ property :auction_quantity, as: :AuctionQuantity
43
+ property :stock, as: :Stock
44
+ property :discontinued, as: :Discontinued
45
+ property :merge_stock, as: :MergeStock
46
+ property :unit_of_quantity, as: :UnitOfQuantity
47
+ property :baseprice_factor, as: :BasepriceFactor
48
+ property :minimum_stock, as: :MinimumStock
49
+ property :selling_price, as: :SellingPrice
50
+ property :buying_price, as: :BuyingPrice
51
+ property :dealer_price, as: :DealerPrice
52
+ property :level, as: :Level
53
+ property :position, as: :Position
54
+ property :title_replace, as: :TitleReplace
55
+ property :tax_rate, as: :TaxRate
56
+ property :weight, as: :Weight
57
+ property :stocklocation_1, as: :Stocklocation_1
58
+ property :stocklocation_2, as: :Stocklocation_2
59
+ property :stocklocation_3, as: :Stocklocation_3
60
+ property :stocklocation_4, as: :Stocklocation_4
61
+ property :search_alias, as: :SearchAlias
62
+ property :froogle, as: :Froogle
63
+ property :kelkoo, as: :Kelkoo
64
+ property :shipping_group, as: :ShippingGroup
65
+ property :shop_shipping_group, as: :ShopShippingGroup
66
+ property :cross_catalog_id, as: :CrossCatalogID
67
+ property :free_value_1, as: :FreeValue1
68
+ property :free_value_2, as: :FreeValue2
69
+ property :free_value_3, as: :FreeValue3
70
+ property :free_value_4, as: :FreeValue4
71
+ property :free_value_5, as: :FreeValue5
72
+ property :free_value_6, as: :FreeValue6
73
+ property :free_value_7, as: :FreeValue7
74
+ property :free_value_8, as: :FreeValue8
75
+ property :free_value_9, as: :FreeValue9
76
+ property :free_value_10, as: :FreeValue10
77
+ property :delivery_time, as: :DeliveryTime
78
+ property :image_small_url, as: :ImageSmallURL
79
+ property :image_large_url, as: :ImageLargeURL
80
+ property :image_name, as: :ImageName
81
+ property :image_source, as: :ImageSource
82
+ property :manufacturer_standard_product_id_type, as: :ManufacturerStandardProductIDType
83
+ property :manufacturer_standard_product_id_value, as: :ManufacturerStandardProductIDValue
84
+ property :product_brand, as: :ProductBrand
85
+ property :google_product_category, as: :GoogleProductCategory
86
+ property :condition, as: :Condition
87
+ property :age_group, as: :AgeGroup
88
+ property :gender, as: :Gender
89
+
90
+ # Using a property here instead of a collection because collections don't allow
91
+ # adding a property at the beginning of the collection as shown below
92
+ #
93
+ # <AddCatalogs>
94
+ # <UpdateAction>1</UpdateAction> <======= (this is the intruder here)
95
+ # <AddCatalog>
96
+ # <CatalogID>1</CatalogID>
97
+ # <CatalogName>First Category</CatalogName>
98
+ # </AddCatalog>
99
+ # <AddCatalog>
100
+ # <CatalogID>2</CatalogID>
101
+ # <CatalogName>Second Category</CatalogName>
102
+ # </AddCatalog>
103
+ # </AddCatalogs>
104
+ property :add_catalogs do
105
+ self.representation_wrap = :AddCatalogs
106
+ property :update_action, as: :UpdateAction
107
+ collection :add_catalogs do
108
+ include CatalogRepresenter
109
+ self.representation_wrap = :AddCatalog
110
+ end
111
+ end
112
+
113
+ property :skus do
114
+ self.representation_wrap = :Skus
115
+ property :update_action, as: :UpdateAction
116
+ collection :skus do
117
+ self.representation_wrap = :Sku
118
+ property :sku_number, content: true
119
+ end
120
+ end
121
+
122
+ property :add_attributes do
123
+ self.representation_wrap = :AddAttributes
124
+ property :update_action, as: :UpdateAction
125
+ collection :add_catalogs do
126
+ include AttributeRepresenter
127
+ self.representation_wrap = :AddAttribut
128
+ end
129
+ end
130
+
131
+ collection :product_pictures, as: :ProductPicture, wrap: :ProductPictures, extend: ProductPictureRepresenter, class: ProductPicture
132
+ end
133
+ end
134
+ end