Pregunta

Estoy intentando escribir un consumidor Python para Sage CRM utilizando su interfaz de servicios Web. Estoy usando SOAPpy como biblioteca de SOAP Python (no casado con ella, era fácil de instalar en Ubuntu, así que fuimos con él).

administrado a buscar el WSDL utilizando el proxy y ejecutado el método de inicio de sesión expuestas por Sage CRM.


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

Devuelve un objeto de sesión, que tipo de parece

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

Ahora estoy tratando de utilizar el método queryrecord de Sage CRM para consultar los datos, y que vuelve

Fault SOAP-ENV:Server: No active usersession detected

La lectura de la documentación revela que tengo que enviar de nuevo a la ID de sesión que RecD. cuando entré en la espalda con cada uno de solicitudes.

De acuerdo con la documentación Sage tengo que devolverlo como tal


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

¿Alguna idea de cómo hacer adjuntar la presente a las cabeceras utilizando SOAPpy?

Cualquier punteros serán muy apreciados.

¿Fue útil?

Solución

En primer lugar, sólo para comentar sobre SOAPpy frente a otras bibliotecas de jabón ... SOAPpy tradicionalmente ha sido la biblioteca fácil de usar que no apoyaron las estructuras de datos complejas. Así que si funciona para el caso, entonces usted es mejor. Para estructuras de datos más complejas en el WSDL que había necesidad de mover ZSI.

En cualquier caso, el enlace de documentación para el proyecto en SourceForge parece estar roto, así que voy a cortar + pegar la documentación cabecera aquí con algunos cambios de formato de menor importancia:

Cómo utilizar cabeceras

SOAPpy tiene una clase de cabecera para almacenar los datos de la cabecera de un mensaje SOAP. Cada instancia de cabecera tiene métodos para establecer / obtener el atributo MustUnderstand, y métodos para establecer / obtener el atributo Actor.

SOAPpy también tiene una clase SOAPContext para que cada servidor puede ser el método implementado de tal manera que se pone el contexto del cliente que se conecta. Esto incluye tanto la información de SOAP común y la información de conexión (ver a continuación para un ejemplo).

Ejemplos de cliente

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)

Esto debería tener éxito (siempre que el servidor ha definido echoInteger), ya que construye una cabecera válida en este cliente con MustUnderstand puesto a 0 y luego envía el jabón con esta cabecera.

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)

Esto debe fallar (incluso si el servidor ha definido 'echoInteger'), ya que construye una cabecera válida en este cliente, pero establece MustUnderstand a 1 para un mensaje que el servidor de suponer que no va a entender antes de enviar.

Otros consejos

soy sólo calcular esto también. Tengo el núcleo de este hecho, sin embargo, por lo que este debe acelerar las cosas para cualquier otra persona que necesita este! Estoy asumiendo que otros subsistemas Sage funcionan de la misma manera (pero todavía no sé), así que traté de dar cuenta de esa posibilidad.

En primer lugar, necesita este módulo que escribí llamado pySage.py. Se define una clase para el funcionamiento de un proceso que tendrá que subclase para su uso personalizado.

#----------------------------
#
# pySage.py
#
# Author: BuvinJ
# Created: December, 2015
#
# This module defines the SageProcess class.
# This class handles connecting and disconnecting
# to Sage web services, and provides an object
# through which the web services can be accessed.
#
#----------------------------

# Download SOAPpy from: https://pypi.python.org/pypi/SOAPpy
from SOAPpy import WSDL, Types as soapTypes

from enum import Enum
SageSubsystem = Enum( 'SageSubsystem', 'CRM' )

# Define your sub system connection parameters here.
CRM_WSDL_FILE_URL = "http://CRMservername/CRMinstallname/eWare.dll/webservice/webservice.wsdl"    
CRM_USER          = "admin"
CRM_PASSWORD      = ""
CRM_NAMESPACE     = "http://tempuri.org/type"

#----------------------------
# SageProcess Class

