wash_out 0.9.2 → 0.11.0.beta.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +31 -4
  4. data/Appraisals +11 -10
  5. data/Gemfile +1 -1
  6. data/README.md +41 -7
  7. data/Rakefile +6 -7
  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 +0 -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 +0 -0
  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/lib/wash_out/dispatcher.rb +68 -38
  21. data/lib/wash_out/param.rb +14 -2
  22. data/lib/wash_out/router.rb +40 -21
  23. data/lib/wash_out/soap.rb +1 -0
  24. data/lib/wash_out/version.rb +1 -1
  25. data/lib/wash_out/wsse.rb +3 -3
  26. data/lib/wash_out.rb +17 -4
  27. data/spec/dummy/config/environments/test.rb +1 -0
  28. data/spec/fixtures/nested_refs_to_arrays.xml +19 -0
  29. data/spec/fixtures/ref_to_one_array.xml +11 -0
  30. data/spec/fixtures/refs_to_arrays.xml +16 -0
  31. data/spec/lib/wash_out/dispatcher_spec.rb +124 -17
  32. data/spec/lib/wash_out/middleware_spec.rb +8 -8
  33. data/spec/lib/wash_out/param_spec.rb +43 -11
  34. data/spec/lib/wash_out/router_spec.rb +33 -5
  35. data/spec/lib/wash_out/type_spec.rb +9 -9
  36. data/spec/lib/wash_out_spec.rb +160 -102
  37. data/spec/spec_helper.rb +24 -4
  38. metadata +19 -11
@@ -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,27 @@ 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
+ @_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 _strip_empty_nodes(params, hash)
39
+ hash.keys.each do |key|
40
+ param = params.detect { |a| a.raw_name.to_s == key.to_s }
41
+ next if !(param && hash[key].is_a?(Hash))
42
+
43
+ value = hash[key].delete_if do |k, _|
44
+ k.to_s[0] == '@' && !param.map.detect { |a| a.raw_name.to_s == k.to_s }
46
45
  end
47
46
 
48
- hash
49
- }
50
- @_params = _load_params(action_spec[:in], strip_empty_nodes.call(xml_data))
47
+ if value.length > 0
48
+ hash[key] = _strip_empty_nodes param.map, value
49
+ else
50
+ hash[key] = nil
51
+ end
52
+ end
53
+
54
+ hash
51
55
  end
52
56
 
53
57
  # Creates the final parameter hash based on the request spec and xml_data from the request
@@ -64,12 +68,11 @@ module WashOut
64
68
 
65
69
  # This action generates the WSDL for defined SOAP methods.
66
70
  def _generate_wsdl
67
-
68
71
  @map = self.class.soap_actions
69
72
  @namespace = soap_config.namespace
70
- @name = controller_path.gsub('/', '_')
73
+ @name = controller_path
71
74
 
72
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/wsdl", :layout => false,
75
+ render :template => "wash_out/#{soap_config.wsdl_style}/wsdl", :layout => false,
73
76
  :content_type => 'text/xml'
74
77
  end
75
78
 
@@ -124,7 +127,7 @@ module WashOut
124
127
  return result_spec
125
128
  }
126
129
 
127
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/response",
130
+ render :template => "wash_out/#{soap_config.wsdl_style}/response",
128
131
  :layout => false,
129
132
  :locals => { :result => inject.call(result, @action_spec[:out]) },
130
133
  :content_type => 'text/xml'
@@ -135,6 +138,10 @@ module WashOut
135
138
  render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out.soap_action']}")
136
139
  end
137
140
 
141
+ def _invalid_request
142
+ render_soap_error("Invalid SOAP request")
143
+ end
144
+
138
145
  def _catch_soap_errors
139
146
  yield
140
147
  rescue SOAPError => error
@@ -146,56 +153,79 @@ module WashOut
146
153
  # Rails do not support sequental rescue_from handling, that is, rescuing an
147
154
  # exception from a rescue_from handler. Hence this function is a public API.
148
155
  def render_soap_error(message, code=nil)
149
- render :template => "wash_with_soap/#{soap_config.wsdl_style}/error", :status => 500,
156
+ render :template => "wash_out/#{soap_config.wsdl_style}/error", :status => 500,
150
157
  :layout => false,
151
158
  :locals => { :error_message => message, :error_code => (code || 'Server') },
152
159
  :content_type => 'text/xml'
