diff --git a/lib/sekken/importer.rb b/lib/sekken/importer.rb index ae006b9..9be08af 100644 --- a/lib/sekken/importer.rb +++ b/lib/sekken/importer.rb @@ -24,27 +24,41 @@ def import(location) # resolve xml schema imports import_schemas do |schema_location| @logger.info("Resolving XML schema import #{schema_location.inspect}.") - + import_document(schema_location) do |document| @schemas.push(document.schemas) end end + + import_included_schemas do |schema_location| + @logger.info("Resolving included schema #{schema_location.inspect}.") + + import_document(schema_location) do |document| + document.schemas.each do |schema| + existing = @schemas.find_by_namespace(schema.target_namespace) + existing.merge(schema) + end + end + end end private def import_document(location, &block) + @logger.info("Importing #{location.inspect}.") if @import_locations.include? location - @logger.info("Skipping already imported location #{location.inspect}.") + @logger.warn("Skipping already imported location #{location.inspect}.") return end xml = @resolver.resolve(location) + @import_locations << location document = WSDL::Document.new Nokogiri.XML(xml), @schemas + block.call(document) - + # resolve wsdl imports document.imports.each do |import_location| @logger.info("Resolving WSDL import #{import_location.inspect}.") @@ -57,20 +71,25 @@ def import_schemas schema.imports.each do |namespace, schema_location| next unless schema_location - unless absolute_url? schema_location + unless @resolver.can_resolve? schema_location @logger.warn("Skipping XML Schema import #{schema_location.inspect}.") next end # TODO: also skip if the schema was already imported - yield(schema_location) end end end - def absolute_url?(location) - location =~ Resolver::URL_PATTERN + def import_included_schemas + @schemas.each do |schema| + schema.includes.each do |include_location| + if @resolver.can_resolve? include_location + yield include_location + end + end + end end end diff --git a/lib/sekken/resolver.rb b/lib/sekken/resolver.rb index 6d01db7..36fcc81 100644 --- a/lib/sekken/resolver.rb +++ b/lib/sekken/resolver.rb @@ -4,17 +4,27 @@ class Resolver URL_PATTERN = /^http[s]?:/ XML_PATTERN = /^</ - def initialize(http) + def initialize(http, base_file_path="") @http = http + @base_file_path = base_file_path end def resolve(location) case location when URL_PATTERN then @http.get(location) when XML_PATTERN then location - else File.read(location) + else + begin + File.read(location) + rescue + File.read(File.join(@base_file_path, location)) + end end end + def can_resolve?(location) + (location =~ Resolver::URL_PATTERN) || (File.readable?(location)) || (File.readable?(File.join(@base_file_path, location))) + end + end end diff --git a/lib/sekken/wsdl.rb b/lib/sekken/wsdl.rb index 1addd84..3381bdc 100644 --- a/lib/sekken/wsdl.rb +++ b/lib/sekken/wsdl.rb @@ -10,8 +10,11 @@ class WSDL def initialize(wsdl, http) @documents = WSDL::DocumentCollection.new @schemas = XS::SchemaCollection.new - - resolver = Resolver.new(http) + + base_file_path = "" + base_file_path = File.dirname(wsdl) if File.exists?(wsdl) + + resolver = Resolver.new(http, base_file_path) importer = Importer.new(resolver, @documents, @schemas) importer.import(wsdl) end diff --git a/lib/sekken/wsdl/document.rb b/lib/sekken/wsdl/document.rb index c695016..0d38950 100644 --- a/lib/sekken/wsdl/document.rb +++ b/lib/sekken/wsdl/document.rb @@ -38,12 +38,12 @@ def schemas def imports imports = [] - + @document.root.xpath('wsdl:import', 'wsdl' => Sekken::NS_WSDL).each do |node| location = node['location'] imports << location if location end - + imports end diff --git a/lib/sekken/xml/element_builder.rb b/lib/sekken/xml/element_builder.rb index 27bae47..1fc60d6 100644 --- a/lib/sekken/xml/element_builder.rb +++ b/lib/sekken/xml/element_builder.rb @@ -40,7 +40,7 @@ def build_element(part) local, namespace = expand_qname(part[:element], part[:namespaces]) schema = @schemas.find_by_namespace(namespace) raise "Unable to find schema for #{namespace.inspect}" unless schema - + xs_element = schema.elements.fetch(local) type = find_type_for_element(xs_element) diff --git a/lib/sekken/xs/schema.rb b/lib/sekken/xs/schema.rb index 011ba7f..1a0cb7d 100644 --- a/lib/sekken/xs/schema.rb +++ b/lib/sekken/xs/schema.rb @@ -17,13 +17,24 @@ def initialize(schema, schemas) @complex_types = {} @simple_types = {} @imports = {} + @includes = [] parse end - attr_accessor :target_namespace, :element_form_default, :imports, + attr_accessor :target_namespace, :element_form_default, :imports, :includes, :attributes, :attribute_groups, :elements, :complex_types, :simple_types + + def merge schema + @attributes.merge! schema.attributes unless @attributes.nil? + @attributes_groups.merge! schema.attribute_groups unless @attributes_groups.nil? + @elements.merge! schema.elements unless @elements.nil? + @complex_types.merge! schema.complex_types unless @complex_types.nil? + @simple_types.merge! schema.simple_types unless @simple_types.nil? + + end + private def parse @@ -31,7 +42,7 @@ def parse :target_namespace => @target_namespace, :element_form_default => @element_form_default } - + @schema.element_children.each do |node| case node.name when 'attribute' then store_element(@attributes, node, schema) @@ -40,6 +51,7 @@ def parse when 'complexType' then store_element(@complex_types, node, schema) when 'simpleType' then store_element(@simple_types, node, schema) when 'import' then store_import(node) + when 'include' then store_include(node) end end end @@ -52,6 +64,10 @@ def store_import(node) @imports[node['namespace']] = node['schemaLocation'] end + def store_include node + @includes.push node['schemaLocation'] + end + end end end diff --git a/spec/fixtures/wsdl/folder/imported.wsdl b/spec/fixtures/wsdl/folder/imported.wsdl new file mode 100644 index 0000000..e894795 --- /dev/null +++ b/spec/fixtures/wsdl/folder/imported.wsdl @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<definitions name="ImportedService" + xmlns="http://schemas.xmlsoap.org/wsdl/" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" + > + <types> + <schema xmlns="http://www.w3.org/2001/XMLSchema" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" + > + </schema> + </types> +</definitions> diff --git a/spec/fixtures/wsdl/import.wsdl b/spec/fixtures/wsdl/import.wsdl new file mode 100644 index 0000000..c831856 --- /dev/null +++ b/spec/fixtures/wsdl/import.wsdl @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definitions name="ImportService" + xmlns="http://schemas.xmlsoap.org/wsdl/" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> + <import location="folder/imported.wsdl" /> +</definitions> diff --git a/spec/fixtures/wsdl/sabre/SessionCreateRQ.wsdl b/spec/fixtures/wsdl/sabre/SessionCreateRQ.wsdl new file mode 100644 index 0000000..afa1939 --- /dev/null +++ b/spec/fixtures/wsdl/sabre/SessionCreateRQ.wsdl @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsd1="http://www.opentravel.org/OTA/2002/11" xmlns:tns="https://webservices.sabre.com/websvc" xmlns:eb="http://www.ebxml.org/namespaces/messageHeader" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" targetNamespace="https://webservices.sabre.com/websvc"> + <types> + <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + <xsd:import namespace="http://www.opentravel.org/OTA/2002/11" schemaLocation="SessionCreateRQRS.xsd"/> + <xsd:import namespace="http://www.ebxml.org/namespaces/messageHeader" schemaLocation="msg-header-2_0.xsd"/> + <xsd:import namespace="http://schemas.xmlsoap.org/ws/2002/12/secext" schemaLocation="wsse.xsd"/> + </xsd:schema> + </types> + <message name="GetSessionCreateInput"> + <part name="header" element="eb:MessageHeader"/> + <part name="header2" element="wsse:Security"/> + <part name="body" element="xsd1:SessionCreateRQ"/> + </message> + <message name="GetSessionCreateOutput"> + <part name="header" element="eb:MessageHeader"/> + <part name="header2" element="wsse:Security"/> + <part name="body" element="xsd1:SessionCreateRS"/> + </message> + <portType name="SessionCreatePortType"> + <operation name="SessionCreateRQ"> + <input message="tns:GetSessionCreateInput"/> + <output message="tns:GetSessionCreateOutput"/> + </operation> + </portType> + <binding name="SessionCreateSoapBinding" type="tns:SessionCreatePortType"> + <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="SessionCreateRQ"> + <soap:operation soapAction="OTA"/> + <input> + <soap:header message="tns:GetSessionCreateInput" part="header" use="literal"/> + <soap:header message="tns:GetSessionCreateInput" part="header2" use="literal"/> + <soap:body parts="body" use="literal"/> + </input> + <output> + <soap:header message="tns:GetSessionCreateOutput" part="header" use="literal"/> + <soap:header message="tns:GetSessionCreateOutput" part="header2" use="literal"/> + <soap:body parts="body" use="literal"/> + </output> + </operation> + </binding> + <service name="SessionCreateRQService"> + <port name="SessionCreatePortType" binding="tns:SessionCreateSoapBinding"> + <soap:address location="https://webservices.sabre.com/websvc"/> + </port> + </service> +</definitions> diff --git a/spec/fixtures/wsdl/sabre/SessionCreateRQ.xsd b/spec/fixtures/wsdl/sabre/SessionCreateRQ.xsd new file mode 100644 index 0000000..f98de51 --- /dev/null +++ b/spec/fixtures/wsdl/sabre/SessionCreateRQ.xsd @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="http://www.opentravel.org/OTA/2002/11" xmlns="http://www.opentravel.org/OTA/2002/11" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + <xs:element name="SessionCreateRQ"> + <xs:complexType> + <xs:sequence> + <xs:element name="POS"> + <xs:complexType> + <xs:sequence> + <xs:element name="Source"> + <xs:complexType> + <xs:attribute name="PseudoCityCode" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="returnContextID" type="xs:boolean"/> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/spec/fixtures/wsdl/sabre/SessionCreateRQRS.xsd b/spec/fixtures/wsdl/sabre/SessionCreateRQRS.xsd new file mode 100644 index 0000000..cc3a751 --- /dev/null +++ b/spec/fixtures/wsdl/sabre/SessionCreateRQRS.xsd @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<schema targetNamespace="http://www.opentravel.org/OTA/2002/11" xmlns:tns="http://www.opentravel.org/OTA/2002/11" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> + <include schemaLocation="SessionCreateRQ.xsd"/> + <include schemaLocation="SessionCreateRS.xsd"/> +</schema> diff --git a/spec/fixtures/wsdl/sabre/SessionCreateRS.xsd b/spec/fixtures/wsdl/sabre/SessionCreateRS.xsd new file mode 100644 index 0000000..359a574 --- /dev/null +++ b/spec/fixtures/wsdl/sabre/SessionCreateRS.xsd @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="http://www.opentravel.org/OTA/2002/11" xmlns="http://www.opentravel.org/OTA/2002/11" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> + <xs:element name="SessionCreateRS"> + <xs:complexType> + <xs:sequence> + <xs:element name="Success" minOccurs="0"> + <xs:complexType/> + </xs:element> + <xs:element name="Warnings" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Warning"> + <xs:complexType> + <xs:attribute name="Language" type="xs:string" use="optional"/> + <xs:attribute name="ShortText" type="xs:string" use="optional"/> + <xs:attribute name="Type" type="xs:string" use="optional"/> + <xs:attribute name="Code" use="optional"> + <xs:annotation> + <xs:documentation xml:lang="en">If present, this refers to a table of coded values exchanged between applications to identify errors or warnings.</xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:annotation> + <xs:documentation xml:lang="en">Used for codes in the OTA code tables.</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:pattern value="[0-9A-Z]{1,3}"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="DocURL" type="xs:anyURI" use="optional"> + <xs:annotation> + <xs:documentation xml:lang="en">If present, this URL refers to an online description of the error that occurred.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="Status" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation xml:lang="en">If present, recommended values are those enumerated in the OTA_ErrorRS, (NotProcessed | Incomplete | Complete | Unknown) however, the data type is designated as string data, recognizing that trading partners may identify additional status conditions not included in the enumeration.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="Tag" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation xml:lang="en">If present, this attribute may identify an unknown or misspelled tag that caused an error in processing. It is recommended that the Tag attribute use XPath notation to identify the location of a tag in the event that more than one tag of the same name is present in the document. Alternatively, the tag name alone can be used to identify missing data [Type=ReqFieldMissing].</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="RecordId" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation xml:lang="en">If present, this attribute allows for batch processing and the identification of the record that failed amongst a group of records.</xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="ConversationId" type="xs:string" minOccurs="0"/> + <xs:element name="Errors" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Error"> + <xs:complexType> + <xs:sequence> + <xs:element name="ErrorInfo"> + <xs:complexType> + <xs:sequence> + <xs:element name="Message" type="xs:string"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="ErrorCode" type="xs:string" use="required"/> + <xs:attribute name="Severity" type="xs:string" use="required"/> + <xs:attribute name="ErrorMessage" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="EchoToken" use="optional"> + <xs:annotation> + <xs:documentation>A sequence number for additional message identification, assigned by the requesting host system. When a request message includes an echo token the corresponding response message MUST include an echo token with an identical value. </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:annotation> + <xs:documentation xml:lang="en">Used for Character Strings, length 1 to 64</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:maxLength value="64"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="TimeStamp" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation>Indicates the creation date and time of the message in UTC using the following format specified by ISO 8601; YYYY- MM- DDThh:mm:ssZ with time values using the 24 hour clock (e.g. 20 November 2003, 1:59:38 pm UTC becomes 2003-11- 20T13:59:38Z). </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="Target" use="optional" default="Production"> + <xs:annotation> + <xs:documentation>Used to indicate whether the request is for the Test or Production system.</xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="Test"/> + <xs:enumeration value="Production"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="version" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation>For all OTA versioned messages, the version of the message is indicated by a decimal value.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="SequenceNmbr" type="xs:nonNegativeInteger" use="optional"> + <xs:annotation> + <xs:documentation>Used to identify the sequence number of the transaction as assigned by the sending system; allows for an application to process messages in a certain order or to request a resynchronization of messages in the event that a system has been off-line and needs to retrieve messages that were missed. </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="PrimaryLangID" type="xs:language" use="optional"> + <xs:annotation> + <xs:documentation>Identifes the primary language preference for the form of travel represented in a collection. + The human language is identified by ISO 639 codes.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="AltLangID" type="xs:language" use="optional"> + <xs:annotation> + <xs:documentation>Identifes the primary language preference for the form of travel represented in a collection. + The human language is identified by ISO 639 codes.</xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="status" type="xs:string" use="optional"/> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/spec/fixtures/wsdl/sabre/msg-header-2_0.xsd b/spec/fixtures/wsdl/sabre/msg-header-2_0.xsd new file mode 100644 index 0000000..7ad3055 --- /dev/null +++ b/spec/fixtures/wsdl/sabre/msg-header-2_0.xsd @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Some parsers may require explicit declaration of 'xmlns:xml="http://www.w3.org/XML/1998/namespace"'. + In that case, a copy of this schema augmented with the above declaration should be cached and used + for the purpose of schema validation on ebXML messages. --> +<schema xmlns:tns="http://www.ebxml.org/namespaces/messageHeader" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.ebxml.org/namespaces/messageHeader" elementFormDefault="qualified" attributeFormDefault="qualified" version="2.0c"> + <import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd"/> + <import namespace="http://www.w3.org/1999/xlink" schemaLocation="xlink.xsd"/> + <import namespace="http://schemas.xmlsoap.org/soap/envelope/" schemaLocation="envelope.xsd"/> + <import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/> + <!-- MANIFEST, for use in soap:Body element --> + <element name="Manifest"> + <complexType> + <sequence> + <element ref="tns:Reference" maxOccurs="unbounded"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:bodyExtension.grp"/> + </complexType> + </element> + <element name="Reference"> + <complexType> + <sequence> + <element ref="tns:Schema" minOccurs="0" maxOccurs="unbounded"/> + <element ref="tns:Description" minOccurs="0" maxOccurs="unbounded"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute ref="tns:id"/> + <attribute ref="xlink:type" fixed="simple"/> + <attribute ref="xlink:href" use="required"/> + <attribute ref="xlink:role"/> + <anyAttribute namespace="##other" processContents="lax"/> + </complexType> + </element> + <element name="Schema"> + <complexType> + <attribute name="location" type="anyURI" use="required"/> + <attribute name="version" type="tns:non-empty-string"/> + </complexType> + </element> + <!-- MESSAGEHEADER, for use in soap:Header element --> + <element name="MessageHeader"> + <complexType> + <sequence> + <element ref="tns:From"/> + <element ref="tns:To"/> + <element ref="tns:CPAId"/> + <element ref="tns:ConversationId"/> + <element ref="tns:Service"/> + <element ref="tns:Action"/> + <element ref="tns:MessageData"/> + <element ref="tns:DuplicateElimination" minOccurs="0"/> + <element ref="tns:Description" minOccurs="0" maxOccurs="unbounded"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + </complexType> + </element> + <element name="CPAId" type="tns:non-empty-string"/> + <element name="ConversationId" type="tns:non-empty-string"/> + <element name="Service"> + <complexType> + <simpleContent> + <extension base="string"> + <attribute name="type" type="tns:non-empty-string"/> + </extension> + </simpleContent> + </complexType> + </element> + <element name="Action" type="tns:non-empty-string"/> + <element name="MessageData"> + <complexType> + <sequence> + <element ref="tns:MessageId"/> + <element ref="tns:Timestamp"/> + <element ref="tns:RefToMessageId" minOccurs="0"/> + <element ref="tns:TimeToLive" minOccurs="0"/> + <element ref="tns:Timeout" minOccurs="0"/> + </sequence> + </complexType> + </element> + <element name="MessageId" type="tns:non-empty-string"/> + <element name="Timeout" type="nonNegativeInteger"/> + <element name="TimeToLive" type="dateTime"/> + <element name="DuplicateElimination" type="anyType"/> + <!-- SYNC REPLY, for use in soap:Header element --> + <element name="SyncReply"> + <complexType> + <sequence> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + <attribute ref="soap:actor" use="required"/> + </complexType> + </element> + <!-- MESSAGE ORDER, for use in soap:Header element --> + <element name="MessageOrder"> + <complexType> + <sequence> + <element ref="tns:SequenceNumber"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + </complexType> + </element> + <element name="SequenceNumber" type="tns:sequenceNumber.type"/> + <!-- ACK REQUESTED, for use in soap:Header element --> + <element name="AckRequested"> + <complexType> + <sequence> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + <attribute ref="soap:actor"/> + <attribute name="signed" type="boolean" use="required"/> + </complexType> + </element> + <!-- ACKNOWLEDGMENT, for use in soap:Header element --> + <element name="Acknowledgment"> + <complexType> + <sequence> + <element ref="tns:Timestamp"/> + <element ref="tns:RefToMessageId"/> + <element ref="tns:From" minOccurs="0"/> + <element ref="tns:Reference" minOccurs="0" maxOccurs="unbounded"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + <attribute ref="soap:actor"/> + </complexType> + </element> + <!-- ERROR LIST, for use in soap:Header element --> + <element name="ErrorList"> + <complexType> + <sequence> + <element ref="tns:Error" maxOccurs="unbounded"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:headerExtension.grp"/> + <attribute name="highestSeverity" type="tns:severity.type" use="required"/> + </complexType> + </element> + <element name="Error"> + <complexType> + <sequence> + <element ref="tns:Description" minOccurs="0"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute ref="tns:id"/> + <attribute name="codeContext" type="anyURI" default="urn:oasis:names:tc:ebxml-msg:service:errors"/> + <attribute name="errorCode" type="tns:non-empty-string" use="required"/> + <attribute name="severity" type="tns:severity.type" use="required"/> + <attribute name="location" type="tns:non-empty-string"/> + <anyAttribute namespace="##other" processContents="lax"/> + </complexType> + </element> + <!-- STATUS RESPONSE, for use in soap:Body element --> + <element name="StatusResponse"> + <complexType> + <sequence> + <element ref="tns:RefToMessageId"/> + <element ref="tns:Timestamp" minOccurs="0"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:bodyExtension.grp"/> + <attribute name="messageStatus" type="tns:messageStatus.type" use="required"/> + </complexType> + </element> + <!-- STATUS REQUEST, for use in soap:Body element --> + <element name="StatusRequest"> + <complexType> + <sequence> + <element ref="tns:RefToMessageId"/> + <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attributeGroup ref="tns:bodyExtension.grp"/> + </complexType> + </element> + <!-- COMMON TYPES --> + <complexType name="sequenceNumber.type"> + <simpleContent> + <extension base="nonNegativeInteger"> + <attribute name="status" type="tns:status.type" default="Continue"/> + </extension> + </simpleContent> + </complexType> + <simpleType name="status.type"> + <restriction base="NMTOKEN"> + <enumeration value="Reset"/> + <enumeration value="Continue"/> + </restriction> + </simpleType> + <simpleType name="messageStatus.type"> + <restriction base="NMTOKEN"> + <enumeration value="UnAuthorized"/> + <enumeration value="NotRecognized"/> + <enumeration value="Received"/> + <enumeration value="Processed"/> + <enumeration value="Forwarded"/> + </restriction> + </simpleType> + <simpleType name="non-empty-string"> + <restriction base="string"> + <minLength value="1"/> + </restriction> + </simpleType> + <simpleType name="severity.type"> + <restriction base="NMTOKEN"> + <enumeration value="Warning"/> + <enumeration value="Error"/> + </restriction> + </simpleType> + <!-- COMMON ATTRIBUTES and ATTRIBUTE GROUPS --> + <attribute name="id" type="ID"/> + <attribute name="version" type="tns:non-empty-string"/> + <attributeGroup name="headerExtension.grp"> + <attribute ref="tns:id"/> + <attribute ref="tns:version" use="required"/> + <anyAttribute namespace="##other" processContents="lax"/> + <!--attribute ref="soap:mustUnderstand" use="optional"/--> + </attributeGroup> + <attributeGroup name="bodyExtension.grp"> + <attribute ref="tns:id"/> + <attribute ref="tns:version" use="required"/> + <anyAttribute namespace="##other" processContents="lax"/> + </attributeGroup> + <!-- COMMON ELEMENTS --> + <element name="PartyId"> + <complexType> + <simpleContent> + <extension base="string"> + <attribute name="type" type="tns:non-empty-string"/> + </extension> + </simpleContent> + </complexType> + </element> + <element name="To"> + <complexType> + <sequence> + <element ref="tns:PartyId" maxOccurs="unbounded"/> + <element name="Role" type="tns:non-empty-string" minOccurs="0"/> + </sequence> + </complexType> + </element> + <element name="From"> + <complexType> + <sequence> + <element ref="tns:PartyId" maxOccurs="unbounded"/> + <element name="Role" type="tns:non-empty-string" minOccurs="0"/> + </sequence> + </complexType> + </element> + <element name="Description"> + <complexType> + <simpleContent> + <extension base="string"> + <attribute ref="xml:lang" use="required"/> + </extension> + </simpleContent> + </complexType> + </element> + <element name="RefToMessageId" type="tns:non-empty-string"/> + <element name="Timestamp" type="string"/> +</schema> diff --git a/spec/fixtures/wsdl/sabre/wsse.xsd b/spec/fixtures/wsdl/sabre/wsse.xsd new file mode 100644 index 0000000..789603e --- /dev/null +++ b/spec/fixtures/wsdl/sabre/wsse.xsd @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema targetNamespace="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:xsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:app2="http://schemas.xmlsoap.org/ws/2002/12/secext" elementFormDefault="qualified" attributeFormDefault="qualified"> + <xs:element name="Security" msdata:Prefix="wsse"> + <xs:complexType> + <xs:sequence> + <xs:element name="UsernameToken" msdata:Prefix="wsse" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Username" type="xs:string" minOccurs="0" msdata:Prefix="wsse"/> + <xs:element name="Password" type="xs:string" minOccurs="0" msdata:Prefix="wsse"/> + <xs:element name="NewPassword" type="xs:string" minOccurs="0" maxOccurs="2" msdata:Prefix="wsse"/> + <xs:element name="Organization" type="xs:string" form="unqualified" minOccurs="0"/> + <xs:element name="Domain" type="xs:string" form="unqualified" minOccurs="0"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="SabreAth" msdata:Prefix="wsse" minOccurs="0" type="xs:string"/> + <element name="BinarySecurityToken" type="xs:string" minOccurs="0" msdata:Prefix="wsse"> + <!--xs:complexType> + <xs:attribute name="EncodingType" type="xs:string" use="optional"/> + <xs:attribute name="valueType" type="xs:string" use="optional"/> + </xs:complexType--> + </element> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/spec/sekken/wsdl_nested_files_spec.rb b/spec/sekken/wsdl_nested_files_spec.rb new file mode 100644 index 0000000..6125fff --- /dev/null +++ b/spec/sekken/wsdl_nested_files_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Sekken::WSDL do + describe 'with local files and imported wsdl' do + it 'does not fail to create the wsdl' do + expect{Sekken::WSDL.new(fixture('wsdl/import'), http_mock)}.to_not raise_error + end + end + + describe 'with local files and imported wsdl with two levels of import' do + it 'does not fail to create the sample_header' do + client = Sekken.new fixture('wsdl/sabre/SessionCreateRQ.wsdl') + svc = client.operation("SessionCreateRQService","SessionCreatePortType", "SessionCreateRQ"); + expect{svc.example_header}.to_not raise_error + end + end + + describe 'with local files and imported wsdl with two levels of import' do + it 'does not fail to create the sample_body' do + client = Sekken.new fixture('wsdl/sabre/SessionCreateRQ.wsdl') + svc = client.operation("SessionCreateRQService","SessionCreatePortType", "SessionCreateRQ"); + expect{svc.example_body}.to_not raise_error + end + end +end