Module: WashOut::Dispatcher

Defined in:
lib/wash_out/dispatcher.rb

Overview

The WashOut::Dispatcher module should be included in a controller acting as a SOAP endpoint. It includes actions for generating WSDL and handling SOAP requests.

Defined Under Namespace

Classes: ProgrammerError, SOAPError

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.deep_replace_href(hash, replace) ⇒ Object



186
187
188
189
190
191
192
193
194
# File 'lib/wash_out/dispatcher.rb', line 186

def self.deep_replace_href(hash, replace)
  return replace[hash[:@href]] if hash.has_key?(:@href)

  hash.keys.each do |key, value|
    hash[key] = deep_replace_href(hash[key], replace) if hash[key].is_a?(Hash)
  end

  hash
end

.deep_select(hash, result = [], &blk) ⇒ Object



176
177
178
179
180
181
182
183
184
# File 'lib/wash_out/dispatcher.rb', line 176

def self.deep_select(hash, result=[], &blk)
  result += Hash[hash.select(&blk)].values

  hash.each do |key, value|
    result = deep_select(value, result, &blk) if value.is_a? Hash
  end

  result
end

.included(controller) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/wash_out/dispatcher.rb', line 160

def self.included(controller)
  entity = if defined?(Rails::VERSION::MAJOR) && (Rails::VERSION::MAJOR >= 4)
    'action'
  else
    'filter' 
  end

  controller.send :"around_#{entity}", :_catch_soap_errors
  controller.send :helper, :wash_out
  controller.send :"before_#{entity}", :_authenticate_wsse,     :except => [
    :_generate_wsdl, :_invalid_action ]
  controller.send :"before_#{entity}", :_map_soap_parameters,   :except => [
    :_generate_wsdl, :_invalid_action ]
  controller.send :"skip_before_#{entity}", :verify_authenticity_token
end

Instance Method Details

#_authenticate_wsseObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/wash_out/dispatcher.rb', line 18

def _authenticate_wsse

  begin
    xml_security   = request.env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first
    xml_security   = xml_security.values_at(:header, :Header).compact.first
    xml_security   = xml_security.values_at(:security, :Security).compact.first
    username_token = xml_security.values_at(:username_token, :UsernameToken).compact.first
  rescue
    username_token = nil
  end

  WashOut::Wsse.authenticate soap_config, username_token

  request.env['WSSE_TOKEN'] = username_token.with_indifferent_access unless username_token.blank?
end

#_catch_soap_errorsObject



143
144
145
146
147
# File 'lib/wash_out/dispatcher.rb', line 143

def _catch_soap_errors
  yield
rescue SOAPError => error
  render_soap_error(error.message, error.code)
end

#_generate_wsdlObject

This action generates the WSDL for defined SOAP methods.



71
72
73
74
75
76
77
78
79
# File 'lib/wash_out/dispatcher.rb', line 71

def _generate_wsdl

  @map       = self.class.soap_actions
  @namespace = soap_config.namespace
  @name      = controller_path.gsub('/', '_')

  render :template => "wash_out/#{soap_config.wsdl_style}/wsdl", :layout => false,
         :content_type => 'text/xml'
end

#_invalid_actionObject

This action is a fallback for all undefined SOAP actions.



139
140
141
# File 'lib/wash_out/dispatcher.rb', line 139

def _invalid_action
  render_soap_error("Cannot find SOAP action mapping for #{request.env['wash_out.soap_action']}")
end

#_load_params(spec, xml_data) ⇒ Object

Creates the final parameter hash based on the request spec and xml_data from the request



59
60
61
62
63
64
65
66
67
68
# File 'lib/wash_out/dispatcher.rb', line 59

def _load_params(spec, xml_data)
  params = HashWithIndifferentAccess.new
  spec.each do |param|
    key = param.raw_name.to_sym
    if xml_data.has_key? key
      params[param.raw_name] = param.load(xml_data, key)
    end
  end
  params
end

#_map_soap_parametersObject



34
35
36
37
# File 'lib/wash_out/dispatcher.rb', line 34

def _map_soap_parameters
  @_params = _load_params action_spec[:in],
    _strip_empty_nodes(action_spec[:in], xml_data)
end

#_render_soap(result, options) ⇒ Object

Render a SOAP response.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/wash_out/dispatcher.rb', line 82

def _render_soap(result, options)
  @namespace   = soap_config.namespace
  @operation   = soap_action = request.env['wash_out.soap_action']
  @action_spec = self.class.soap_actions[soap_action]

  result = { 'value' => result } unless result.is_a? Hash
  result = HashWithIndifferentAccess.new(result)

  inject = lambda {|data, map|
    result_spec = []
    return result_spec if data.nil?

    map.each_with_index do |param, i|
      result_spec[i] = param.flat_copy

      unless data.is_a?(Hash)
        raise ProgrammerError,
          "SOAP response used #{data.inspect} (which is #{data.class.name}), " +
          "in the context where a Hash with key of '#{param.raw_name}' " +
          "was expected."
      end

      value = data[param.raw_name]

      unless value.nil?
        if param.multiplied && !value.is_a?(Array)
          raise ProgrammerError,
            "SOAP response tried to use '#{value.inspect}' " +
            "(which is of type #{value.class.name}), as the value for " +
            "'#{param.raw_name}' (which expects an Array)."
        end

        # Inline complex structure              {:foo => {bar: ...}}
        if param.struct? && !param.multiplied
          result_spec[i].map = inject.call(value, param.map)

        # Inline array of complex structures    {:foo => [{bar: ...}]}
        elsif param.struct? && param.multiplied
          result_spec[i].map = value.map{|e| inject.call(e, param.map)}

        # Inline scalar                         {:foo => :string}
        else
          result_spec[i].value = value
        end
      end
    end

    return result_spec
  }

  render :template => "wash_out/#{soap_config.wsdl_style}/response",
         :layout => false,
         :locals => { :result => inject.call(result, @action_spec[:out]) },
         :content_type => 'text/xml'
end

#_strip_empty_nodes(params, hash) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/wash_out/dispatcher.rb', line 39

def _strip_empty_nodes(params, hash)
  hash.keys.each do |key|
    param = params.detect { |a| a.raw_name.to_s == key.to_s }
    next if !(param && hash[key].is_a?(Hash))

    value = hash[key].delete_if do |k, _|
      k.to_s[0] == '@' && !param.map.detect { |a| a.raw_name.to_s == k.to_s }
    end

    if value.length > 0
      hash[key] = _strip_empty_nodes param.map, value
    else
      hash[key] = nil
    end
  end

  hash
end

#render_soap_error(message, code = nil) ⇒ Object

Render a SOAP error response.

Rails do not support sequental rescue_from handling, that is, rescuing an exception from a rescue_from handler. Hence this function is a public API.



153
154
155
156
157
158
# File 'lib/wash_out/dispatcher.rb', line 153

def render_soap_error(message, code=nil)
  render :template => "wash_out/#{soap_config.wsdl_style}/error", :status => 500,
         :layout => false,
         :locals => { :error_message => message, :error_code => (code || 'Server') },
         :content_type => 'text/xml'
end