Frage

Ich versuche, mithilfe der Web Services-Schnittstelle einen Python-Consumer für Sage CRM zu schreiben.Ich verwende SOAPpy als Python-SOAP-Bibliothek (nicht damit verbunden, war unter Ubuntu einfach zu installieren, also ging es damit).

Es ist mir gelungen, die WSDL mithilfe des Proxys abzurufen und die von Sage CRM bereitgestellte Anmeldemethode auszuführen.


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

Es gibt ein Sitzungsobjekt zurück, das so aussieht

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

Jetzt versuche ich, die QueryRecord-Methode von Sage CRM zu verwenden, um die Daten abzufragen, und das Ergebnis wird zurückgegeben

Fault SOAP-ENV:Server: No active usersession detected

Das Lesen der Dokumentation zeigt, dass ich die Sitzungs-ID zurücksenden muss, die aufgezeichnet wurde.als ich mich bei jeder Anfrage wieder anmeldete.

Laut Sage-Dokumentation muss ich es als solches zurückschicken


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

Irgendwelche Ideen, wie man dies mit SOAPpy an die Header anhängen kann?

Für Hinweise sind wir sehr dankbar.

War es hilfreich?

Lösung

Zunächst nur ein Kommentar zu SOAPpy vs.andere Soap-Bibliotheken...SOAPpy war traditionell die benutzerfreundliche Bibliothek, die keine komplexen Datenstrukturen unterstützte.Wenn es also in Ihrem Fall funktioniert, sind Sie besser dran.Für komplexere Datenstrukturen in der WSDL müssten Sie ZSI verschieben.

Wie auch immer, der Dokumentationslink für das Projekt auf SourceForge scheint defekt zu sein, daher schneide ich hier einfach die Header-Dokumentation aus und füge sie mit einigen geringfügigen Formatierungsänderungen ein:

Verwenden von Headern

SOAPpy verfügt über eine Header-Klasse zum Speichern von Daten für den Header einer SOAP-Nachricht.Jede Header -Instanz verfügt über Methoden, um das Attribut "Mustverstand" zu setzen/abzurufen, und Methoden zum Festlegen/Abrufen des Akteurattributs.

Soappy hat auch eine SOAPCONTEXT -Klasse, sodass jede Servermethode so implementiert werden kann, dass sie den Kontext des Connecting -Clients erhält.Dies umfasst sowohl gemeinsame SOAP -Informationen als auch Verbindungsinformationen (siehe unten für ein Beispiel).

Kundenbeispiele

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)

Dies sollte erfolgreich sein (vorausgesetzt, der Server hat den Echointeneger definiert), da er einen gültigen Header in diesen Client mit dem auf 0 festgelegten Must -Verhalten aufbaut und dann die Seife mit diesem Header sendet.

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)

Dies sollte fehlschlagen (auch wenn der Server "EchointeEger" definiert hat), da er einen gültigen Kopfzeilen in diesen Client aufbaut, aber auf 1 muss, um eine Nachricht zu erhalten, die der Server vermutlich vor dem Senden nicht versteht.

Andere Tipps

Am Bezifferung nur dies auch aus. Ich habe den Kern dieses zwar getan, so dass dies sollte Dinge für jemanden anderen beschleunigen, die dies braucht! Ich gehe davon aus anderen Sage-Subsysteme die gleiche Weise (aber ich weiß noch nicht), so habe ich versucht, diese Möglichkeit zu berücksichtigen.

Als erstes müssen Sie dieses Modul I pySage.py genannt geschrieben hätte. Es definiert eine Klasse für einen Prozess ausgeführt wird, die Sie benötigen für Ihre individuelle Anwendung zu Unterklasse.

#----------------------------
#
# 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 ""

Dann fügen Sie einen Client für diese Klasse. Hier ist ein Beispiel, das ich erstellt genannt 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()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top