Python中的Sage CRM web服务示例
我正在尝试使用sagecrm的Web服务接口为其编写Python消费者。我使用SOAPpy作为pythonsoap库(并没有与之结合,在Ubuntu上很容易安装,所以就随它一起去了) 管理使用代理获取WSDL,并执行Sage CRM公开的登录方法Python中的Sage CRM web服务示例,python,soap,crm,sage-crm,Python,Soap,Crm,Sage Crm,我正在尝试使用sagecrm的Web服务接口为其编写Python消费者。我使用SOAPpy作为pythonsoap库(并没有与之结合,在Ubuntu上很容易安装,所以就随它一起去了) 管理使用代理获取WSDL,并执行Sage CRM公开的登录方法 from SOAPpy import * proxy = WSDL.Proxy('http://192.168.0.3/MATE/eware.dll/webservice/webservice.wsdl') 它返回一个session对象,看起来像
from SOAPpy import *
proxy = WSDL.Proxy('http://192.168.0.3/MATE/eware.dll/webservice/webservice.wsdl')
它返回一个session对象,看起来像
SOAPpy.Types.structType result at 151924492: {'sessionid': '170911104429792'}
如何使用SOAPpy将其附加到标题中
任何提示都将不胜感激。首先,请评论一下SOAPpy与其他soap库的比较。。。SOAPpy传统上是一个易于使用的库,不支持复杂的数据结构。因此,如果它对你的情况有效,那么你的境况会更好。对于WSDL中更复杂的数据结构,您需要移动ZSI 无论如何,SourceForge上项目的文档链接似乎已断开,因此我将在这里剪切并粘贴标题文档,并对格式进行一些小的更改: 使用标题 SOAPpy有一个Header类来保存SOAP消息头的数据。 每个标头实例都有设置/获取MustUnderstand属性的方法,以及 方法来设置/获取Actor属性 SOAPpy还有一个SOAPContext类,这样每个服务器方法都可以 以获取连接客户端的上下文的方式实现。 这包括公共SOAP信息和连接信息(请参阅 下面是一个例子) 客户示例 这应该会成功(只要服务器定义了echoInteger),因为 将MustUnderstand设置为0的有效标头生成到此客户端 然后用这个头发送SOAP
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)
这应该会失败(即使服务器定义了“echoInteger”),因为
在此客户端中生成有效头,但将MustUnderstand设置为1
对于服务器在发送之前可能无法理解的消息 我也在想办法。我已经完成了这项工作的核心,所以这应该会加快其他任何需要这项工作的人的速度!我假设其他Sage子系统也以同样的方式工作(但我还不知道),所以我试图解释这种可能性 首先,您需要我编写的名为pySage.py的模块。它定义了一个用于运行进程的类,您需要为自定义用途对其进行子类化
#----------------------------
#
# 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 ""
然后,为这个类添加一个客户端。下面是我创建的一个名为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()
感谢您的回复。SOAPpy.Header附带哪个版本的SOAPpy?我的Ubuntu(Hardy)机器发行了0.12.0-2版本,似乎没有SOAPpy.Header。在谷歌搜索期间,我确实找到了一些关于SOAPpy.Header的文档,但在包中找不到它时,我不知所措。我必须从SVN获取Soapy吗?嗨,Devraj,看起来0.12.0是最新的打包版本,所以这意味着您需要在
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)
#----------------------------
#
# 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 ""
#----------------------------
#
# 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()