Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gowsdl does not respect the targetNamespace value on wsdl definition. #257

Open
kmirzavaziri opened this issue Jul 16, 2023 · 0 comments
Open

Comments

@kmirzavaziri
Copy link

kmirzavaziri commented Jul 16, 2023

Assume we have the following wsdl:

<wsdl:definitions
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:ns5="TestNamespace"
        xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
        xmlns:tns="https://example.com" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        targetNamespace="https://example.com"
>
    <wsp:Policy wsu:Id="UTOverTransport">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                        <sp:TransportToken>
                            <wsp:Policy>
                                <sp:HttpsToken RequireClientCertificate="false"/>
                            </wsp:Policy>
                        </sp:TransportToken>
                        <sp:AlgorithmSuite>
                            <wsp:Policy>
                                <sp:Basic256/>
                            </wsp:Policy>
                        </sp:AlgorithmSuite>
                        <sp:Layout>
                            <wsp:Policy>
                                <sp:Lax/>
                            </wsp:Policy>
                        </sp:Layout>
                        <sp:IncludeTimestamp/>
                    </wsp:Policy>
                </sp:TransportBinding>
                <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                    <wsp:Policy>
                        <sp:UsernameToken
                                sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"/>
                    </wsp:Policy>
                </sp:SignedSupportingTokens>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
    <wsdl:types>
        <xsd:schema elementFormDefault="qualified" targetNamespace="https://example.com">
            <xsd:import namespace="TestNamespace" targetNamespace="https://example.com"/>
            <xsd:element name="Request">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element minOccurs="0" name="filter" nillable="true" type="ns5:Filter"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="Response">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element minOccurs="0" name="Message" nillable="true" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
        <xsd:schema elementFormDefault="qualified" targetNamespace="TestNamespace"> <!-- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Note targetNamespace="TestNamespace" Here -->
            <xsd:complexType name="Filter">
                <xsd:sequence>
                    <xsd:element minOccurs="0" name="SomeParameter" nillable="true" type="xsd:string"/>
                    <xsd:element minOccurs="0" name="SomeNestedParameter" nillable="true" type="ns5:NestedType"/>
                </xsd:sequence>
            </xsd:complexType>
            <xsd:complexType name="NestedType">
                <xsd:sequence>
                    <xsd:element minOccurs="0" name="SomeInnerParameter" nillable="true" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
            <xsd:element name="Filter" nillable="true" type="ns5:Filter"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="Req">
        <wsdl:part name="parameters" element="tns:Request"/>
    </wsdl:message>
    <wsdl:message name="Resp">
        <wsdl:part name="parameters" element="tns:Response"/>
    </wsdl:message>
    <wsdl:portType name="TestPortType" wsp:PolicyURIs="#UTOverTransport">
        <wsdl:operation name="TestOperation">
            <wsdl:input message="tns:Req"/>
            <wsdl:output message="tns:Resp"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="TestBinding" type="tns:TestPortType">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <wsdl:operation name="TestOperation">
            <soap:operation soapAction="https://example.com/TestService/TestOperation" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="TestService">
        <wsdl:port name="TestPort" binding="tns:TestBinding">
            <soap:address
                    location="https://example.com/services/TestService.TestPort"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Using zeep in python I can generate a valid request:

from requests import Session

from zeep import Client, Transport
from zeep.proxy import ServiceProxy
from zeep.wsdl.utils import etree_to_string

session = Session()

client = Client(
    "buggy-example.xml",
    transport=Transport(cache=None),
)

service: ServiceProxy = client.create_service(
    "{https://example.com}TestBinding",
    "https://example.com",
)

request_filter = {
    "SomeParameter": "SomeValue",
    "SomeNestedParameter": {
        "SomeInnerParameter": "InnerValue"
    },
}

node = client.create_message(client.service, 'TestOperation', filter=request_filter)

print(etree_to_string(node).decode())