153
160
  end
154
161
 
155
162
  def self.included(controller)
156
- controller.send :around_filter, :_catch_soap_errors
163
+ entity = if defined?(Rails::VERSION::MAJOR) && (Rails::VERSION::MAJOR >= 4)
164
+ 'action'
165
+ else
166
+ 'filter'
167
+ end
168
+
169
+ controller.send :"around_#{entity}", :_catch_soap_errors
157
170
  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
171
+ controller.send :"before_#{entity}", :_authenticate_wsse, :if => :soap_action?
172
+ controller.send :"before_#{entity}", :_map_soap_parameters, :if => :soap_action?
173
+ controller.send :"skip_before_#{entity}", :verify_authenticity_token
163
174
  end
164
175
 
165
- def self.deep_select(hash, result=[], &blk)
166
- result += Hash[hash.select(&blk)].values
176
+ def self.deep_select(collection, result=[], &blk)
177
+ values = collection.respond_to?(:values) ? collection.values : collection
178
+ result += values.select(&blk)
167
179
 
168
- hash.each do |key, value|
169
- result = deep_select(value, result, &blk) if value.is_a? Hash
180
+ values.each do |value|
181
+ if value.is_a?(Hash) || value.is_a?(Array)
182
+ result = deep_select(value, result, &blk)
183
+ end
170
184
  end
171
185
 
172
186
  result
173
187
  end
174
188
 
175
- def self.deep_replace_href(hash, replace)
176
- return replace[hash[:@href]] if hash.has_key?(:@href)
189
+ def self.deep_replace_href(element, replace)
190
+ return element unless element.is_a?(Array) || element.is_a?(Hash)
177
191
 
178
- hash.keys.each do |key, value|
179
- hash[key] = deep_replace_href(hash[key], replace) if hash[key].is_a?(Hash)
192
+ if element.is_a?(Array) # Traverse arrays
193
+ return element.map{|x| deep_replace_href(x, replace)}
180
194
  end
181
195
 
182
- hash
196
+ if element.has_key?(:@href) # Replace needle and traverse replacement
197
+ return deep_replace_href(replace[element[:@href]], replace)
198
+ end
199
+
200
+ element.each do |key, value| # Traverse hashes
201
+ element[key] = deep_replace_href(value, replace)
202
+ end
203
+
204
+ element
183
205
  end
184
206
 
185
207
  private
208
+ def soap_action?
209
+ soap_action.present?
210
+ end
186
211
 
187
212
  def action_spec
188
213
  self.class.soap_actions[soap_action]
189
214
  end
190
215
 
216
+ def request_input_tag
217
+ action_spec[:request_tag]
218
+ end
219
+
191
220
  def soap_action
192
221
  request.env['wash_out.soap_action']
193
222
  end
194
223
 
195
224
  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 || {}
225
+ xml_data = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
226
+ xml_data = xml_data.values_at(:body, :Body).compact.first || {}
227
+ return xml_data if soap_config.wsdl_style == "document"
228
+ xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym, request_input_tag.to_sym).compact.first || {}
199
229
  end
200
230
 
201
231
  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,26 @@ 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.url(request, controller_name)
8
+ route = Rails.application.routes.routes.select do |x|
9
+ defaults = x.defaults
10
+ defaults = defaults[:defaults] if defaults.include?(:defaults) # Rails 5
11
+ defaults[:controller] == controller_name && defaults[:action] == 'soap'
12
+ end.first
13
+
14
+ path = if route.respond_to?(:optimized_path) # Rails 4
15
+ route.optimized_path
16
+ elsif route.path.respond_to?(:build_formatter) # Rails 5
17
+ route.path.build_formatter.evaluate(nil)
18
+ else
19
+ route.format({}) # Rails 3.2
20
+ end
21
+
22
+ path = path.join('') if path.is_a?(Array)
23
+
24
+ request.protocol + request.host_with_port + path
25
+ end
26
+
7
27
  def initialize(controller_name)
8
28
  @controller_name = "#{controller_name.to_s}_controller".camelize
9
29
  end
@@ -17,15 +37,13 @@ module WashOut
17
37
 
18
38
  soap_action = controller.soap_config.soap_action_routing ? env['HTTP_SOAPACTION'].to_s.gsub(/^"(.*)"$/, '\1')
19
39
  : ''
20
-
21
40
  if soap_action.blank?
