سؤال

أحاول كتابة مستهلك ثعبان ل SAGE CRM باستخدام واجهة خدمات الويب الخاصة بهم. أنا أستخدم WAPPY حيث كانت مكتبة الصابون بيثون (غير متزوجة من ذلك، كانت سهلة التركيب على Ubuntu، لذلك ذهب معها).

تمكنت من جلب WSDL باستخدام الوكيل وتنفيذ طريقة تسجيل الدخول المكشوفة بواسطة Sage CRM.


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

إرجاع كائن جلسة نوع من يشبه

النتيجة SOAPPY.TYPES.SERTTYPE عند 151924492: {'sessionid': '170911104429792'}

الآن أحاول استخدام طريقة Queryrecord Sage CRM للاستعلام عن البيانات، وهذا يعود

خطأ SOAP-ENV: الخادم: لا يوجد حساب مستخدم نشط

تقرأ الوثائق تكشف أنه يجب علي إرساله إلى معرف الجلسة الذي Recd. عندما قمت بتسجيل الدخول مرة أخرى مع كل طلبات.

وفقا لوثائق حكيم، يجب أن أرسلها إلى ذلك


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

أي أفكار كيفية القيام بإلحاق هذا إلى الرؤوس باستخدام الأخطاء؟

سيتم تقدير أي مؤشرات تقدير كبير.

هل كانت مفيدة؟

المحلول

أولا، فقط للتعليق على مكتبة الصابون الأخرى مقابل الأخطاء الأخرى ... كانت Sapy Throundally مكتبة سهلة الاستخدام التي لم تدعم هياكل البيانات المعقدة. لذلك إذا كان يعمل من أجل قضيتك، فأنت أفضل حالا. بالنسبة لهياكل بيانات أكثر تعقيدا في WSDL، كنت بحاجة إلى نقل ZSI.

على أي حال، يبدو أن رابط الوثائق للمشروع على Sourceforge مكسورة، لذلك أنا فقط قطع + لصق وثائق الرأس هنا مع بعض التغييرات التنسيقية الطفيفة:

باستخدام رؤوس

يحتوي Sapy على فئة رأس لعقد بيانات لرأس رسالة الصابون. يحتوي كل مثيل رأس طرقا لتعيين / احصل على السمة المسائية، والأساليب لتعيين / الحصول على سمة الممثل.

يحتوي Sapy أيضا على فئة SOAPCONTEXT بحيث يمكن تنفيذ كل طريقة خادم بهذه الطريقة التي يحصل عليها سياق عميل الاتصال. يتضمن ذلك معلومات عن معلومات الصابون المشتركة والاتصال (انظر أدناه للحصول على مثال).

أمثلة العميل

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)

يجب أن ينجح هذا (شريطة أن يحدد الخادم Echointeger)، لأنه يقوم بإنشاء رأس صالح في هذا العميل مع العتاد تعيين إلى 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)

يجب أن يفشل ذلك (حتى إذا كان الخادم قد حدد "Echointeger")، حيث يقوم بإنشاء رأس صالح في هذا العميل، ولكن قم بتعيين المسيطرات الطويلة إلى 1 للحصول على رسالة مفادها أنه لن يفترض الخادم قبل الإرسال.

نصائح أخرى

أنا فقط معرفة هذا أيضا. لدي جوهر هذا من ذلك، لذلك يجب أن يسرع هذه الأمور لأي شخص آخر يحتاج إلى هذا! أفترض أن النظم الفرعية الحكومية الأخرى تعمل بنفس الطريقة (لكنني لا أعرف بعد)، لذلك حاولت أن أحسب هذا الاحتمال.

أولا، تحتاج إلى هذه الوحدة التي كتبتها تسمى 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()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top