The result is

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:Body>
        <ns0:Request xmlns:ns0="https://example.com">
            <ns0:filter>
                <ns1:SomeParameter xmlns:ns1="TestNamespace">SomeValue</ns1:SomeParameter>
                <ns2:SomeNestedParameter xmlns:ns2="TestNamespace">
                    <ns2:SomeInnerParameter>InnerValue</ns2:SomeInnerParameter>
                </ns2:SomeNestedParameter>
            </ns0:filter>
        </ns0:Request>
    </soap-env:Body>
</soap-env:Envelope>

But using gowsdl I cannot.

 gowsdl buggy-example.xml 
🍀  Reading file /home/kamyar/repos/tmp/go/pg/buggy-example.xml
🍀  [WARN] Don't know where to find XSD for TestNamespace
🍀  Done 👍

With the following we can see the generated xml

	const XmlNsSoapEnv string = "http://schemas.xmlsoap.org/soap/envelope/"

	someValue := "SomeValue"
	innerValue := "InnerValue"

	request := &myservice.Request{
		Filter: &myservice.Filter{
			SomeParameter: &someValue,
			SomeNestedParameter: &myservice.NestedType{
				SomeInnerParameter: &innerValue,
			},
		},
	}

	envelope := soap.SOAPEnvelope{
		XmlNS: XmlNsSoapEnv,
	}

	envelope.Body.Content = request

	buffer := new(bytes.Buffer)

	encoder := xml.NewEncoder(buffer)

	if err := encoder.Encode(envelope); err != nil {
		panic(err)
	}
	if err := encoder.Flush(); err != nil {
		panic(err)
	}

	fmt.Println(buffer.String())

Which is

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <Request xmlns="https://example.com">  <!-- We have xmlns here which is a good thing -->
            <filter>
                <SomeParameter>SomeValue</SomeParameter> <!-- Note the lack of xmlns="TestNamespace" Here -->
                <SomeNestedParameter>  <!-- And Here -->
                    <SomeInnerParameter>InnerValue</SomeInnerParameter>
                </SomeNestedParameter>
            </filter>
        </Request>
    </soap:Body>
</soap:Envelope>

The generated code looks like

type Request struct {
	XMLName xml.Name `xml:"https://example.com Request"`
        // I believe this parameter is used to add
        // xmlns="https://example.com"
        // to the request which is the same as zeep response

	Filter *Filter `xml:"filter,omitempty" json:"filter,omitempty"`
}

type Response struct {
	XMLName xml.Name `xml:"https://example.com Response"`

	Message *string `xml:"Message,omitempty" json:"Message,omitempty"`
}

type Filter struct {
	SomeParameter *string `xml:"SomeParameter,omitempty" json:"SomeParameter,omitempty"`
        // (see challenge 1)

	SomeNestedParameter *NestedType `xml:"SomeNestedParameter,omitempty" json:"SomeNestedParameter,omitempty"`
}

type NestedType struct {
        // Here we also need something like
        // XMLName xml.Name `xml:"TestNamespace SomeNestedParameter"`
        // but it is not generated (see challenge 2)

	SomeInnerParameter *string `xml:"SomeInnerParameter,omitempty" json:"SomeInnerParameter,omitempty"`
}

Challenges

  1. The method used to add xmlns attribute does not work for a primitive (non-struct) types. But there is this workaround:
type Filter struct {
	SomeParameter *SomeParameter `xml:"SomeParameter,omitempty" json:"SomeParameter,omitempty"`
        // (see challenge 1)

	SomeNestedParameter *NestedType `xml:"SomeNestedParameter,omitempty" json:"SomeNestedParameter,omitempty"`
}


type SomeParameter struct {
	XMLName xml.Name `xml:"TestNamespace SomeParameter"`

	Value string `xml:",chardata"` // <- Note here we make it to become the root value of the containing node
}
  1. The second challenge is that we need to include the name of this type in the parent parameter inside the type definition which is impossible since these struct types may be reused in multiple messages with different names.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant