wash_out 0.9.2 → 0.11.0

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 +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +35 -4
  4. data/Appraisals +8 -12
  5. data/Gemfile +1 -1
  6. data/README.md +85 -7
  7. data/Rakefile +15 -5
  8. data/app/helpers/wash_out_helper.rb +59 -24
  9. data/app/views/{wash_with_soap → wash_out}/document/error.builder +0 -0
  10. data/app/views/{wash_with_soap → wash_out}/document/response.builder +7 -0
  11. data/app/views/{wash_with_soap → wash_out}/document/wsdl.builder +14 -14
  12. data/app/views/{wash_with_soap → wash_out}/rpc/error.builder +0 -0
  13. data/app/views/{wash_with_soap → wash_out}/rpc/response.builder +8 -1
  14. data/app/views/{wash_with_soap → wash_out}/rpc/wsdl.builder +15 -15
  15. data/gemfiles/rails_3.2.13.gemfile +21 -0
  16. data/gemfiles/rails_4.0.0.gemfile +20 -0
  17. data/gemfiles/rails_4.1.0.gemfile +20 -0
  18. data/gemfiles/rails_4.2.0.gemfile +20 -0
  19. data/gemfiles/rails_5.0.0.beta2.gemfile +19 -0
  20. data/gemfiles/rails_5.0.0.gemfile +19 -0
  21. data/lib/wash_out/dispatcher.rb +96 -39
  22. data/lib/wash_out/param.rb +14 -2
  23. data/lib/wash_out/router.rb +52 -22
  24. data/lib/wash_out/soap.rb +11 -0
  25. data/lib/wash_out/version.rb +1 -1
  26. data/lib/wash_out/wsse.rb +3 -3
  27. data/lib/wash_out.rb +32 -5
  28. data/spec/dummy/config/environments/test.rb +1 -0
  29. data/spec/fixtures/nested_refs_to_arrays.xml +19 -0
  30. data/spec/fixtures/ref_to_one_array.xml +11 -0
  31. data/spec/fixtures/refs_to_arrays.xml +16 -0
  32. data/spec/lib/wash_out/dispatcher_spec.rb +124 -17
  33. data/spec/lib/wash_out/middleware_spec.rb +8 -8
  34. data/spec/lib/wash_out/param_spec.rb +43 -11
  35. data/spec/lib/wash_out/router_spec.rb +33 -5
  36. data/spec/lib/wash_out/type_spec.rb +9 -9
  37. data/spec/lib/wash_out_spec.rb +361 -103
  38. data/spec/spec_helper.rb +26 -4
  39. metadata +18 -9
@@ -16,9 +16,8 @@ module WashOut
16
16
  class ProgrammerError < Exception; end
17
17
 
18
18
  def _authenticate_wsse
19
-
20
19
  begin
21
- xml_security = env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
20
+ xml_security = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
22
21
  xml_security = xml_security.values_at(:header, :Header).compact.first
23
22
  xml_security = xml_security.values_at(:security, :Security).compact.first
24
23
  username_token = xml_security.values_at(:username_token, :UsernameToken).compact.first
@@ -32,22 +31,31 @@ module WashOut
32
31
  end
33
32
 
34
33
  def _map_soap_parameters
35
- strip_empty_nodes = lambda{|hash|
36
- hash.keys.each do |key|
37
- if hash[key].is_a? Hash
38
- value = hash[key].delete_if{|k, v| k.to_s[0] == '@'}
34
+ self.params = _load_params action_spec[:in],
35
+ _strip_empty_nodes(action_spec[:in], xml_data)
36
+ end
39
37
 
40
- if value.length > 0
41
- hash[key] = strip_empty_nodes.call(value)
42
- else
43
- hash[key] = nil
44
- end
45
- end
38
+ def _map_soap_headers
39
+ @_soap_headers = xml_header_data
40
+ end
41
+
42
+ def _strip_empty_nodes(params, hash)
43
+ hash.keys.each do |key|
44
+ param = params.detect { |a| a.raw_name.to_s == key.to_s }
45
+ next if !(param && hash[key].is_a?(Hash))
46
+
47
+ value = hash[key].delete_if do |k, _|
48
+ k.to_s[0] == '@' && !param.map.detect { |a| a.raw_name.to_s == k.to_s }
46
49
  end
47
50
 
48
- hash
49
- }
50
- @_params = _load_params(action_spec[:in], strip_empty_nodes.call(xml_data))
51
+ if value.length > 0
52
+ hash[key] = _strip_empty_nodes param.map, value
53
+ else
54
+ hash[key] = nil
55
+ end
56
+ end
57
+
58
+ hash
51
59
  end
