Class: Wasabi::Parser

Inherits:
Object
  • Object
show all
Includes:
XPathHelper
Defined in:
lib/wasabi/parser.rb

Overview

Wasabi::Parser

Parses WSDL documents and remembers their important parts.

Constant Summary

Constants included from XPathHelper

XPathHelper::NAMESPACES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from XPathHelper

#at_xpath, #xpath

Constructor Details

#initialize(document) ⇒ Parser

Returns a new instance of Parser.



13
14
15
16
17
18
19
20
# File 'lib/wasabi/parser.rb', line 13

def initialize(document)
  self.document = document
  self.operations = {}
  self.namespaces = {}
  self.types = {}
  self.deferred_types = []
  self.element_form_default = :unqualified
end

Instance Attribute Details

#deferred_typesObject

Returns a map of deferred type Proc objects.



38
39
40
# File 'lib/wasabi/parser.rb', line 38

def deferred_types
  @deferred_types
end

#documentObject

Returns the Nokogiri document.



23
24
25
# File 'lib/wasabi/parser.rb', line 23

def document
  @document
end

#element_form_defaultObject

Returns the elementFormDefault value.



44
45
46
# File 'lib/wasabi/parser.rb', line 44

def element_form_default
  @element_form_default
end

#endpointObject

Returns the SOAP endpoint.



41
42
43
# File 'lib/wasabi/parser.rb', line 41

def endpoint
  @endpoint
end

#namespaceObject

Returns the target namespace.



26
27
28
# File 'lib/wasabi/parser.rb', line 26

def namespace
  @namespace
end

#namespacesObject

Returns a map from namespace identifier to namespace URI.



29
30
31
# File 'lib/wasabi/parser.rb', line 29

def namespaces
  @namespaces
end

#operationsObject

Returns the SOAP operations.



32
33
34
# File 'lib/wasabi/parser.rb', line 32

def operations
  @operations
end

#typesObject

Returns a map from a type name to a Hash with type information.



35
36
37
# File 'lib/wasabi/parser.rb', line 35

def types
  @types
end

Instance Method Details

#find_namespace(type) ⇒ Object



140
141
142
143
# File 'lib/wasabi/parser.rb', line 140

def find_namespace(type)
  schema_namespace = at_xpath(type, "ancestor::xs:schema/@targetNamespace")
  schema_namespace ? schema_namespace.to_s : @namespace
end

#input_for(operation) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/wasabi/parser.rb', line 145

def input_for(operation)
  operation_name = operation["name"]

  # Look up the input by walking up to portType, then up to the message.

  binding_type = at_xpath(operation, "../@type").to_s.split(':').last
  port_type_input = at_xpath(operation, "../../wsdl:portType[@name='#{binding_type}']/wsdl:operation[@name='#{operation_name}']/wsdl:input")

  # TODO: Stupid fix for missing support for imports.
  # Sometimes portTypes are actually included in a separate WSDL.
  if port_type_input
    port_message_ns_id, port_message_type = port_type_input.attribute("message").to_s.split(':')

    message_ns_id, message_type = nil

    # TODO: Support multiple 'part' elements in the message.
    if (port_message_part = at_xpath(port_type_input, "../../../wsdl:message[@name='#{port_message_type}']/wsdl:part[1]"))
      if (port_message_part_element = port_message_part.attribute("element"))
        message_ns_id, message_type = port_message_part_element.to_s.split(':')
      end
    end

    # Fall back to the name of the binding operation
    if message_type
      [message_ns_id, message_type]
    else
      [port_message_ns_id, operation_name]
    end
  else
    [nil, operation_name]
  end
end

#parseObject



46
47
48
49
50
51
52
# File 'lib/wasabi/parser.rb', line 46

def parse
  parse_namespaces
  parse_endpoint
  parse_operations
  parse_types
  parse_deferred_types
end

#parse_deferred_typesObject



136
137
138
# File 'lib/wasabi/parser.rb', line 136

def parse_deferred_types
  deferred_types.each(&:call)
end

#parse_endpointObject



67
68
69
70
71
72
73
74
75
76
# File 'lib/wasabi/parser.rb', line 67

def parse_endpoint
  endpoint = at_xpath("wsdl:definitions/wsdl:service//soap11:address/@location")
  endpoint ||= at_xpath("wsdl:definitions/wsdl:service//soap12:address/@location")

  begin
    @endpoint = URI(URI.escape(endpoint.to_s)) if endpoint
  rescue URI::InvalidURIError
    @endpoint = nil
  end
end

#parse_namespacesObject



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/wasabi/parser.rb', line 54

def parse_namespaces
  element_form_default = at_xpath("wsdl:definitions/wsdl:types/xs:schema/@elementFormDefault")
  @element_form_default = element_form_default.to_s.to_sym if element_form_default

  namespace = at_xpath("wsdl:definitions/@targetNamespace")
  @namespace = namespace.to_s if namespace

  @namespaces = @document.namespaces.inject({}) do |memo, (key, value)|
    memo[key.sub("xmlns:", "")] = value
    memo
  end
end

#parse_operationsObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/wasabi/parser.rb', line 78

def parse_operations
  operations = xpath("wsdl:definitions/wsdl:binding/wsdl:operation")
  operations.each do |operation|
    name = operation.attribute("name").to_s

    soap_action = at_xpath(operation, ".//soap11:operation/@soapAction")
    soap_action ||= at_xpath(operation, ".//soap12:operation/@soapAction")

    if soap_action
      soap_action = soap_action.to_s
      action = soap_action && !soap_action.empty? ? soap_action : name

      # There should be a matching portType for each binding, so we will lookup the input from there.
      namespace_id, input = input_for(operation)

      # Store namespace identifier so this operation can be mapped to the proper namespace.
      @operations[name.snakecase.to_sym] = { :action => action, :input => input, :namespace_identifier => namespace_id }
    elsif !@operations[name.snakecase.to_sym]
      @operations[name.snakecase.to_sym] = { :action => name, :input => name }
    end
  end
end

#parse_typesObject



101
102
103
104
105
106
107
# File 'lib/wasabi/parser.rb', line 101

def parse_types
  xpath("wsdl:definitions/wsdl:types/xs:schema/xs:element[@name]").
    each { |type| process_type(at_xpath(type, "./xs:complexType"), type.attribute("name").to_s) }

  xpath("wsdl:definitions/wsdl:types/xs:schema/xs:complexType[@name]").
    each { |type| process_type(type, type.attribute("name").to_s) }
end

#process_type(type, name) ⇒ Object



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
# File 'lib/wasabi/parser.rb', line 109

def process_type(type, name)
  return unless type
  @types[name] ||= { :namespace => find_namespace(type) }

  xpath(type, "./xs:sequence/xs:element").
    each { |inner| @types[name][inner.attribute("name").to_s] = { :type => inner.attribute("type").to_s } }

  type.xpath("./xs:complexContent/xs:extension/xs:sequence/xs:element",
    "xs" => "http://www.w3.org/2001/XMLSchema"
  ).each do |inner_element|
    @types[name][inner_element.attribute('name').to_s] = {
      :type => inner_element.attribute('type').to_s
    }
  end

  type.xpath('./xs:complexContent/xs:extension[@base]',
    "xs" => "http://www.w3.org/2001/XMLSchema"
  ).each do |inherits|
    base = inherits.attribute('base').value.match(/\w+$/).to_s
    if @types[base]
      @types[name].merge! @types[base]
    else
      deferred_types << Proc.new { @types[name].merge! @types[base] }
    end
  end
end