afterbuy 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.
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