52
60
 
53
61
  # Creates the final parameter hash based on the request spec and xml_data from the request
@@ -64,12 +72,11 @@ module WashOut
64
72
 
65
73
  # This action generates the WSDL for defined SOAP methods.
66
74
  def _generate_wsdl
67
-
68
75
  @map = self.class.soap_actions
69
76
  @namespace = soap_config.namespace
70
- @name = controller_path.gsub('/', '_')
77
+ @name = controller_path
71
78
 
72
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/wsdl", :layout => false,
79
+ render :template => "wash_out/#{soap_config.wsdl_style}/wsdl", :layout => false,
73
80
  :content_type => 'text/xml'
74
81
  end
75
82
 
@@ -124,9 +131,19 @@ module WashOut
124
131
  return result_spec
125
132
  }
126
133
 
127
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/response",
134
+ header = options[:header]
135
+ if header.present?
136
+ header = { 'value' => header } unless header.is_a? Hash
137
+ header = HashWithIndifferentAccess.new(header)
138
+ end
139
+
140
+ render :template => "wash_out/#{soap_config.wsdl_style}/response",
128
141
  :layout => false,
129
- :locals => { :result => inject.call(result, @action_spec[:out]) },
142
+ :locals => {
143
+ :header => header.present? ? inject.call(header, @action_spec[:header_out])
144
+ : nil,
145
+ :result => inject.call(result, @action_spec[:out])
146
+ },
130
147
  :content_type => 'text/xml'
131
148
  end
132
149
 
@@ -135,6 +152,10 @@ module WashOut
135
152
  render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out.soap_action']}")
136
153
  end
137
154
 
155
+ def _invalid_request
156
+ render_soap_error("Invalid SOAP request")
157
+ end
158
+
138
159
  def _catch_soap_errors
139
160
  yield
140
161
  rescue SOAPError => error
@@ -146,56 +167,92 @@ module WashOut
146
167
  # Rails do not support sequental rescue_from handling, that is, rescuing an
147
168
  # exception from a rescue_from handler. Hence this function is a public API.
148
169
  def render_soap_error(message, code=nil)
149
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/error", :status => 500,
170
+ render :template => "wash_out/#{soap_config.wsdl_style}/error", :status => 500,
150
171
  :layout => false,
151
172
  :locals => { :error_message => message, :error_code => (code || 'Server') },
152
173
  :content_type => 'text/xml'
153
174
  end
154
175
 
176
+ def soap_request
177
+ OpenStruct.new({
178
+ params: @_params,
179
+ headers: @_soap_headers
180
+ })
181
+ end
182
+
155
183
  def self.included(controller)
156
- controller.send :around_filter, :_catch_soap_errors
184
+ entity = if defined?(Rails::VERSION::MAJOR) && (Rails::VERSION::MAJOR >= 4)
185
+ 'action'
186
+ else
187
+ 'filter'
188
+ end
189
+
190
+ controller.send :"around_#{entity}", :_catch_soap_errors
157
191
  controller.send :helper, :wash_out
158
- controller.send :before_filter, :_authenticate_wsse, :except => [
159
- :_generate_wsdl, :_invalid_action ]
160
- controller.send :before_filter, :_map_soap_parameters, :except => [
161
- :_generate_wsdl, :_invalid_action ]
162
- controller.send :skip_before_filter, :verify_authenticity_token
192
+ controller.send :"before_#{entity}", :_authenticate_wsse, :if => :soap_action?
193
+ controller.send :"before_#{entity}", :_map_soap_parameters, :if => :soap_action?
194
+ controller.send :"before_#{entity}", :_map_soap_headers, :if => :soap_action?
195
+ controller.send :"skip_before_#{entity}", :verify_authenticity_token, :raise => false
163
196
  end
164
197
 
165
- def self.deep_select(hash, result=[], &blk)
166
- result += Hash[hash.select(&blk)].values
198
+ def self.deep_select(collection, result=[], &blk)
199
+ values = collection.respond_to?(:values) ? collection.values : collection
200
+ result += values.select(&blk)
167
201
 
