wash_out 0.10.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a23248a1bd38c14fe7eae75c2589b4eb945687f6
4
- data.tar.gz: 5f806cadfebf37d0b46ee2d7f69d00266f0bae53
3
+ metadata.gz: 9d49adf22c0c16c3f0e8e3e2dcf694b7d6851920
4
+ data.tar.gz: c42b0da56ed90cd44e4d014aff450f3bef83325f
5
5
  SHA512:
6
- metadata.gz: 5879036aca9ca31804573e45c5cc3aa4f7ffdb5303c27ea195c322c9246afd1ecde6d518783cd6e13dcd49ccb7a29c99f7017bc28a8feb43fbb45afeb2988063
7
- data.tar.gz: d791bece6a7eb059ff83dbac5bce07ccd6e7dfc95c761579ff53ff97776dae4c7a5156b76997131f8de931b5263ba846545ec5018daa75eeb66faf985bbb2a6e
6
+ metadata.gz: 9f2dd4239ee3fa9e449bd0903c977b5f2457382ee363cb3d1404d102f5fd0b531fe8f0e32dcee5cc6a5ff14996d60b37bb565ad702b913ac2ed344fbde90ea83
7
+ data.tar.gz: c9045dd957a04e6751b9ed5ffc2a28fdc6c1d4a1c4eff7a57d8564699423a442a4975fe8c6c4cac7a451a19cd3eeda112ba8357cd0e19228d838e4d8ffcabdb4
data/.travis.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  script: bundle exec rspec
2
2
  gemfile:
3
- - gemfiles/rails_3.1.3.gemfile
4
- - gemfiles/rails_3.2.12.gemfile
3
+ - gemfiles/rails_3.2.13.gemfile
5
4
  - gemfiles/rails_4.0.0.gemfile
6
5
  - gemfiles/rails_4.1.0.gemfile
7
6
  - gemfiles/rails_4.2.0.gemfile
7
+ - gemfiles/rails_5.0.0.beta2.gemfile
8
8
  rvm:
9
9
  - 1.9.3
10
10
  - 2.0.0
@@ -15,12 +15,20 @@ rvm:
15
15
  matrix:
16
16
  exclude:
17
17
  - rvm: 2.2.4
18
- gemfile: gemfiles/rails_3.1.3.gemfile
19
- - rvm: 2.2.4
20
- gemfile: gemfiles/rails_3.2.12.gemfile
21
- - rvm: 2.3.0
22
- gemfile: gemfiles/rails_3.1.3.gemfile
18
+ gemfile: gemfiles/rails_3.2.13.gemfile
23
19
  - rvm: 2.3.0
24
- gemfile: gemfiles/rails_3.2.12.gemfile
20
+ gemfile: gemfiles/rails_3.2.13.gemfile
21
+ - rvm: 1.9.3
22
+ gemfile: gemfiles/rails_4.2.0.gemfile
23
+ - rvm: jruby
24
+ gemfile: gemfiles/rails_4.2.0.gemfile
25
+ - rvm: 1.9.3
26
+ gemfile: gemfiles/rails_5.0.0.beta2.gemfile
27
+ - rvm: 2.0.0
28
+ gemfile: gemfiles/rails_5.0.0.beta2.gemfile
29
+ - rvm: 2.1.8
30
+ gemfile: gemfiles/rails_5.0.0.beta2.gemfile
31
+ - rvm: jruby
32
+ gemfile: gemfiles/rails_5.0.0.beta2.gemfile
25
33
  before_install:
26
34
  - gem update bundler
data/Appraisals CHANGED
@@ -1,21 +1,24 @@
1
- appraise "rails-3.1.3" do
2
- gem "rails", "3.1.3"
3
- gem "test-unit"
4
- end
5
-
6
- appraise "rails-3.2.12" do
7
- gem "rails", "3.2.12"
1
+ appraise "rails-3.2.13" do
2
+ gem "rails", "3.2.13"
8
3
  gem "test-unit"
4
+ gem "listen", "< 3.1.0"
9
5
  end
10
6
 
11
7
  appraise "rails-4.0.0" do
12
8
  gem "rails", "4.0.0"
9
+ gem "listen", "< 3.1.0"
13
10
  end
14
11
 
15
12
  appraise "rails-4.1.0" do
16
13
  gem "rails", "4.1.0"
14
+ gem "listen", "< 3.1.0"
17
15
  end
