Reputation: 43
I am trying to talk to a SOAP web service using SUDS and Python. After lots of messing around learning Python (yes I am new to this) and working out how to use SUDS I have come across a problem.
The signature of the web method I am calling, according to suds, is
(FWTCaseCreate){
ClassificationEventCode = None
Priority = None
Title = None
Description = None
Queue = None
DueDate = None
AssociatedObject =
(FWTObjectBriefDetails){
ObjectID =
(FWTObjectID){
ObjectType = None
ObjectReference[] = <empty>
}
ObjectDescription = None
Details = None
Category = None
}
Form =
(FWTCaseForm){
FormField[] = <empty>
FormName = None
FormKey = None
}
Internal = None
InteractionID = None
XCoord = None
YCoord = None
}
So I use SUDS to create the classes that I want and send it to the method. However I get an error. So I turned logging on and I can see that the XML that is being sent is not correct which is causing a deserialize error. The SOAP package looks like the following
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://www.CRM.com/wsdl/FLTypes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<wsse:Security>
<wsse:BinarySecurityToken>eaadf1ddff99a8</wsse:BinarySecurityToken>
</wsse:Security>
</SOAP-ENV:Header>
<ns1:Body>
<ns0:FWTCaseCreate>
<ClassificationEventCode>
<ClassificationEventCode>2000023</ClassificationEventCode>
<Priority>1</Priority>
<Title>testing</Title>
<Description>testing</Description>
<Queue/>
<Internal>True</Internal>
<XCoord>356570</XCoord>
<YCoord>168708</YCoord>
</ClassificationEventCode>
</ns0:FWTCaseCreate>
</ns1:Body>
As you can see there is a 'ClassificationEventCode' element around all the other elements, this should not be there. If I cut and paste this xml into SOAPUI and first remove this element and then post it directly to the web service it works successfully.
Here is the code I am using to make the call
client = Client(url)
#Add a header for the security
ssnns = ('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')
ssn = Element('BinarySecurityToken', ns=ssnns).setText(binaryKey)
ssn1 = Element('Security',ns=ssnns)
ssn1.append(ssn)
client.set_options(soapheaders=ssn1)
newCase = client.factory.create('ns1:FWTCaseCreate')
classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023
newCase.ClassificationEventCode = classEventCode
newCase.Priority = 1
#optional
newCase.AssociatedObject = None
#optional
newCase.Form = None
#optional
newCase.Internal = None
#optional
newCase.InteractionID = None
#optional
newCase.DueDate = None
#optional
newCase.Queue = None
newCase.Title = 'Title'
newCase.Description = 'description'
newCase.XCoord = '356570'
newCase.YCoord = '168708'
caseID = client.service.createCase(newCase)
Does anyone have any ideas why this is happening? I guess SUDS thinks that it should be there based on the WSDL.
Thanks.
Upvotes: 3
Views: 5811
Reputation: 304
I've found this thread searching for the solution of the same problem. So far I've researched it happens only when you pass factory created object directly to service method. And only with wsdl data types using extension (inheritance).
There is more solutions I could think of.
I've chose the last one, as it is easiest way. So there is the code.
def sudsToDict(data):
return dict([(str(key),val) for key,val in data])
Use like this.
data = client.factory.create('wsdl_data_type')
# now fill with values and then
data = sudsToDict(data)
client.service.some_method(**data)
Upvotes: 0
Reputation: 1255
I was getting exactly the same problem. The sequence of parameters in my SOAP request is being wrapped in an element with the same name as the first parameter. e.g.
....
<ns0:Body>
<ns1:CreationReq>
<ReqType>
<ReqType>1</ReqType>
<Title>Mr</Title>
....
</ReqType>
</ns1:CreationReq>
</ns0:Body>
....
I've checked the WSDL over to make sure there is no problem with it.
The problem it seems is because I created a CreationReq object using the client.factory.create method. Checking the client by printing it shows that the method I am calling does not take that object as a parameter. Rather it takes a list of named args.
So my code was:
req = client.factory.create('CreationReq')
req.ReqType = 1
req.Title = 'Mr'
resp = client.service.Create(req)
Now it is:
req = {}
req['ReqType'] = 1
req['Title'] = 'Mr'
resp = client.service.Create(**req)
Upvotes: 6
Reputation: 23
If you create a client for your suds services, there are some attributes that you can see to determine which objects are needed to pass into the service call.
For example:
import suds
client = suds.Client(url)
for a in client.sd: #print the whole service definition
print a
This should show you the prefixes, ports with methods, and types. For your code, you should be able to see what needs to be passed in the service call to createCase. Even though the WSDL may define the method as needing a 'FWTCaseCreate', suds may be picking up the definition for createCase to need ClassificationEventCode, Priority, Title types, etc.
Therefore you wouldn't want to do: (which passes in newCase for the first argument, putting all the details under that tag)
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase)
But instead call the service like so: (based on the service definition)
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(newCase.ClassificationEventCode, newCase.Priority, ...)
Or maybe:
newCase = client.factory.create('ns1:FWTCaseCreate')
caseID = client.service.createCase(*[getattr(newCase,a) for a in newCase.__keylist__])
Passing in the list of args that are required for the service call.
I don't know why the service call definition is incorrectly resolved as being what it is, but passing in the correct object doesn't expand automatically to the correct argument list needed. Perhaps reading the suds source ( http://jortel.fedorapeople.org/suds/doc/ ) would help divulge the answer.
Upvotes: 1
Reputation: 41695
Your creating the element twice. Remove this:
classEventCode = client.factory.create('ns1:FWTEventCode')
classEventCode.value = 2000023
And change this:
newcase.ClassificationEventCode = 2000023
This should remove that extra tag.
Upvotes: 0
Reputation: 32
Are you going to be using this as a config file, or to store infomation. Or is this to send data across the web?
Okay then, if this is so then why not use json or json-rpc, they are said to be much faster, easier to parse and far easier to read. XML is a horible data type and I personally can't wait till it dies, if your looking for data sending it would be well worth using the json's.
Upvotes: 0