168
- hash.each do |key, value|
169
- result = deep_select(value, result, &blk) if value.is_a? Hash
202
+ values.each do |value|
203
+ if value.is_a?(Hash) || value.is_a?(Array)
204
+ result = deep_select(value, result, &blk)
205
+ end
170
206
  end
171
207
 
172
208
  result
173
209
  end
174
210
 
175
- def self.deep_replace_href(hash, replace)
176
- return replace[hash[:@href]] if hash.has_key?(:@href)
211
+ def self.deep_replace_href(element, replace)
212
+ return element unless element.is_a?(Array) || element.is_a?(Hash)
177
213
 
178
- hash.keys.each do |key, value|
179
- hash[key] = deep_replace_href(hash[key], replace) if hash[key].is_a?(Hash)
214
+ if element.is_a?(Array) # Traverse arrays
215
+ return element.map{|x| deep_replace_href(x, replace)}
180
216
  end
181
217
 
182
- hash
218
+ if element.has_key?(:@href) # Replace needle and traverse replacement
219
+ return deep_replace_href(replace[element[:@href]], replace)
220
+ end
221
+
222
+ element.each do |key, value| # Traverse hashes
223
+ element[key] = deep_replace_href(value, replace)
224
+ end
225
+
226
+ element
183
227
  end
184
228
 
185
229
  private
230
+ def soap_action?
231
+ soap_action.present?
232
+ end
186
233
 
187
234
  def action_spec
188
235
  self.class.soap_actions[soap_action]
189
236
  end
190
237
 
238
+ def request_input_tag
239
+ action_spec[:request_tag]
240
+ end
241
+
191
242
  def soap_action
192
243
  request.env['wash_out.soap_action']
193
244
  end
194
245
 
195
246
  def xml_data
196
- xml_data = env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
197
- xml_data = xml_data.values_at(:body, :Body).compact.first
198
- xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym).compact.first || {}
247
+ envelope = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
248
+ xml_data = envelope.values_at(:body, :Body).compact.first || {}
249
+ return xml_data if soap_config.wsdl_style == "document"
250
+ xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym, request_input_tag.to_sym).compact.first || {}
251
+ end
252
+
253
+ def xml_header_data
254
+ envelope = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
255
+ header_data = envelope.values_at(:header, :Header).compact.first || {}
199
256
  end
200
257
 
201
258
  end
@@ -7,6 +7,7 @@ module WashOut
7
7
  attr_accessor :multiplied
8
8
  attr_accessor :value
9
9
  attr_accessor :source_class
10
+ attr_accessor :soap_config
10
11
 
11
12
  # Defines a WSDL parameter with name +name+ and type specifier +type+.
12
13
  # The type specifier format is described in #parse_def.
@@ -63,6 +64,7 @@ module WashOut
63
64
  operation = case type
64
65
  when 'string'; :to_s
65
66
  when 'integer'; :to_i
67
+ when 'long'; :to_i
66
68
  when 'double'; :to_f
67
69
  when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat}
68
70
  when 'date'; :to_date
@@ -160,10 +162,19 @@ module WashOut
160
162
  def flat_copy
161
163
  copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied)
162
164
  copy.raw_name = raw_name
163
- copy.source_class = copy.source_class
165
+ copy.source_class = source_class
164
166
  copy
165
167
  end
166
168
 
169
+ def attribute?
170
+ name[0] == "@"
171
+ end
172
+
173
+ def attr_name
174
+ raise 'Not attribute' unless attribute?
175
+ name[1..-1]
176
+ end
177
+
167
178
  private
168
179
 
169
180
  # Used to load an entire structure.
@@ -178,7 +189,8 @@ module WashOut
178
189
  # RUBY18 Enumerable#each_with_object is better, but 1.9 only.
179
190
  @map.map do |param|
180
191
  if data.has_key? param.raw_name
181
- struct[param.raw_name] = yield param, data, param.raw_name
192
+ param_name = param.attribute? ? param.attr_name : param.raw_name
193
+ struct[param_name] = yield param, data, param.raw_name
182
194
  end
183
195
  end
184
196
 
@@ -4,6 +4,37 @@ module WashOut
4
4
  # This class is a Rack middleware used to route SOAP requests to a proper
5
5
  # action of a given SOAP controller.
6
6
  class Router