18
16
 
19
17
  appraise "rails-4.2.0" do
20
18
  gem "rails", "4.2.0"
19
+ gem "listen", "< 3.1.0"
21
20
  end
21
+
22
+ appraise "rails-5.0.0.beta2" do
23
+ gem "rails", "5.0.0.beta2"
24
+ end
data/README.md CHANGED
@@ -10,11 +10,7 @@ But if you have a chance, please [http://stopsoap.com/](http://stopsoap.com/).
10
10
 
11
11
  ## Compatibility
12
12
 
13
- Rails >3.0 only. MRI 1.9, 2.0, JRuby (--1.9).
14
-
15
- Ruby 1.8 is not officially supported since 0.5.3. We will accept further compatibilty pull-requests but no upcoming versions will be tested against it.
16
-
17
- Rubinius support temporarily dropped since 0.6.2 due to Rails 4 incompatibility.
13
+ Rails 3.2.13 and higher (if you are using SOAP and still on Ruby 1.9 - that's just too much evil, sorry)
18
14
 
19
15
  ## Installation
20
16
 
@@ -75,7 +71,7 @@ class RumbasController < ApplicationController
75
71
  :args => { :data => [:integer] },
76
72
  :return => [:boolean]
77
73
  def integers_to_boolean
78
- render :soap => params[:data].map{|x| x ? 1 : 0}
74
+ render :soap => params[:data].map{|i| i > 0}
79
75
  end
80
76
 
81
77
  # Params from XML attributes;
@@ -160,7 +156,7 @@ inside separate classes for the complex ones. Here's the way to do that:
160
156
  class Fluffy < WashOut::Type
161
157
  map :universe => {
162
158
  :name => :string,
163
- :age => :int
159
+ :age => :integer
164
160
  }
165
161
  end
166
162
 
@@ -3,7 +3,11 @@ module WashOutHelper
3
3
  def wsdl_data_options(param)
4
4
  case controller.soap_config.wsdl_style
5
5
  when 'rpc'
6
- { :"xsi:type" => param.namespaced_type }
6
+ if param.map.present? || param.value
7
+ { :"xsi:type" => param.namespaced_type }
8
+ else
9
+ { :"xsi:nil" => true }
10
+ end
7
11
  when 'document'
8
12
  { }
9
13
  end
@@ -96,11 +100,11 @@ module WashOutHelper
96
100
  end
97
101
 
98
102
  def wsdl_occurence(param, inject, extend_with = {})
99
- data = !param.multiplied ? {} : {
100
- "#{'xsi:' if inject}minOccurs" => 0,
101
- "#{'xsi:' if inject}maxOccurs" => 'unbounded'
102
- }
103
-
103
+ data = {"#{'xsi:' if inject}nillable" => 'true'}
104
+ if param.multiplied
105
+ data["#{'xsi:' if inject}minOccurs"] = 0
106
+ data["#{'xsi:' if inject}maxOccurs"] = 'unbounded'
107
+ end
104
108
  extend_with.merge(data)
105
109
  end
106
110
  end
@@ -62,7 +62,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',
62
62
 
63
63
  xml.service :name => "service" do
64
64
  xml.port :name => "#{@name}_port", :binding => "tns:#{@name}_binding" do
65
- xml.tag! "soap:address", :location => send("#{@name}_action_url")
65
+ xml.tag! "soap:address", :location => WashOut::Router.url(request, @name)
66
66
  end
67
67
  end
68
68
  end
@@ -62,7 +62,7 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',
62
62
 
63
63
  xml.service :name => "service" do
64
64
  xml.port :name => "#{@name}_port", :binding => "tns:#{@name}_binding" do
65
- xml.tag! "soap:address", :location => send("#{@name}_action_url")
65
+ xml.tag! "soap:address", :location => WashOut::Router.url(request, @name)
66
66
  end
67
67
  end
68
68
  end
@@ -14,7 +14,8 @@ gem "tzinfo"
14
14
  gem "pry"
15
15
  gem "simplecov"
16
16
  gem "simplecov-summary"
17
- gem "rails", "3.2.12"
17
+ gem "rails", "3.2.13"
18
18
  gem "test-unit"
19
+ gem "listen", "< 3.1.0"
19
20
 
20
21
  gemspec :path => "../"
@@ -15,5 +15,6 @@ gem "pry"
15
15
  gem "simplecov"
16
16
  gem "simplecov-summary"
17
17
  gem "rails", "4.0.0"
18
+ gem "listen", "< 3.1.0"
18
19
 
19
20
  gemspec :path => "../"
@@ -15,5 +15,6 @@ gem "pry"
15
15
  gem "simplecov"
16
16
  gem "simplecov-summary"
17
17
  gem "rails", "4.1.0"
18
+ gem "listen", "< 3.1.0"
18
19
 
19
20
  gemspec :path => "../"
@@ -15,5 +15,6 @@ gem "pry"
15
15
  gem "simplecov"
16
16
  gem "simplecov-summary"
17
17
  gem "rails", "4.2.0"
18
+ gem "listen", "< 3.1.0"
18
19
 
19
20
  gemspec :path => "../"
@@ -14,7 +14,6 @@ gem "tzinfo"
14
14
  gem "pry"
15
15
  gem "simplecov"
16
16
  gem "simplecov-summary"
17
- gem "rails", "3.1.3"
18
- gem "test-unit"
17
+ gem "rails", "5.0.0.beta2"
19
18
 
20
19
  gemspec :path => "../"
@@ -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
@@ -69,10 +68,9 @@ module WashOut
69
68
 
70
69
  # This action generates the WSDL for defined SOAP methods.
71
70
  def _generate_wsdl
72
-
73
71
  @map = self.class.soap_actions
74
72
  @namespace = soap_config.namespace
75
- @name = controller_path.gsub('/', '_')
73
+ @name = controller_path
76
74
 
77
75
  render :template => "wash_out/#{soap_config.wsdl_style}/wsdl", :layout => false,
78
76
  :content_type => 'text/xml'
@@ -140,6 +138,10 @@ module WashOut
140
138
  render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out.soap_action']}")
141
139
  end
142
140
 
141
+ def _invalid_request
142
+ render_soap_error("Invalid SOAP request")
143
+ end
144
+
143
145
  def _catch_soap_errors
144
146
  yield
145
147
  rescue SOAPError => error
@@ -158,36 +160,54 @@ module WashOut
158
160
  end
159
161
 
160
162
  def self.included(controller)
161
- 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
162
170
  controller.send :helper, :wash_out
163
- controller.send :before_filter, :_authenticate_wsse, :except => [
164
- :_generate_wsdl, :_invalid_action ]
165
- controller.send :before_filter, :_map_soap_parameters, :except => [
166
- :_generate_wsdl, :_invalid_action ]
167
- 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
168
174
  end
169
175
 
170
- def self.deep_select(hash, result=[], &blk)
171
- 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)
172
179
 
173
- hash.each do |key, value|
174
- 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
175
184
  end
176
185
 
177
186
  result
178
187
  end
179
188
 
180
- def self.deep_replace_href(hash, replace)
181
- 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)
182
191
 
