class Wasabi::Parser
Wasabi::Parser
¶ ↑
Parses WSDL
documents and remembers their important parts.
Constants
- SOAP_1_1
- SOAP_1_2
- WSDL
- XSD
Attributes
deferred_types[RW]
Returns a map of deferred type Proc objects.
document[RW]
Returns the Nokogiri document.
element_form_default[RW]
Returns the elementFormDefault value.
endpoint[RW]
Returns the SOAP endpoint.
namespace[RW]
Returns the target namespace.
namespaces[RW]
Returns a map from namespace identifier to namespace URI.
operations[RW]
Returns the SOAP operations.
service_name[RW]
Returns the SOAP Service Name
types[RW]
Returns a map from a type name to a Hash with type information.
Public Class Methods
new(document)
click to toggle source
# File lib/wasabi/parser.rb, line 19 def initialize(document) self.document = document self.operations = {} self.namespaces = {} self.service_name = '' self.types = {} self.deferred_types = [] self.element_form_default = :unqualified end
Public Instance Methods
input_for(operation)
click to toggle source
# File lib/wasabi/parser.rb, line 234 def input_for(operation) input_output_for(operation, 'input') end
input_output_for(operation, input_output)
click to toggle source
@return [namespace_id, message_type]
# File lib/wasabi/parser.rb, line 243 def input_output_for(operation, input_output) operation_name = operation['name'] # Look up the input by walking up to portType, then up to the message. binding_type = operation.parent['type'].to_s.split(':').last if @port_type_operations[binding_type] port_type_operation = @port_type_operations[binding_type][operation_name] end port_type_input_output = port_type_operation&.element_children&.find { |node| node.name == input_output } # find the message for the portType operation # if there is no message, we will use the operation name as the message name # TODO: Stupid fix for missing support for imports. # Sometimes portTypes are actually included in a separate WSDL. if port_type_input_output # If the message attribute contains a colon, it means the message is namespaced. if port_type_input_output.attribute('message').to_s.include? ':' port_message_ns_id, port_message_type = port_type_input_output.attribute('message').to_s.split(':') else port_message_type = port_type_input_output.attribute('message').to_s end message_ns_id, message_type = nil # When there is a parts attribute in soap:body element, we should use that value # to look up the message part from messages array. input_output_element = operation.element_children.find { |node| node.name == input_output } if input_output_element soap_body_element = input_output_element.element_children.find { |node| node.name == 'body' } soap_body_parts = soap_body_element['parts'] if soap_body_element end # look for any message part that matches the soap body parts message = @messages[port_message_type] port_message_part = message&.element_children&.find do |node| soap_body_parts.nil? ? (node.name == "part") : (node.name == "part" && node["name"] == soap_body_parts) end if port_message_part && port_element = port_message_part.attribute('element') port_message_part = port_element.to_s if port_message_part.include?(':') message_ns_id, message_type = port_message_part.split(':') else message_type = port_message_part end end # If the message is not found, we should use the operation name as the message name for document style operations # applies only to output if input_output == 'output' # if the operation is document style and theres no port_message_part, we should use the operation_name soap_operation = operation.element_children.find { |node| node.name == 'operation' } if message_type.nil? && (soap_operation.nil? || soap_operation['style'] != 'rpc') if port_message_part.nil? message_ns_id = port_message_ns_id message_type = operation_name else message_ns_id = port_message_ns_id message_type = port_message_type end 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
output_for(operation)
click to toggle source
# File lib/wasabi/parser.rb, line 238 def output_for(operation) input_output_for(operation, 'output') end
parse()
click to toggle source
# File lib/wasabi/parser.rb, line 56 def parse parse_namespaces parse_endpoint parse_service_name parse_messages parse_port_types parse_port_type_operations parse_operations parse_operations_parameters parse_types parse_deferred_types end
parse_deferred_types()
click to toggle source
# File lib/wasabi/parser.rb, line 230 def parse_deferred_types deferred_types.each(&:call) end
parse_endpoint()
click to toggle source
# File lib/wasabi/parser.rb, line 82 def parse_endpoint if service_node = service endpoint = service_node.at_xpath('.//soap11:address/@location', 'soap11' => SOAP_1_1) endpoint ||= service_node.at_xpath(service_node, './/soap12:address/@location', 'soap12' => SOAP_1_2) end @endpoint = parse_url(endpoint) if endpoint end
parse_messages()
click to toggle source
# File lib/wasabi/parser.rb, line 103 def parse_messages messages = document.root.element_children.select { |node| node.name == 'message' } @messages = Hash[messages.map { |node| [node['name'], node] }] end
parse_namespaces()
click to toggle source
# File lib/wasabi/parser.rb, line 69 def parse_namespaces element_form_default = schemas.first && schemas.first['elementFormDefault'] @element_form_default = element_form_default.to_s.to_sym if element_form_default namespace = document.root['targetNamespace'] @namespace = namespace.to_s if namespace @namespaces = @document.namespaces.inject({}) do |memo, (key, value)| memo[key.sub('xmlns:', '')] = value memo end end
parse_operations()
click to toggle source
# File lib/wasabi/parser.rb, line 138 def parse_operations operations = document.xpath('wsdl:definitions/wsdl:binding/wsdl:operation', 'wsdl' => WSDL) operations.each do |operation| name = operation.attribute('name').to_s snakecase_name = Wasabi::CoreExt::String.snakecase(name).to_sym # TODO: check for soap namespace? soap_operation = operation.element_children.find { |node| node.name == 'operation' } soap_action = soap_operation['soapAction'] if soap_operation soap_document = soap_operation['style'] == 'document' if soap_operation if soap_action || soap_document 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, output = output_for(operation) namespace_id, input = input_for(operation) # Store namespace identifier so this operation can be mapped to the proper namespace. @operations[snakecase_name] = { :action => action, :input => input, :output => output, :namespace_identifier => namespace_id} elsif !@operations[snakecase_name] @operations[snakecase_name] = { :action => name, :input => name } end end end
parse_operations_parameters()
click to toggle source
# File lib/wasabi/parser.rb, line 122 def parse_operations_parameters document.xpath("wsdl:definitions/wsdl:types/*[local-name()='schema']/*[local-name()='element']", 'wsdl' => WSDL).each do |element| name = Wasabi::CoreExt::String.snakecase(element.attribute('name').to_s).to_sym if operation = @operations[name] element.xpath("*[local-name() ='complexType']/*[local-name() ='sequence']/*[local-name() ='element']").each do |child_element| attr_name = child_element.attribute('name').to_s attr_type = (attr_type = child_element.attribute('type').to_s.split(':')).size > 1 ? attr_type[1] : attr_type[0] operation[:parameters] ||= {} operation[:parameters][attr_name.to_sym] = { :name => attr_name, :type => attr_type } end end end end
parse_port_type_operations()
click to toggle source
# File lib/wasabi/parser.rb, line 113 def parse_port_type_operations @port_type_operations = {} @port_types.each do |port_type_name, port_type| operations = port_type.element_children.select { |node| node.name == 'operation' } @port_type_operations[port_type_name] = Hash[operations.map { |node| [node['name'], node] }] end end
parse_port_types()
click to toggle source
# File lib/wasabi/parser.rb, line 108 def parse_port_types port_types = document.root.element_children.select { |node| node.name == 'portType' } @port_types = Hash[port_types.map { |node| [node['name'], node] }] end
parse_service_name()
click to toggle source
# File lib/wasabi/parser.rb, line 98 def parse_service_name service_name = document.root['name'] @service_name = service_name.to_s if service_name end
parse_types()
click to toggle source
# File lib/wasabi/parser.rb, line 165 def parse_types schemas.each do |schema| schema_namespace = schema['targetNamespace'] schema.element_children.each do |node| namespace = schema_namespace || @namespace case node.name when 'element' complex_type = node.at_xpath('./xs:complexType', 'xs' => XSD) process_type namespace, complex_type, node['name'].to_s if complex_type when 'complexType' process_type namespace, node, node['name'].to_s end end end end
parse_url(url)
click to toggle source
# File lib/wasabi/parser.rb, line 91 def parse_url(url) unescaped_url = Addressable::URI.unescape(url.to_s) escaped_url = Addressable::URI.escape(unescaped_url) URI(escaped_url) rescue URI::InvalidURIError, Addressable::URI::InvalidURIError end
process_type(namespace, type, name)
click to toggle source
# File lib/wasabi/parser.rb, line 183 def process_type(namespace, type, name) @types[namespace] ||= {} @types[namespace][name] ||= { :namespace => namespace } @types[namespace][name][:order!] = [] type.xpath('./xs:sequence/xs:element', 'xs' => XSD).each do |inner| element_name = inner.attribute('name').to_s @types[namespace][name][element_name] = { :type => inner.attribute('type').to_s } [ :nillable, :minOccurs, :maxOccurs ].each do |attr| if v = inner.attribute(attr.to_s) @types[namespace][name][element_name][attr] = v.to_s end end @types[namespace][name][:order!] << element_name end type.xpath('./xs:complexContent/xs:extension/xs:sequence/xs:element', 'xs' => XSD).each do |inner_element| element_name = inner_element.attribute('name').to_s @types[namespace][name][element_name] = { :type => inner_element.attribute('type').to_s } @types[namespace][name][:order!] << element_name end type.xpath('./xs:complexContent/xs:extension[@base]', 'xs' => XSD).each do |inherits| base = inherits.attribute('base').value.match(/\w+$/).to_s if @types[namespace][base] # Reverse merge because we don't want subclass attributes to be overriden by base class @types[namespace][name] = types[namespace][base].merge(types[namespace][name]) @types[namespace][name][:order!] = @types[namespace][base][:order!] | @types[namespace][name][:order!] @types[namespace][name][:base_type] = base else p = Proc.new do if @types[namespace][base] # Reverse merge because we don't want subclass attributes to be overriden by base class @types[namespace][name] = @types[namespace][base].merge(@types[namespace][name]) @types[namespace][name][:order!] = @types[namespace][base][:order!] | @types[namespace][name][:order!] @types[namespace][name][:base_type] = base end end deferred_types << p end end end
schemas()
click to toggle source
# File lib/wasabi/parser.rb, line 320 def schemas types = section('types').first types ? types.element_children : [] end
section(section_name)
click to toggle source
# File lib/wasabi/parser.rb, line 330 def section(section_name) sections[section_name] || [] end
sections()
click to toggle source
# File lib/wasabi/parser.rb, line 334 def sections @sections ||= document.root.element_children.group_by { |node| node.name } end
service()
click to toggle source
# File lib/wasabi/parser.rb, line 325 def service services = section('service') services.first if services # service nodes could be imported? end