7
+ def self.lookup_soap_routes(controller_name, routes, path=[], &block)
8
+ routes.each do |x|
9
+ defaults = x.defaults
10
+ defaults = defaults[:defaults] if defaults.include?(:defaults) # Rails 5
11
+ if defaults[:controller] == controller_name && defaults[:action] == 'soap'
12
+ yield path+[x]
13
+ end
14
+
15
+ app = x.app
16
+ app = app.app if app.respond_to?(:app)
17
+ if app.respond_to?(:routes)
18
+ lookup_soap_routes(controller_name, app.routes.routes, path+[x], &block)
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.url(request, controller_name)
24
+ lookup_soap_routes(controller_name, Rails.application.routes.routes) do |routes|
25
+
26
+ path = if routes.first.respond_to?(:optimized_path) # Rails 4
27
+ routes.map(&:optimized_path)
28
+ elsif routes.first.path.respond_to?(:build_formatter) # Rails 5
29
+ routes.map{|x| x.path.build_formatter.evaluate(nil)}
30
+ else
31
+ routes.map{|x| x.format({})} # Rails 3.2
32
+ end
33
+
34
+ return request.protocol + request.host_with_port + path.flatten.join('')
35
+ end
36
+ end
37
+
7
38
  def initialize(controller_name)
8
39
  @controller_name = "#{controller_name.to_s}_controller".camelize
9
40
  end
@@ -17,15 +48,13 @@ module WashOut
17
48
 
18
49
  soap_action = controller.soap_config.soap_action_routing ? env['HTTP_SOAPACTION'].to_s.gsub(/^"(.*)"$/, '\1')
19
50
  : ''
20
-
21
51
  if soap_action.blank?
22
52
  parsed_soap_body = nori(controller.soap_config.snakecase_input).parse(soap_body env)
23
53
  return nil if parsed_soap_body.blank?
24
54
 
25
- soap_action = parsed_soap_body
26
- .values_at(:envelope, :Envelope).compact.first
27
- .values_at(:body, :Body).compact.first
28
- .keys.first.to_s
55
+ soap_action = parsed_soap_body.values_at(:envelope, :Envelope).try(:compact).try(:first)
56
+ soap_action = soap_action.values_at(:body, :Body).try(:compact).try(:first) if soap_action
57
+ soap_action = soap_action.keys.first.to_s if soap_action
29
58
  end
30
59
 
31
60
  # RUBY18 1.8 does not have force_encoding.
@@ -52,18 +81,17 @@ module WashOut
52
81
  end
53
82
 
54
83
  def soap_body(env)
55
- # Don't let nobody intercept us ^_^
56
- env['rack.input'].rewind if env['rack.input'].respond_to?(:rewind)
57
-
58
- env['rack.input'].respond_to?(:string) ? env['rack.input'].string
59
- : env['rack.input'].read
84
+ body = env['rack.input']
85
+ body.rewind if body.respond_to? :rewind
86
+ body.respond_to?(:string) ? body.string : body.read
87
+ ensure
88
+ body.rewind if body.respond_to? :rewind
60
89
  end
61
90
 
62
91
  def parse_soap_parameters(env)
63
92
  return env['wash_out.soap_data'] if env['wash_out.soap_data']
64
-
65
93
  env['wash_out.soap_data'] = nori(controller.soap_config.snakecase_input).parse(soap_body env)
66
- references = WashOut::Dispatcher.deep_select(env['wash_out.soap_data']){|k,v| v.is_a?(Hash) && v.has_key?(:@id)}
94
+ references = WashOut::Dispatcher.deep_select(env['wash_out.soap_data']){|v| v.is_a?(Hash) && v.has_key?(:@id)}
67
95
 
68
96
  unless references.blank?
69
97
  replaces = {}; references.each{|r| replaces['#'+r[:@id]] = r}
@@ -76,19 +104,21 @@ module WashOut
76
104
  def call(env)
77
105
  @controller = @controller_name.constantize
78
106
 
79
- soap_action = parse_soap_action(env)
80
- return [200, {}, ['OK']] if soap_action.blank?
107
+ soap_action = parse_soap_action(env)
81
108
 
82
- soap_parameters = parse_soap_parameters(env)
83
-
84
- action_spec = controller.soap_actions[soap_action]
85
-
86
- if action_spec
87
- action = action_spec[:to]
109
+ action = if soap_action.blank?
110
+ '_invalid_request'
88
111
  else
