Search code examples
pythonsoapcrmsage-crm

Sage CRM web services example in Python


I am trying to write a Python consumer for Sage CRM using their Web Services interface. I am using SOAPpy as Python SOAP library (not married to it, was easy to install on Ubuntu so went with it).

Managed to fetch the WSDL using the Proxy and executed the logon method exposed by Sage CRM.


from SOAPpy import *
proxy = WSDL.Proxy('http://192.168.0.3/MATE/eware.dll/webservice/webservice.wsdl')

It returns a session object which sort of looks like

SOAPpy.Types.structType result at 151924492: {'sessionid': '170911104429792'}

Now I am trying to use Sage CRM's queryrecord method to query the data, and that returns

Fault SOAP-ENV:Server: No active usersession detected

Reading the documentation reveals that I have to send back to the session id that recd. when I logged in back with each requests.

According to the Sage documentation I have to send it back as such


SID = binding.logon("admin", ""); 
binding.SessionHeaderValue = new SessionHeader(); 
binding.SessionHeaderValue.sessionId = SID.sessionid;

Any ideas how to do append this to the headers using SOAPpy?

Any pointers will be greatly appreciated.


Solution

  • First, just to comment on SOAPpy vs. other soap libraries... SOAPpy traditionally has been the easy-to-use library that didn't support complex data structures. So if it works for your case then you are better off. For more complex data structures in the WSDL you'd need to move ZSI.

    Anyway, the documentation link for the project on SourceForge seems to be broken, so I'll just cut+paste the header documentation here with some minor formatting changes:

    Using Headers

    SOAPpy has a Header class to hold data for the header of a SOAP message. Each Header instance has methods to set/get the MustUnderstand attribute, and methods to set/get the Actor attribute.

    SOAPpy also has a SOAPContext class so that each server method can be implemented in such a way that it gets the context of the connecting client. This includes both common SOAP information and connection information (see below for an example).

    Client Examples

    import SOAPpy
    test = 42
    server = SOAPpy.SOAPProxy("http://localhost:8888")
    server = server._sa ("urn:soapinterop")
    
    hd = SOAPpy.Header()
    hd.InteropTestHeader ='This should fault, as you don\'t understand the header.'
    hd._setMustUnderstand ('InteropTestHeader', 0)
    hd._setActor ('InteropTestHeader','http://schemas.xmlsoap.org/soap/actor/next')
    server = server._hd (hd)
    
    print server.echoInteger (test)
    

    This should succeed (provided the server has defined echoInteger), as it builds a valid header into this client with MustUnderstand set to 0 and then sends the SOAP with this header.

    import SOAPpy
    test = 42
    server = SOAPpy.SOAPProxy("http://localhost:8888")
    server = server._sa ("urn:soapinterop")
    #Header
    hd = SOAPpy.Header()
    hd.InteropTestHeader = 'This should fault,as you don\'t understand the header.'
    hd._setMustUnderstand ('InteropTestHeader', 1)
    hd._setActor ('InteropTestHeader','http://schemas.xmlsoap.org/soap/actor/next')
    server = server._hd (hd)
    
    print server.echoInteger (test)
    

    This should fail (even if the server has defined 'echoInteger'), as it builds a valid header into this client, but sets MustUnderstand to 1 for a message that the server presumably won't understand before sending.