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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +31 -4
- data/Appraisals +11 -10
- data/Gemfile +1 -1
- data/README.md +41 -7
- data/Rakefile +6 -7
- data/app/helpers/wash_out_helper.rb +59 -24
- data/app/views/{wash_with_soap → wash_out}/document/error.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/document/response.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/document/wsdl.builder +14 -14
- data/app/views/{wash_with_soap → wash_out}/rpc/error.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/rpc/response.builder +0 -0
- data/app/views/{wash_with_soap → wash_out}/rpc/wsdl.builder +15 -15
- data/gemfiles/rails_3.2.13.gemfile +21 -0
- data/gemfiles/rails_4.0.0.gemfile +20 -0
- data/gemfiles/rails_4.1.0.gemfile +20 -0
- data/gemfiles/rails_4.2.0.gemfile +20 -0
- data/gemfiles/rails_5.0.0.beta2.gemfile +19 -0
- data/lib/wash_out/dispatcher.rb +68 -38
- data/lib/wash_out/param.rb +14 -2
- data/lib/wash_out/router.rb +40 -21
- data/lib/wash_out/soap.rb +1 -0
- data/lib/wash_out/version.rb +1 -1
- data/lib/wash_out/wsse.rb +3 -3
- data/lib/wash_out.rb +17 -4
- data/spec/dummy/config/environments/test.rb +1 -0
- data/spec/fixtures/nested_refs_to_arrays.xml +19 -0
- data/spec/fixtures/ref_to_one_array.xml +11 -0
- data/spec/fixtures/refs_to_arrays.xml +16 -0
- data/spec/lib/wash_out/dispatcher_spec.rb +124 -17
- data/spec/lib/wash_out/middleware_spec.rb +8 -8
- data/spec/lib/wash_out/param_spec.rb +43 -11
- data/spec/lib/wash_out/router_spec.rb +33 -5
- data/spec/lib/wash_out/type_spec.rb +9 -9
- data/spec/lib/wash_out_spec.rb +160 -102
- data/spec/spec_helper.rb +24 -4
- metadata +19 -11
data/lib/wash_out/dispatcher.rb
CHANGED
@@ -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
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
73
|
+
@name = controller_path
|
71
74
|
|
72
|
-
render :template => "
|
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 => "
|
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 => "
|
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
|
-
|
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 :
|
159
|
-
|
160
|
-
controller.send :
|
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(
|
166
|
-
|
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
|
-
|
169
|
-
|
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(
|
176
|
-
return
|
189
|
+
def self.deep_replace_href(element, replace)
|
190
|
+
return element unless element.is_a?(Array) || element.is_a?(Hash)
|
177
191
|
|
178
|
-
|
179
|
-
|
192
|
+
if element.is_a?(Array) # Traverse arrays
|
193
|
+
return element.map{|x| deep_replace_href(x, replace)}
|
180
194
|
end
|
181
195
|
|
182
|
-
|
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
|
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
|
data/lib/wash_out/param.rb
CHANGED
@@ -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 =
|
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
|
-
|
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
|
|
data/lib/wash_out/router.rb
CHANGED
@@ -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
|
-
|
27
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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']){|
|
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
|
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
|
87
|
-
|
98
|
+
action = if soap_action.blank?
|
99
|
+
'_invalid_request'
|
88
100
|
else
|
89
|
-
|
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
|
data/lib/wash_out/version.rb
CHANGED
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.
|
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.
|
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
|
-
|
19
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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.
|
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].
|
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, {:
|
61
|
-
allow(dispatcher).to receive(:xml_data).and_return(:
|
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(
|
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).
|
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).
|
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).
|
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).
|
201
|
+
expect(dispatcher._load_params(spec, xml_data)).to eq({"nested" => {}})
|
95
202
|
end
|
96
203
|
|
97
204
|
end
|