Software
2nd December 2014 | By:

Using SOAP with SUDS – an introduction

I’m sure that most of you reading this post have heard/used the XML/SOAP – same as me.

However I’d never used the WSDL before, yet last week I needed to work with two, unconnected to each other APIs, using this protocol. So I’ve started researching  WSDL and apparently it is the standard protocol for describing API methods of web services.

WSDL stands for Web Services Description Language and is an XML document that uses SOAP to exchange information. It allows you to send requests and receive responses from a remote server. One of the interesting and useful things about this protocol is that it describes the accepted types and methods really precisely. That allows you to easily prepare empty objects (like template) which is formatted in a way the server can accept.

The most common language that we’re using in-house is Python (by the way, it’s an awesome language). The choice of available WSDL libs for python is not great and a lot of libs that exist out there are just outdated. It seems that the only one that is noteworthy is suds.

Suds is a lightweight SOAP python client for consuming Web Services. And its use is really straightforward.

from suds.client import Client
url = 'https://api-s5.postini.com/dl/api/automatedbatch.wsdl'
client = Client(url)

You can easily inspect the service types and methods by parsing client object to string.

print client



Suds ( https://fedorahosted.org/suds/ )  version: 0.4 GA  build: R699-20100913



Service ( AutomatedBatchService ) tns="http://postini.com/PSTN/SOAPAPI/v2/automatedbatch"
   Prefixes (1)
      ns0 = "http://postini.com/PSTN/SOAPAPI/v2/automatedbatch"
   Ports (1):
      (AutomatedBatchPort)
         Methods (22):
            addalias(authElem authElem, xs:string userAddressOrId, xs:string aliasAddress, xs:string confirm, )
            adddomain(authElem authElem, xs:string orgNameOrId, adddomainargs args, )
            .
            . # Other functions
            .
            suspenduser(authElem authElem, xs:string userAddressOrId, xs:string optArgStr1, xs:string optArgStr2, xs:string optArgStr3, )
            test(xs:boolean should_work, )
         Types (67):
            AdminBlockException
            AuthenticationException
            .
            . # Other types
            .
            testResponse
            userRecord

As I mentioned before we know exactly how the functions and types are built. Thereby we can use object factory provided by suds. Let’s say we want to execute the `adddomain` method. To do that we need to prepare three objects whose types are `authElem`, xs:string, adddomainargs.

auth = client.factory.create('authElem')
args = client.factory.create('adddomainargs')
print auth
(authElem){
   apiKey = None
   email = None
   pword = None
   xauth = None
}



print args
(adddomainargs){
   domain = None
}

As we can see the objects were created for us. In the case of `orgNameOrId` we don’t need to create a special type as it is just a simple string.

Anyway, now we just need to fill the created objects with proper data.

auth.apiKey = '< your api key >'
auth.email = 'info@aeguana.com'
auth.pword = '< your password >'
auth.xauth = '< your auth key >'



orgNameOrId = 'www.google.com'



args.domain = 'www.aeguana.com'

And call the method:

resp = adddomain(auth, orgNameOrId, args)

But (there is always a ‘but’), sometimes the XML is broken and/or the namespace defined in the XML document is unknown. Therefore we can not create the suds client and we are getting back an error, each time we try to do it. And as it was in my case one of the services was causing this error:

TypeNotFound: Type not found: '(Array, http://schemas.xmlsoap.org/soap/encoding/, )'

The solution for this is really easy but it’s hard to figure out what’s happening at the beginning.

I found some help by reading the post here on Parky’s Place blog.

To solve the issue we need to tell suds which namespace our XML is using. To find out what namespace it is we need to look at the XML file header.

<definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:idu" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/" name="idu" targetNamespace="urn:idu">



# XML body



</definitions>

The value from fields “xmlns:tns” and “targetNamespace” are the ones we are interested in. As we can see the namespace used in this document is “urn:idu”. So now we need to instruct the suds client to use this namespace:

tns = "urn:idu"
imp = Import('http://schemas.xmlsoap.org/soap/encoding/', 'http://schemas.xmlsoap.org/soap/encoding/')
imp.filter.add(tns)



client = Client(url, plugins=[ImportDoctor(imp)])

That’s it, our client is ready to work now!

Tags: , , ,