183
- hash.keys.each do |key, value|
184
- 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)}
185
194
  end
186
195
 
187
- 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
188
205
  end
189
206
 
190
207
  private
208
+ def soap_action?
209
+ soap_action.present?
210
+ end
191
211
 
192
212
  def action_spec
193
213
  self.class.soap_actions[soap_action]
@@ -202,7 +222,7 @@ module WashOut
202
222
  end
203
223
 
204
224
  def xml_data
205
- xml_data = env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
225
+ xml_data = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
206
226
  xml_data = xml_data.values_at(:body, :Body).compact.first || {}
207
227
  return xml_data if soap_config.wsdl_style == "document"
208
228
  xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym, request_input_tag.to_sym).compact.first || {}
@@ -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.
@@ -61,9 +79,8 @@ module WashOut
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)
@@ -1,3 +1,3 @@
1
1
  module WashOut
2
- VERSION = "0.10.0"
2
+ VERSION = "0.11.0.beta.1"
3
3
  end
data/lib/wash_out.rb CHANGED
@@ -21,11 +21,18 @@ module ActionDispatch::Routing
21
21
  class Mapper
22
22
  # Adds the routes for a SOAP endpoint at +controller+.
23
23
  def wash_out(controller_name, options={})
24
- options.each_with_index { |key, value| @scope[key] = value } if @scope
25
- 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
26
28
 
27
- match "#{controller_name}/wsdl" => "#{controller_name}#_generate_wsdl", :via => :get, :format => false
28
- 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"
29
36
  end
