spyne icon indicating copy to clipboard operation
spyne copied to clipboard

Reordering WSDL definitions elements

Open rayrapetyan opened this issue 11 years ago • 5 comments

Hi. I'll start from the issue described here: http://scn.sap.com/thread/1627690. In short, SAP awaits elements defined in the following order: types;message;portType;binding;service, while "spyne" always produces WSDL with "service" element coming before "portType" element (spyne/wsdl11.py, build_interface_document function). Anyway, such WSDL passes SOAPUI WS-I tool validation perfectly well, it also passes validation of our "Java" clients. There are also no any strict ordering rules defined in the reference doc: http://www.w3.org/TR/wsdl#_document-s.

But, as per wiki, Oracle, MS, O'Reily books, SAP... (and many other sources), "service" should always be the last element. Proof links (well, actually they are not proofs, because of the reasons described above):

http://en.wikipedia.org/wiki/Web_Services_Description_Language http://msdn.microsoft.com/en-us/library/ms996486.aspx http://download.oracle.com/otn_hosted_doc/jdeveloper/1012/web_services/ws_wsdlstructure.html

I think the best solution here would be to allow to define "definitions" elements order when creating service. Thanks.

rayrapetyan avatar Aug 17 '14 03:08 rayrapetyan

Hello,

Thanks for taking the time to report this.

I'll be happy accept pull requests that address this issue the following way:

  1. Make ServerBase accept an optional AllYourInterfaceDocuments instance. You could suggest another name for that class while you're at it, but even though I happen to more-or-less like it, I promise I'll be reasonable :)
  2. Make Wsdl11 accept an order argument that defaults to the current order.
  3. Patch the Wsdl11.build_interface_document function to apply the given order in the ctor.
  4. It'd be fantastic if there were actual tests in C# that can be run in whatever version of mono is fashionable nowadays, but I'll make do with just a test that checks the order of tags in the wsdl document.

Best regards,

plq avatar Aug 17 '14 23:08 plq

Hello.

Sorry, I've done a little of research before posting this issue, but here is a quick workaround - just a simple patch in _on_wsdl_document_built callback:

def _on_wsdl_document_built(doc):
    service_el = doc.root_elt.find('{http://schemas.xmlsoap.org/wsdl/}service')
    if service_el:
        doc.root_elt.remove(service_el)
        doc.root_elt.append(service_el)

I think spynes' wsdl generator provides enough mechanisms (I was simply unaware of a callback), no need to make additional interface changes for such a simple issue (certainly it's up to you to decide).

In any case - just leave a note somewhere in docs, it may be useful for developers which deal with SAP-like clients...

Thank you for your wonderful project!

rayrapetyan avatar Aug 17 '14 23:08 rayrapetyan

Hey, thanks for the workaround, it's always nice to have these around :)

However I still would prefer this to be implemented cleanly, so I won't close the issue.

Thanks for using spyne :)

cheers,

plq avatar Aug 17 '14 23:08 plq

Hi, I'm also facing this problem; regrettably I don't have the time right now to fix the issue and submit a PR, but I managed to tweak the snippet in https://github.com/arskom/spyne/issues/389#issuecomment-52439619 to fit my use case, which involves more than one service:


def on_wsdl_document_built(doc):
    services = doc.root_elt.findall('{http://schemas.xmlsoap.org/wsdl/}service')
    for service in services:
        doc.root_elt.remove(service)
        doc.root_elt.append(service)

wsgi_app.doc.wsdl11.event_manager.add_listener("wsdl_document_built", on_wsdl_document_built)

RazZziel avatar Jan 25 '17 16:01 RazZziel

I just wanted to provide the patch in a complete example

def on_wsdl_document_built(doc):
    services = doc.root_elt.findall('{http://schemas.xmlsoap.org/wsdl/}service')
    for service in services:
        doc.root_elt.remove(service)
        doc.root_elt.append(service)

class HelloWorldService(ServiceBase):
    @rpc(Unicode, Integer, _returns=Iterable(Unicode))
    def say_hello(ctx, name, times):
        for i in range(times):
            yield 'Hello, %s' % name

application = Application([HelloWorldService],
    tns='spyne.examples.hello',
    in_protocol=Soap11(validator='lxml'),
    out_protocol=Soap11()
)

application.doc.wsdl11.event_manager.add_listener("wsdl_document_built", on_wsdl_document_built)

if __name__ == '__main__':
    # You can use any Wsgi server. Here, we chose
    # Python's built-in wsgi server but you're not
    # supposed to use it in production.
    from wsgiref.simple_server import make_server
    wsgi_app = WsgiApplication(application)
    server = make_server('0.0.0.0', 8000, wsgi_app)
    server.serve_forever()

jeremyforan avatar Aug 07 '19 19:08 jeremyforan