89
- action = '_invalid_action'
112
+ soap_parameters = parse_soap_parameters(env)
113
+ action_spec = controller.soap_actions[soap_action]
114
+
115
+ if action_spec
116
+ action_spec[:to]
117
+ else
118
+ '_invalid_action'
119
+ end
90
120
  end
91
-
121
+ env["action_dispatch.request.content_type"] = Mime[:soap]
92
122
  controller.action(action).call(env)
93
123
  end
94
124
  end
data/lib/wash_out/soap.rb CHANGED
@@ -13,7 +13,15 @@ module WashOut
13
13
  #
14
14
  # An optional option :to can be passed to allow for names of SOAP actions
15
15
  # which are not valid Ruby function names.
16
+ # There is also an optional :header_return option to specify the format of the
17
+ # SOAP response's header tag (<env:Header></env:Header>). If unspecified, there will
18
+ # be no header tag in the response.
16
19
  def soap_action(action, options={})
20
+ if options[:as].present?
21
+ options[:to] ||= action
22
+ action = options[:as]
23
+ end
24
+
17
25
  if action.is_a?(Symbol)
18
26
  if soap_config.camelize_wsdl.to_s == 'lower'
19
27
  options[:to] ||= action.to_s
@@ -28,9 +36,12 @@ module WashOut
28
36
  default_response_tag = soap_config.camelize_wsdl ? 'Response' : '_response'
29
37
  default_response_tag = action+default_response_tag
30
38
 
39
+
31
40
  self.soap_actions[action] = options.merge(
32
41
  :in => WashOut::Param.parse_def(soap_config, options[:args]),
42
+ :request_tag => options[:as] || action,
33
43
  :out => WashOut::Param.parse_def(soap_config, options[:return]),
44
+ :header_out => options[:header_return].present? ? WashOut::Param.parse_def(soap_config, options[:header_return]) : nil,
34
45
  :to => options[:to] || action,
35
46
  :response_tag => options[:response_tag] || default_response_tag
36
47
  )
@@ -1,3 +1,3 @@
1
1
  module WashOut
2
- VERSION = "0.9.2"
2
+ VERSION = "0.11.0"
3
3
  end
data/lib/wash_out/wsse.rb CHANGED
@@ -49,8 +49,8 @@ module WashOut
49
49
  def matches_expected_digest?(password)
50
50
  nonce = @username_token.values_at(:nonce, :Nonce).compact.first
51
51
  timestamp = @username_token.values_at(:created, :Created).compact.first
52
-
53
52
  return false if nonce.nil? || timestamp.nil?
53
+ timestamp = timestamp.to_datetime
54
54
 
55
55
  # Token should not be accepted if timestamp is older than 5 minutes ago
56
56
  # http://www.oasis-open.org/committees/download.php/16782/wss-v1.1-spec-os-UsernameTokenProfile.pdf
@@ -62,11 +62,11 @@ module WashOut
62
62
  flavors = Array.new
63
63
 
64
64
  # Ruby / Savon
65
- token = nonce + timestamp.to_s + expected_password
65
+ token = nonce + timestamp.strftime("%Y-%m-%dT%H:%M:%SZ") + expected_password
66
66
  flavors << Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
67
67
 
68
68
  # Java
69
- token = Base64.decode64(nonce) + timestamp.to_s + expected_password
69
+ token = Base64.decode64(nonce) + timestamp.strftime("%Y-%m-%dT%H:%M:%SZ") + expected_password
70
70
  flavors << Base64.encode64(Digest::SHA1.digest(token)).chomp!
71
71
 
72
72
  flavors.each do |f|
data/lib/wash_out.rb CHANGED
@@ -11,15 +11,28 @@ require 'wash_out/model'
11
11
  require 'wash_out/wsse'
12
12
  require 'wash_out/middleware'
13
13
 
14
+ module WashOut
15
+ def self.root
16
+ File.expand_path '../..', __FILE__
17
+ end
18
+ end
19
+
14
20
  module ActionDispatch::Routing
15
21
  class Mapper
16
22
  # Adds the routes for a SOAP endpoint at +controller+.
17
23
  def wash_out(controller_name, options={})
18
- options.each_with_index { |key, value| @scope[key] = value } if @scope
19
- controller_class_name = [options[:module], controller_name].compact.join("/")
24
+ if @scope
25
+ scope_frame = @scope.respond_to?(:frame) ? @scope.frame : @scope
26
+ options.each{ |key, value| scope_frame[key] = value }
27
+ end
20
28
 
21
- match "#{controller_name}/wsdl" => "#{controller_name}#_generate_wsdl", :via => :get, :format => false
22
- match "#{controller_name}/action" => WashOut::Router.new(controller_class_name), :via => [:get, :post], :defaults => { :controller => controller_class_name, :action => '_action' }, :format => false
29
+ controller_class_name = [scope_frame[:module], controller_name].compact.join("/").underscore
30
+
31
+ match "#{controller_name}/wsdl" => "#{controller_name}#_generate_wsdl", :via => :get, :format => false,
32
+ :as => "#{controller_class_name}_wsdl"
33
+ match "#{controller_name}/action" => WashOut::Router.new(controller_class_name), :via => [:get, :post],
34
+ :defaults => { :controller => controller_class_name, :action => 'soap' }, :format => false,
35
+ :as => "#{controller_class_name}_soap"
23
36
  end
24
37
  end
25
38
  end
@@ -31,7 +44,7 @@ ActionController::Renderers.add :soap do |what, options|
31
44
  _render_soap(what, options)
32
45
  end
33
46
 
34
- ActionController::Base.class_eval do
47
+ ActionController::Metal.class_eval do
35
48
 
36
49
  # Define a SOAP service. The function has no required +options+:
37
50
  # but allow any of :parser, :namespace, :wsdl_style, :snakecase_input,
@@ -45,3 +58,17 @@ ActionController::Base.class_eval do
45
58
  self.soap_config = options
46
59
  end
47
60
  end
61
+
62
+ if Rails::VERSION::MAJOR >= 5
63
+ module ActionController
64
+ module ApiRendering
65
+ include ActionView::Rendering
66
+ end
67
+ end
68
+
69
+ ActiveSupport.on_load :action_controller do
70
+ if self == ActionController::API
71
+ include ActionController::Helpers
72
+ end
73
+ end
74
+ end
@@ -26,4 +26,5 @@ Dummy::Application.configure do
26
26
 
27
27
  # Print deprecation notices to the stderr
28
28
  config.active_support.deprecation = :stderr
29
+ config.active_support.test_order = :random
29
30
  end
@@ -0,0 +1,19 @@
1
+ <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
2
+ <s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
5
+ <parent href="#id1" />
6
+ <q2:child id="id1" xsi:type="q2:child" xmlns:q2="urn:FakeService">
7
+ <first_list href="#id2" />
8
+ <second_list href="#id3" />
9
+ </q2:child>
10
+ <q3:Array id="id2" q3:arrayType="xsd:int[1]" xmlns:q3="http://schemas.xmlsoap.org/soap/encoding/">
11
+ <Item>1</Item>
12
+ <Item>2</Item>
13
+ </q3:Array>
14
+ <q4:Array id="id3" q4:arrayType="xsd:int[1]" xmlns:q4="http://schemas.xmlsoap.org/soap/encoding/">
15
+ <Item>11</Item>
16
+ <Item>22</Item>
17
+ </q4:Array>
18
+ </s:Body>
19
+ </s:Envelope>
@@ -0,0 +1,11 @@
1
+ <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
2
+ <s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
5
+ <list href="#id1" />
6
+ <q1:Array id="id1" q1:arrayType="xsd:int[1]" xmlns:q1="http://schemas.xmlsoap.org/soap/encoding/">
7
+ <Item>1</Item>
8
+ <Item>2</Item>
9
+ </q1:Array>
10
+ </s:Body>
11
+ </s:Envelope>
@@ -0,0 +1,16 @@
1
+ <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
2
+ <s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
5
+ <first_list href="#id1" />
6
+ <second_list href="#id2" />
7
+ <q1:Array id="id1" q1:arrayType="xsd:int[1]" xmlns:q1="http://schemas.xmlsoap.org/soap/encoding/">
8
+ <Item>1</Item>
9
+ <Item>2</Item>
10
+ </q1:Array>
11
+ <q2:Array id="id2" q2:arrayType="xsd:int[1]" xmlns:q2="http://schemas.xmlsoap.org/soap/encoding/">
12
+ <Item>11</Item>
13
+ <Item>22</Item>
14
+ </q2:Array>
15
+ </s:Body>
16
+ </s:Envelope>