22
41
  parsed_soap_body = nori(controller.soap_config.snakecase_input).parse(soap_body env)
23
42
  return nil if parsed_soap_body.blank?
24
43
 
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
44
+ soap_action = parsed_soap_body.values_at(:envelope, :Envelope).try(:compact).try(:first)
45
+ soap_action = soap_action.values_at(:body, :Body).try(:compact).try(:first) if soap_action
46
+ soap_action = soap_action.keys.first.to_s if soap_action
29
47
  end
30
48
 
31
49
  # RUBY18 1.8 does not have force_encoding.
@@ -52,18 +70,17 @@ module WashOut
52
70
  end
53
71
 
54
72
  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
73
+ body = env['rack.input']
74
+ body.rewind if body.respond_to? :rewind
75
+ body.respond_to?(:string) ? body.string : body.read
76
+ ensure
77
+ body.rewind if body.respond_to? :rewind
60
78
  end
61
79
 
62
80
  def parse_soap_parameters(env)
63
81
  return env['wash_out.soap_data'] if env['wash_out.soap_data']
64
-
65
82
  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)}
83
+ references = WashOut::Dispatcher.deep_select(env['wash_out.soap_data']){|v| v.is_a?(Hash) && v.has_key?(:@id)}
67
84
 
68
85
  unless references.blank?
69
86
  replaces = {}; references.each{|r| replaces['#'+r[:@id]] = r}
@@ -76,17 +93,19 @@ module WashOut
76
93
  def call(env)
77
94
  @controller = @controller_name.constantize
78
95
 
79
- soap_action = parse_soap_action(env)
80
- return [200, {}, ['OK']] if soap_action.blank?
81
-
82
- soap_parameters = parse_soap_parameters(env)
83
-
84
- action_spec = controller.soap_actions[soap_action]
96
+ soap_action = parse_soap_action(env)
85
97
 
86
- if action_spec
87
- action = action_spec[:to]
98
+ action = if soap_action.blank?
99
+ '_invalid_request'
88
100
  else
89
- action = '_invalid_action'
101
+ soap_parameters = parse_soap_parameters(env)
102
+ action_spec = controller.soap_actions[soap_action]
103
+
104
+ if action_spec
105
+ action_spec[:to]
106
+ else
107
+ '_invalid_action'
108
+ end
90
109
  end
91
110
 
92
111
  controller.action(action).call(env)
data/lib/wash_out/soap.rb CHANGED
@@ -30,6 +30,7 @@ module WashOut
30
30
 
31
31
  self.soap_actions[action] = options.merge(
32
32
  :in => WashOut::Param.parse_def(soap_config, options[:args]),
33
+ :request_tag => options[:as] || action,
33
34
  :out => WashOut::Param.parse_def(soap_config, options[:return]),
34
35
  :to => options[:to] || action,
35
36
  :response_tag => options[:response_tag] || default_response_tag
@@ -1,3 +1,3 @@
1
1
  module WashOut
2
- VERSION = "0.9.2"
2
+ VERSION = "0.11.0.beta.1"
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
28
+
29
+ controller_class_name = [scope_frame[:module], controller_name].compact.join("/").underscore
20
30
 
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
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
@@ -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>
@@ -12,20 +12,127 @@ describe WashOut::Dispatcher do
12
12
  end
13
13
  end
14
14
 
15
- it "finds nested hashes" do
16
- WashOut::Dispatcher.deep_select(:foo => 1){|k,v| k == :foo}.should == [1]
17
- WashOut::Dispatcher.deep_select({:foo => {:foo => 1}}){|k,v| k == :foo}.should == [{:foo => 1}, 1]
15
+ describe ".deep_select" do
16
+ blk = lambda{|v| v.is_a?(Hash) && v.has_key?(:@id)}
17
+
18
+ it "find no elements if there aren't any ids" do
19
+ expect(WashOut::Dispatcher.deep_select({k: {v: :v2}}, &blk)).to eq []
20
+ end
21
+
22
+ it "finds elements with ids in a hash" do
23
+ expect(WashOut::Dispatcher.deep_select({k: {:@id => 5, x: :y}}, &blk)).to eq [{:@id => 5, x: :y}]
24
+ end
25
+
26
+ it "finds elements with ids in a array" do
27
+ expect(WashOut::Dispatcher.deep_select({k: [{:@id => 5, x: :y}]}, &blk)).to eq [{:@id => 5, x: :y}]
28
+ end
29
+
30
+ it "finds elements with ids in hashes" do
31
+ expect(WashOut::Dispatcher.deep_select(
32
+ {
33
+ k: {:@id => 5, x: :y},
34
+ k2: {:@id => 6, n: :m}
35
+ }, &blk)).to eq [{:@id => 5, x: :y}, {:@id => 6, n: :m}]
36
+ end
37
+
38
+ it "finds elements in a hash and in a array" do
39
+ expect(WashOut::Dispatcher.deep_select(
40
+ {
41
+ k: [{:@id => 5, x: :y}],
42
+ k2: {:@id => 6, n: :m}
43
+ }, &blk)).to contain_exactly({:@id => 5, x: :y}, {:@id => 6, n: :m})
44
+ end
45
+
46
+ it "finds elements with ids in multiple arrays" do
47
+ expect(WashOut::Dispatcher.deep_select(
48
+ {
49
+ k: [{:@id => 5, x: :y}],
50
+ k2: [{:@id => 6, n: :m}]
51
+ }, &blk)).to eq [{:@id => 5, x: :y}, {:@id => 6, n: :m}]
52
+ end
18
53
  end
19
54
 
20
- it "replaces nested hashed" do
21
- WashOut::Dispatcher.deep_replace_href({:foo => {:@href => 1}}, {1 => 2}).should == {:foo => 2}
22
- WashOut::Dispatcher.deep_replace_href({:bar => {:foo => {:@href => 1}}}, {1 => 2}).should == {:bar => {:foo => 2}}
55
+ describe ".deep_replace_href" do
56
+ it "replaces nested hashed" do
57
+ expect(WashOut::Dispatcher.deep_replace_href(
58
+ {:foo => {:@href => 1}},
59
+ {1 => 2})).to eq(
60
+ {:foo => 2}
61
+ )
62
+ end
63
+
64
+ it "replaces deeper nested hashes" do
65
+ expect(WashOut::Dispatcher.deep_replace_href(
66
+ {:bar => {:foo => {:@href => 1}}},
67
+ {1 => 2}
68
+ )).to eq(
69
+ {:bar => {:foo => 2}}
70
+ )
71
+ end
72
+
73
+ it "replace nested refs" do
74
+ hash = {fizz: {:@href => "#id4"}}
75
+ replaces = {
76
+ "#id4" => {:@href => "#id6"},
77
+ "#id6" => {foo: :bar}
78
+ }
79
+ expect(WashOut::Dispatcher.deep_replace_href(hash, replaces)).to eq({
80
+ fizz: {foo: :bar}})
81
+ end
82
+
83
+ it "replace really nested refs" do
84
+ hash = {fizz: {:@href => "#id4"}}
85
+ replaces = {
86
+ "#id4" => {:@href => "#id6"},
87
+ "#id6" => {:@href => "#id7"},
88
+ "#id7" => {foo: :bar}
89
+ }
90
+ expect(WashOut::Dispatcher.deep_replace_href(hash, replaces)).to eq({
91
+ fizz: {foo: :bar}})
92
+ end
93
+
94
+ it "replaces arrays in nested hashes" do
95
+ hash = {
96
+ fizz: {:@href => "#id4"},
97
+ Array: [
98
+ {Item: [{:@href => "#id6"}, {:@href => "#id7"}]},
99
+ {Item: {loo: :iioo}}
100
+ ]
101
+ }
102
+ replaces = {
103
+ "#id4" => {Item: [{:@href => "#id6"}, {:@href => "#id7"}]},
104
+ "#id6" => {foo: :bar},
105
+ "#id7" => {baz: :bats}
106
+ }
107
+ expect(WashOut::Dispatcher.deep_replace_href(hash, replaces)).to eq({
108
+ fizz: {Item: [
109
+ {foo: :bar},
110
+ {baz: :bats}
111
+ ]},
112
+ Array: [
113
+ {Item: [{foo: :bar}, {baz: :bats}]},
114
+ {Item: {loo: :iioo}}
115
+ ]
116
+ })
117
+ end
118
+
119
+ it "can traverse arrays that do not contain hashes" do
120
+ hash = {
121
+ fizz: {:@href => "#id1"},
122
+ }
123
+ replaces = {
124
+ "#id1" => {Item: ["1", "2"]},
125
+ }
126
+ expect(WashOut::Dispatcher.deep_replace_href(hash, replaces)).to eq({
127
+ fizz: {Item: ["1", "2"]},
128
+ })
129
+ end
23
130
  end
24
131
 
25
132
  xit "parses typical request" do
26
133
  dispatcher = Dispatcher.mock("<foo>1</foo>")
27
134
  dispatcher._parse_soap_parameters
28
- dispatcher.params.should == {:foo => "1"}
135
+ expect(dispatcher.params).to eq({:foo => "1"})
29
136
  end
30
137
 
31
138
  xit "parses href request" do
@@ -45,11 +152,11 @@ describe WashOut::Dispatcher do
45
152
  </root>
46
153
  XML
47
154
  dispatcher._parse_soap_parameters
48
- dispatcher.params[:root][:request][:entities].should == {
155
+ expect(dispatcher.params[:root][:request][:entities]).to eq({
49
156
  :foo => {:bar=>"1"},
50
157
  :sub => {:foo=>"1", :@id=>"id2"},
51
158
  :@id => "id1"
52
- }
159
+ })
53
160
  end
54
161
 
55
162
  describe "#_map_soap_parameters" do
@@ -57,13 +164,13 @@ describe WashOut::Dispatcher do
57
164
  let(:soap_config) { WashOut::SoapConfig.new(camelize_wsdl: false) }
58
165
 
59
166
  before do
60
- allow(dispatcher).to receive(:action_spec).and_return(in: WashOut::Param.parse_def(soap_config, {:empty => :string } ))
61
- allow(dispatcher).to receive(:xml_data).and_return(:empty => { :"@xsi:type" => "xsd:string" })
167
+ allow(dispatcher).to receive(:action_spec).and_return(in: WashOut::Param.parse_def(soap_config, { foo: { "@bar" => :string, empty: :string } } ))
168
+ allow(dispatcher).to receive(:xml_data).and_return(foo: { "@bar" => "buzz", empty: { :"@xsi:type" => "xsd:string" } })
62
169
  end
63
170
 
64
- it "should handle empty strings that have been parsed wrong by nori" do
171
+ it "should handle empty strings that have been parsed wrong by nori, but preserve attrs" do
65
172
  dispatcher._map_soap_parameters
66
- expect(dispatcher.params).to eq('empty' => nil)
173
+ expect(dispatcher.params).to eq("foo" => { "bar" => "buzz", "empty" => nil })
67
174
  end
68
175
  end
69
176
 
@@ -73,25 +180,25 @@ describe WashOut::Dispatcher do
73
180
  it "should load params for an array" do
74
181
  spec = WashOut::Param.parse_def(soap_config, {:my_array => [:integer] } )
75
182
  xml_data = {:my_array => [1, 2, 3]}
76
- dispatcher._load_params(spec, xml_data).should == {"my_array" => [1, 2, 3]}
183
+ expect(dispatcher._load_params(spec, xml_data)).to eq({"my_array" => [1, 2, 3]})
77
184
  end
78
185
 
79
186
  it "should load params for an empty array" do
80
187
  spec = WashOut::Param.parse_def(soap_config, {:my_array => [:integer] } )
81
188
  xml_data = {}
82
- dispatcher._load_params(spec, xml_data).should == {}
189
+ expect(dispatcher._load_params(spec, xml_data)).to eq({})
83
190
  end
84
191
 
85
192
  it "should load params for a nested array" do
86
193
  spec = WashOut::Param.parse_def(soap_config, {:nested => {:my_array => [:integer]}} )
87
194
  xml_data = {:nested => {:my_array => [1, 2, 3]}}
88
- dispatcher._load_params(spec, xml_data).should == {"nested" => {"my_array" => [1, 2, 3]}}
195
+ expect(dispatcher._load_params(spec, xml_data)).to eq({"nested" => {"my_array" => [1, 2, 3]}})
89
196
  end
90
197
 
91
198
  it "should load params for an empty nested array" do
92
199
  spec = WashOut::Param.parse_def(soap_config, {:nested => {:empty => [:integer] }} )
93
200
  xml_data = {:nested => nil}
94
- dispatcher._load_params(spec, xml_data).should == {"nested" => {}}
201
+ expect(dispatcher._load_params(spec, xml_data)).to eq({"nested" => {}})
95
202
  end
96
203
 
97
204
  end