Package: salesforce-api
Python Salesforce APIThis project aims to provide an easy to use, highly flexible and testable solution for communicating with Salesforce
through its REST and SOAP api.ContentSimple UsageAuthenticationRecord managementInsertUpsertUpdateGetDeleteQuering SObjectsBulkToolingDeployingRetrievingAdditional featuresSimple usageCreating a new connection / client is as simple as this:fromsalesforce_apiimportSalesforceclient=Salesforce(username='test@example.com',password='my-password',security_token='password-token')AuthenticationTo get started in the simples of ways, you would do the followingfromsalesforce_apiimportSalesforceclient=Salesforce(username='test@example.com',password='my-password',security_token='password-token')If you are trying to connect to a sandbox, you have to specify this using theis_sandboxargument.fromsalesforce_apiimportSalesforceclient=Salesforce(username='test@example.com',password='my-password',security_token='password-token',is_sandbox=True)If for some reason the login-url differs from the standard prod/test login urls, you can specify the login url. This can be useful if you are using a mock-server, for example. This will override theis_sandboxargument.fromsalesforce_apiimportSalesforceclient=Salesforce(username='test@example.com',password='my-password',security_token='password-token',domain='login.example.com')The examples so far would use the SOAP API for authenticating. If you want to authenticate using an app, that's easy engough. The login-url and sandbox-arguments applies here as well.fromsalesforce_apiimportSalesforceclient=Salesforce(username='test@example.com',password='my-password',client_id='123',client_secret='my-secret')If you already have an OAuth access token, obtained elsewhere, you can just as easily create a new client.fromsalesforce_apiimportSalesforceclient=Salesforce(access_token='access-token-here',domain='access-token-domain')If you want to explicitly use one or the other methods of authenticating, you can do that as wellfromsalesforce_apiimportSalesforce,loginclient=Salesforce(login.oauth2(username='test@example.com',password='my-password',client_id='123',client_secret='my-secret'))If you want to use a specific version of the Salesforce API, you can specify this:fromsalesforce_apiimportSalesforceclient=Salesforce(access_token='access-token-here',domain='access-token-domain',api_version='51.0')Record managementWokring with records is easy. All SObject-related methods are exposed through thesobjects-property on the client.The data returned from the different calls is the decoded data from the raw response.InsertExampleclient.sobjects.Contact.insert({'LastName':'Example','Email':'test@example.com'})Returns{"id":"0031l000007rU3vAAE","success":true,"errors":[]}GetExampleclient.sobjects.Contact.get('0031l000007rU3vAAE')Returns{
    "attributes": {
        "type": "Contact",
        "url": "/services/data/v44.0/sobjects/Contact/0031l000007rU3vAAE"
    },
    "Id": "0031l000007rU3vAAE",
    "LastName": "Example",
    "FirstName": "Test",
    ...
}UpdateExampleclient.sobjects.Contact.update('0031l000007rU3vAAE',{'FirstName':'Felix','LastName':'Lindstrom'})ReturnsTrueUpsertExampleclient.sobjects.Contact.upsert('customExtIdField__c','11999',{'FirstName':'Felix','LastName':'Lindstrom'})ReturnsTrueDeleteExampleclient.sobjects.Contact.delete('0031l000007rU3vAAE')ReturnsTrueMetadataExampleclient.sobjects.Contact.metadata()Returns{
    'objectDescribe': {
        'activateable': False,
        'createable': True,
        'custom': False,
        ...
        'urls': {
            'compactLayouts': '/services/data/v44.0/sobjects/Contact/describe/compactLayouts',
            'rowTemplate': '/services/data/v44.0/sobjects/Contact/{ID}',
            'approvalLayouts': '/services/data/v44.0/sobjects/Contact/describe/approvalLayouts',
            ...
        }
    },
    'recentItems': []
}DescribeExampleclient.sobjects.Contact.describe()Returns{
    ...
}Querying SObjectsThe Salesforce API is great at returning large amounts of data, so the pagination that Salesforce implements for the result of your queries is taken cared of automagically when querying for data.Exampleclient.sobjects.query("SELECT Id, FirstName, LastName FROM Contact WHERE FirstName='Felix'")Return[{
    'attributes': {
        'type': 'Contact',
        'url': '/services/data/v44.0/sobjects/Contact/0031l000007Jia4AAC'
    },
    'Id': '0031l000007Jia4AAC',
    'FirstName': 'Felix',
    'LastName': 'Lindstrom'
}, ...]BulkThis module implements the Bulk V2 API. Basically, it allows you to think less and do more.Note that the correct permission-set might be needed on the user, seehttps://success.salesforce.com/issues_view?id=a1p3A000000BMPFQA4Bulk InsertExampleclient.bulk.insert('Contact',[{'LastName':'Lindstrom','Email':'test@example.com'},{'LastName':'Something else','Email':'test@example.com'}])Returns[<SuccessResultRecordrecord_id="0031l000007rU5rAAE"success="True"/>,<SuccessResultRecordrecord_id="0031l000007rU5sAAE"success="True"/>]Bulk InsertExampleclient.bulk.insert('Contact',[{'LastName':'Lindstrom','Email':'test@example.com'},{'LastName':'Something else','Email':'test@example.com'}])Returns[<SuccessResultRecordrecord_id="0031l000007rU5rAAE"success="True"/>,<SuccessResultRecordrecord_id="0031l000007rU5sAAE"success="True"/>]Bulk UpsertExampleclient.bulk.upsert('Contact',[{'LastName':'Lindstrom','Email':'test@example.com','MyId__c':1},{'LastName':'Something else','Email':'test@example.com','MyId__c':2}],external_id_field='MyId__c')Returns[<SuccessResultRecordrecord_id="0031l000007rU5rAAE"success="True"/>,<SuccessResultRecordrecord_id="0031l000007rU5sAAE"success="True"/>]Bulk UpdateExampleclient.bulk.update('Contact',[{'LastName':'Lindstrom','Email':'test@example.com'},{'LastName':'Something else','Email':'test@example.com'}])Returns[<SuccessResultRecordrecord_id="0031l000007rU5rAAE"success="True"/>,<SuccessResultRecordrecord_id="0031l000007rU5sAAE"success="True"/>]Bulk DeleteExampleclient.bulk.delete('Contact',['0031l000007rU5rAAE','0031l000007rU5sAAE'])Returns[<SuccessResultRecordrecord_id="0031l000007rU5rAAE"success="True"/>,<SuccessResultRecordrecord_id="0031l000007rU5sAAE"success="True"/>]Failed requestsExample (Given that the records no longer exists)client.bulk.update('Contact',['0031l000007rU5rAAE','0031l000007rU5sAAE'])Returns[<FailResultRecordrecord_id="0031l000007rU5rAAE"success="False"error="ENTITY_IS_DELETED:entity is deleted:--"/>,<FailResultRecordrecord_id="0031l000007rU5sAAE"success="False"error="ENTITY_IS_DELETED:entity is deleted:--"/>]Manual managing bulk jobBy using the api above, the library hides the uploading and waiting for the bulk-process to get processed.In some cases you might want to handle this differently. Perhaps you want to upload bunch of records to be inserted and then forget about the process. This can be done by creating a job and managing it by yourself.bulk_job=client.bulk.create_job(OPERATION.INSERT,'Contact')bulk_job.upload([{'LastName':'Lindstrom','Email':'test@example.com'},{'LastName':'Something else','Email':'test@example.com'}])whilenotbulk_job.is_done():time.sleep(5)ToolingExecute ApexExampleclient.tooling.execute_apex("System.debug('Test');")Return on success<SuccessfulApexExecutionResultline="-1"column="-1"compiled="True"success="True"compile_problem="None"exception_stack_trace="None"exception_message="None"/>Return on failure<FailedApexExecutionResult line="1" column="13" compiled="False" success="False" compile_problem="Unexpected token '('." exception_stack_trace="None" exception_message="None" />DeployingDeploying an existing packagefromsalesforce_api.models.deployimportOptionsdeployment=client.deploy.deploy('/path/to/file.zip')deployment.wait()result=deployment.get_status()Only validatingfromsalesforce_api.models.deployimportOptionsdeployment=client.deploy.deploy('/path/to/file.zip',Options(checkOnly=True))deployment.wait()result=deployment.get_status()Validating running specific testsfromsalesforce_api.models.deployimportOptionsdeployment=client.deploy.deploy('/path/to/file.zip',Options(checkOnly=True,testLevel='RunSpecifiedTests',runTests=['TesterIntegrationApplicationTest','TesterIntegrationProcessTest']))deployment.wait()result=deployment.get_status()Canceling a deployment as soon as it failsfromsalesforce_api.models.deployimportOptionsdeployment=client.deploy.deploy('/path/to/file.zip',Options(checkOnly=True))whilenotdeployment.is_done():ifdeployment.has_failed():deployment.cancel()breakRetrievingExamplefromsalesforce_api.models.sharedimportType# Decide what you want to retrievetypes=[Type('ApexTrigger'),Type('ApexClass',['MyMainClass','AnotherClass'])]# Create retrievement-jobretrievement=client.retrieve.retrieve(types)# Wait for the job to finishretrievement.wait()# Write the resulting zip-archive to a fileopen('retrieve.zip','wb').write(retrievement.get_zip_file().read())Additional featuresIf for some reason you need to specify how the client communicates with Salesforce, you can create the requests-session yourself and pass it to the client upon creation. This would, for example, allow you proxy your requests:importrequestsfromsalesforce_apiimportSalesforcesession=requests.Session()session.proxies.update({'https':'https://my-proxy.com/'})session.headers.update({'User-Agent':'My-User-Agent'})client=Salesforce(username='test@example.com',password='my-password',security_token='password-token',session=session)