# To use this class:
#
# 1) Create a SageProcess subclass and define the method 
#    body(). 
# 2) Instanitate an instance of the subclass, passing a
#    SageSubsystem enumeration, e.g. SageSubsystem.CRM.
# 3) Invoke the run() method of the process instance.
#    This will in turn invoke body() to execute your 
#    custom actions.
#
# To access the sage web services, use the "sage" member of 
# a SageProcess instance. For help using Sage web service
# objects see: 
# https://community.sagecrm.com/developerhelp/default.htm
#
# You may also invoke the sageHelp() method of a SageProcess 
# instance to view the top level web service object members. 
# Or, recordHelp( sageRecord ) to view the members of the 
# various record objects returned by the web services.
#
#----------------------------
class SageProcess():

    # Construction & service connection methods
    #----------------------------

    def __init__( self, subsystem, verbose=False ):
        """
        @param subsystem: The Sage subsystem on which to run the process. Ex. SageSubsystem.CRM
        @param verbose: If True, details of the SOAP exchange are displayed.
        """        
        self.subsystem = subsystem
        self.verbose = verbose        
        if self.subsystem == SageSubsystem.CRM :
            self.wsdl      = CRM_WSDL_FILE_URL
            self.namespace = CRM_NAMESPACE
            self.username  = CRM_USER
            self.password  = CRM_PASSWORD
        else :
            self.abort( "Unknown subsystem specified!" )   
        self.sessionId = None                    
        self.connect()

    def connect( self ) :               
        try :
            self.sage = WSDL.Proxy( self.wsdl, namespace=self.namespace )            
        except :
            self.abort( "WSDL failure. This is may be caused by access settings. File url: " + CRM_WSDL_FILE_URL )                
        if self.verbose : "Connected to web service."
        self.soapProxy = self.sage.soapproxy        
        if self.verbose : self.sageDebug()

    # Core process methods
    #----------------------------        

    def run( self ) :        
        if not self.logOn( self.username, self.password ) : 
            self.abort( "Log On failed!" )    
        else :
            if self.verbose : 
                print "Logged On. Session Id: " + str( self.sessionId )            
        self.appendSessionHeader()               
        self.body()
        if self.logOff() : 
            if self.verbose : print "Logged Off."

    def logOn( self, username, password ) : 
        try : self.sessionId = self.sage.logon( username, password ).sessionid
        except Exception as e: 
            self.abortOnException( "Log On failure.\n(You may need to enable forced logins.)", e )
        return (self.sessionId is not None)

    def appendSessionHeader( self ) : 
        self.soapProxy = self.soapProxy._sa( "urn:sessionid" )
        soapHeader = soapTypes.headerType()
        soapHeader.SessionHeader = soapTypes.structType( None, "SessionHeader" )
        soapHeader.SessionHeader.sessionId = soapTypes.stringType( self.sessionId )
        self.soapProxy = self.soapProxy._hd( soapHeader )
        self.sage.soapproxy = self.soapProxy

    def body( self ) : 
        """
        You should override this method when you subclass SageProcess. 
        It will be called after logging on, and will be followed by logging off.
        Use self.sage to access the system from within this method.
        """

    def logOff( self ) : 
        success = False
        try : success = self.sage.logoff( self.sessionId )
        except Exception as e: self.abortOnException( "Log off failure.", e )   
        return success

    # Helper methods
    #----------------------------   

    # Immediately exit the program with an indication of success
    def quit( self, msg=None ) :
        if msg is not None: print msg
        import os
        os._exit( 0 )      

    # Immediately exit the program with an error
    def abort( self, msg=None, errorCode=1 ) :        
        if msg is not None: print msg
        print "Process terminated..."
        import os
        os._exit( errorCode )      

    # Immediately exit the program with an Exception error
    def abortOnException( self, e, msg=None, errorCode=1 ) :
        if msg is not None: print msg
        print ""
        print e 
        print ""
        self.abort( None, errorCode )      

    def sageDebug( self, enable=True ) : 
        if enable : self.soapProxy.config.debug = 1
        else :      self.soapProxy.config.debug = 0       

    def sageHelp( self ) : 
        print "\nSage web service methods:\n"        
        self.sage.show_methods()

    def recordHelp( self, record, typeDescr=None ) :         
        if record is None : return
        print ""        
        description = "record object members:\n"
        if typeDescr is not None : 
            description = typeDescr + " " + description
        print description
        print dir( record )
        print ""

A continuación, agregue un cliente para esta clase. He aquí un ejemplo que he creado llamado fetch_company_data_example.py:

#----------------------------
#
#  fetch_company_data_example.py
#
#----------------------------

from pySage import SageProcess, SageSubsystem

def main() :

    # Get process parameters from the command line
    import sys
    try :
        companyId = sys.argv[1]
    except : 
        abort( "Usage: " + sys.argv[0] + " companyId [-v] [--verbose]" )       
    verbose = False
    try :
        if ( sys.argv[2] == "-v" or 
             sys.argv[2] == "--verbose" ) :
            verbose = True
    except : pass

    # Create & run the custom Sage process
    process = FetchCompanyDataProcess( companyId, verbose )
    process.run()

class FetchCompanyDataProcess( SageProcess ):

    def __init__( self, companyId, verbose ):
        SageProcess.__init__( self, SageSubsystem.CRM, verbose )
        self.companyId = companyId

    def body( self ):   

        # Fetch the company record (exiting if no data is returned)
        companyRecord = self.getCompanyRecord()
        if companyRecord is None: self.quit( "\nNo records found.\n" )                

        # Uncomment for development help...
        #if self.verbose : self.recordHelp( companyRecord, "Company" )        
        #if self.verbose : self.recordHelp( companyRecord.address.records, "Address" )        

        # Print some of the company info 
        print "" 
        print "Company Id: " + self.companyId
        print "Name: " + companyRecord.name
        print "Location: " + self.getCompanyLocation( companyRecord )
        print ""

    def getCompanyRecord( self ) :     
        try : 
            queryentity = self.sage.queryentity( self.companyId, "company" )
        except Exception as e: 
            self.abortOnException( "Get Company Record failure.", e )
        try : return queryentity.records
        except : return None 

    def getCompanyLocation( self, companyRecord ) :     
        try : 
            return (companyRecord.address.records.city + ", " +
                    companyRecord.address.records.state)
        except : return ""

# Entry point        
if __name__ == '__main__' : main()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top