30
37
  end
31
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,14 +12,121 @@ describe WashOut::Dispatcher do
12
12
  end
13
13
  end
14
14
 
15
- it "finds nested hashes" do
16
- expect(WashOut::Dispatcher.deep_select(:foo => 1){|k,v| k == :foo}).to eq [1]
17
- expect(WashOut::Dispatcher.deep_select({:foo => {:foo => 1}}){|k,v| k == :foo}).to eq([{: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
- expect(WashOut::Dispatcher.deep_replace_href({:foo => {:@href => 1}}, {1 => 2})).to eq({:foo => 2})
22
- expect(WashOut::Dispatcher.deep_replace_href({:bar => {:foo => {:@href => 1}}}, {1 => 2})).to eq({: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
@@ -11,12 +11,40 @@ describe WashOut::Router do
11
11
  env = {}
12
12
  env['REQUEST_METHOD'] = 'GET'
13
13
  env['rack.input'] = double 'basic-rack-input', {:string => ''}
14
- result = WashOut::Router.new('Api').call env
14
+ result = WashOut::Router.new('Route::Space::Api').call env
15
15
 
16
- expect(result[0]).to eq(200)
17
- #expect(result[1]['Content-Type']).to eq('text/xml')
16
+ expect(result[0]).to eq(500)
17
+ expect(result[1]['Content-Type']).to eq('text/xml; charset=utf-8')
18
+ end
19
+
20
+ def parse_soap_params_from_xml(filename)
21
+ xml = File.read(File.expand_path("../../../fixtures/#{filename}", __FILE__))
22
+ env = {'rack.input' => StringIO.new(xml)}
23
+
24
+ router = WashOut::Router.new('')
25
+ controller = double("controller", soap_config: WashOut::SoapConfig.new)
26
+ allow(router).to receive(:controller).and_return(controller)
27
+
28
+ router.parse_soap_parameters(env)[:Envelope][:Body]
29
+ end
30
+
31
+ it "returns refs to arrays correctly" do
32
+ body = parse_soap_params_from_xml('ref_to_one_array.xml')
33
+
34
+ expect(body[:list][:Item]).to eq(["1", "2"])
35
+ end
36
+
37
+ it "returns refs to multiple arrays correctly" do
38
+ body = parse_soap_params_from_xml('refs_to_arrays.xml')
39
+
40
+ expect(body[:first_list][:Item]).to eq(["1", "2"])
41
+ expect(body[:second_list][:Item]).to eq(["11", "22"])
42
+ end
43
+
44
+ it "returns nested refs to multiple arrays correctly" do
45
+ body = parse_soap_params_from_xml('nested_refs_to_arrays.xml')
18
46
 
19
- msg = result[2][0]
20
- expect(msg).to eq('OK')
47
+ expect(body[:parent][:first_list][:Item]).to eq(["1", "2"])
48
+ expect(body[:parent][:second_list][:Item]).to eq(["11", "22"])
21
49
  end
22
50
  end
@@ -12,17 +12,19 @@ describe WashOut do
12
12
  )
13
13
  end
14
14
 
15
- def savon(method, message={}, &block)
15
+ def savon(method, message={}, hashify=true, &block)
16
16
  message = {:value => message} unless message.is_a?(Hash)
17
17
 
18
- savon = Savon::Client.new(:log => false, :wsdl => 'http://app/api/wsdl', &block)
19
- savon.call(method, :message => message).to_hash
18
+ savon = Savon::Client.new(:log => false, :wsdl => 'http://app/route/api/wsdl', &block)
19
+ result = savon.call(method, :message => message)
20
+ result = result.to_hash if hashify
21
+ result
20
22
  end
21
23
 
22
24
  def savon!(method, message={}, &block)
23
25
  message = {:value => message} unless message.is_a?(Hash)
24
26
 
25
- savon = Savon::Client.new(:log => true, :wsdl => 'http://app/api/wsdl', &block)
27
+ savon = Savon::Client.new(:log => true, :wsdl => 'http://app/route/api/wsdl', &block)
26
28
  savon.call(method, :message => message).to_hash
27
29
  end
28
30
 
@@ -59,7 +61,7 @@ describe WashOut do
59
61
  :return => { :circle2 => { :y => :integer } }
60
62
  end
61
63
 
62
- HTTPI.get("http://app/api/wsdl").body
64
+ HTTPI.get("http://app/route/api/wsdl").body
63
65
  end
64
66
 
65
67
  let :xml do
@@ -84,6 +86,13 @@ describe WashOut do
84
86
 
85
87
  expect(x[:'@min_occurs']).to eq "0"
86
88
  expect(x[:'@max_occurs']).to eq "unbounded"
89
+ expect(x[:'@nillable']).to eq "true"
90
+ end
91
+
92
+ it "adds nillable to all type definitions" do
93
+ types = xml[:definitions][:message].map { |d| d[:part] }.compact
94
+ nillable = types.map { |t| t[:"@xsi:nillable"] }
95
+ expect(nillable.all? { |v| v == "true" }).to be true
87
96
  end
88
97
  end
89
98
 
@@ -109,7 +118,7 @@ describe WashOut do
109
118
  </env:Envelope>
110
119
  XML
111
120
 
112
- expect(HTTPI.post("http://app/api/action", request).body).to eq <<-XML
121
+ expect(HTTPI.post("http://app/route/api/action", request).body).to eq <<-XML
113
122
  <?xml version="1.0" encoding="UTF-8"?>
114
123
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="false">
115
124
  <soap:Body>
@@ -145,6 +154,17 @@ describe WashOut do
145
154
  to eq "42"
146
155
  end
147
156
 
157
+ it "shows date in correct format" do
158
+ mock_controller do
159
+ soap_action "answer", :args => {}, :return => {:a => :date}
160
+ def answer
161
+ render :soap => {:a => DateTime.new(2000, 1, 1)}
162
+ end
163
+ end
164
+ result = Hash.from_xml savon(:answer, {}, false).http.body
165
+ expect(result['Envelope']['Body']['answerResponse']['A']).to eq '2000-01-01T00:00:00+00:00'
166
+ end
167
+
148
168
  it "accept empty parameter" do
149
169
  mock_controller do
150
170
  soap_action "answer", :args => {:a => :string}, :return => {:a => :string}
@@ -152,8 +172,7 @@ describe WashOut do
152
172
  render :soap => {:a => params[:a]}
153
173
  end
154
174
  end
155
- expect(savon(:answer, :a => '')[:answer_response][:a]).
156
- to eq({:"@xsi:type"=>"xsd:string"})
175
+ expect(savon(:answer, :a => '')[:answer_response][:a]).to be_nil
157
176
  end
158
177
 
159
178
  it "accept one parameter" do
@@ -410,10 +429,20 @@ describe WashOut do
410
429
  end
411
430
  end
412
431
 
413
- expect(savon(:rocknroll)[:rocknroll_response][:my_value]).
414
- to eq({
415
- :"@xsi:type" => "tns:MyValue"
416
- })
432
+ expect(savon(:rocknroll)[:rocknroll_response][:my_value]).to be_nil
433
+ end
434
+
435
+ it "responds with missing parameters" do
436
+ mock_controller do
437
+ soap_action "rocknroll",
438
+ args: nil,
439
+ return: {my_value: :integer}
440
+ def rocknroll
441
+ render soap: {my_value: nil}
442
+ end
443
+ end
444
+
445
+ expect(savon(:rocknroll)[:rocknroll_response][:my_value]).to be_nil
417
446
  end
418
447
 
419
448
  it "handles incomplete array response" do
@@ -642,11 +671,25 @@ describe WashOut do
642
671
 
643
672
  savon(:rocknroll, "ZOMG" => 'yam!')
644
673
  end
674
+ end
645
675
 
676
+ describe "Router" do
677
+ it "raises when SOAP message without SOAP Envelope arrives" do
678
+ mock_controller do; end
679
+ invalid_request = '<a></a>'
680
+ response_hash = Nori.new.parse(HTTPI.post("http://app/route/api/action", invalid_request).body)
681
+ expect(response_hash["soap:Envelope"]["soap:Body"]["soap:Fault"]['faultstring']).to eq "Invalid SOAP request"
682
+ end
683
+
684
+ it "raises when SOAP message without SOAP Body arrives" do
685
+ mock_controller do; end
686
+ invalid_request = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"></s:Envelope>'
687
+ response_hash = Nori.new.parse(HTTPI.post("http://app/route/api/action", invalid_request).body)
688
+ expect(response_hash["soap:Envelope"]["soap:Body"]["soap:Fault"]['faultstring']).to eq "Invalid SOAP request"
689
+ end
646
690
  end
647
691
 
648
692
  describe "WS Security" do
649
-
650
693
  it "appends username_token to params" do
651
694
  mock_controller(wsse_username: "gorilla", wsse_password: "secret") do
652
695
  soap_action "checkToken", :args => :integer, :return => nil, :to => 'check_token'
data/spec/spec_helper.rb CHANGED
@@ -50,12 +50,18 @@ HTTPI.adapter = :rack
50
50
 
51
51
  HTTPI::Adapter::Rack.mount 'app', Dummy::Application
52
52
  Dummy::Application.routes.draw do
53
- wash_out :api
53
+ namespace :route do
54
+ scope module: 'space' do
55
+ wash_out :api
56
+ end
57
+ end
54
58
  end
55
59
 
56
60
  def mock_controller(options = {}, &block)
57
- Object.send :remove_const, :ApiController if defined?(ApiController)
58
- Object.send :const_set, :ApiController, Class.new(ApplicationController) {
61
+ Object.send :const_set, :Route, Module.new unless defined?(Route)
62
+ Route.send :const_set, :Space, Module.new unless defined?(Route::Space)
63
+ Route::Space.send :remove_const, :ApiController if defined?(Route::Space::ApiController)
64
+ Route::Space.send :const_set, :ApiController, Class.new(ApplicationController) {
59
65
  include RSpec::Matchers
60
66
 
61
67
  soap_service options.reverse_merge({
@@ -66,5 +72,17 @@ def mock_controller(options = {}, &block)
66
72
  class_exec &block if block
67
73
  }
68
74
 
69
- ActiveSupport::Dependencies::Reference.instance_variable_get(:'@store').delete('ApiController')
75
+ ActiveSupport::Dependencies::Reference.instance_variable_get(:'@store').delete('Route::Space::ApiController')
76
+ end
77
+
78
+ unless defined?(silence_stream) # Rails 5
79
+ def silence_stream(stream)
80
+ old_stream = stream.dup
81
+ stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
82
+ stream.sync = true
83
+ yield
84
+ ensure
85
+ stream.reopen(old_stream)
86
+ old_stream.close
87
+ end
70
88
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wash_out
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boris Staal
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-04 00:00:00.000000000 Z
12
+ date: 2016-06-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nori
@@ -48,11 +48,11 @@ files:
48
48
  - app/views/wash_out/rpc/error.builder
49
49
  - app/views/wash_out/rpc/response.builder
50
50
  - app/views/wash_out/rpc/wsdl.builder
51
- - gemfiles/rails_3.1.3.gemfile
52
- - gemfiles/rails_3.2.12.gemfile
51
+ - gemfiles/rails_3.2.13.gemfile
53
52
  - gemfiles/rails_4.0.0.gemfile
54
53
  - gemfiles/rails_4.1.0.gemfile
55
54
  - gemfiles/rails_4.2.0.gemfile
55
+ - gemfiles/rails_5.0.0.beta2.gemfile
56
56
  - init.rb
57
57
  - lib/wash_out.rb
58
58
  - lib/wash_out/configurable.rb
@@ -90,6 +90,9 @@ files:
90
90
  - spec/dummy/public/favicon.ico
91
91
  - spec/dummy/public/stylesheets/.gitkeep
92
92
  - spec/dummy/script/rails
93
+ - spec/fixtures/nested_refs_to_arrays.xml
94
+ - spec/fixtures/ref_to_one_array.xml
95
+ - spec/fixtures/refs_to_arrays.xml
93
96
  - spec/lib/wash_out/dispatcher_spec.rb
94
97
  - spec/lib/wash_out/middleware_spec.rb
95
98
  - spec/lib/wash_out/param_spec.rb
@@ -114,12 +117,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
117
  version: '0'
115
118
  required_rubygems_version: !ruby/object:Gem::Requirement
116
119
  requirements:
117
- - - ">="
120
+ - - ">"
118
121
  - !ruby/object:Gem::Version
119
- version: '0'
122
+ version: 1.3.1
120
123
  requirements: []
121
124
  rubyforge_project:
122
- rubygems_version: 2.2.5
125
+ rubygems_version: 2.5.1
123
126
  signing_key:
124
127
  specification_version: 4
125
128
  summary: Dead simple Rails 3 SOAP server library