Reordering WSDL definitions elements
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.
Hello,
Thanks for taking the time to report this.
I'll be happy accept pull requests that address this issue the following way:
- Make
ServerBaseaccept an optionalAllYourInterfaceDocumentsinstance. 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 :) - Make
Wsdl11accept anorderargument that defaults to the current order. - Patch the
Wsdl11.build_interface_documentfunction to apply the given order in the ctor. - 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,
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!
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,
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)
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()