A very simple Salesforce.com REST API client for Python

Overview

Simple Salesforce

https://api.travis-ci.org/simple-salesforce/simple-salesforce.svg?branch=master Documentation Status

Simple Salesforce is a basic Salesforce.com REST API client built for Python 3.5, 3.6, 3.7 and 3.8. The goal is to provide a very low-level interface to the REST Resource and APEX API, returning a dictionary of the API JSON response.

You can find out more regarding the format of the results in the Official Salesforce.com REST API Documentation

Examples

There are two ways to gain access to Salesforce

The first is to simply pass the domain of your Salesforce instance and an access token straight to Salesforce()

For example:

from simple_salesforce import Salesforce
sf = Salesforce(instance='na1.salesforce.com', session_id='')

If you have the full URL of your instance (perhaps including the schema, as is included in the OAuth2 request process), you can pass that in instead using instance_url:

from simple_salesforce import Salesforce
sf = Salesforce(instance_url='https://na1.salesforce.com', session_id='')

There are also three means of authentication, one that uses username, password and security token; one that uses IP filtering, username, password and organizationId; and the other that uses a private key to sign a JWT.

To login using the security token method, simply include the Salesforce method and pass in your Salesforce username, password and token (this is usually provided when you change your password):

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token')

To login using IP-whitelist Organization ID method, simply use your Salesforce username, password and organizationId:

from simple_salesforce import Salesforce
sf = Salesforce(password='password', username='[email protected]', organizationId='OrgId')

To login using the JWT method, use your Salesforce username, consumer key from your app, and private key:

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', consumer_key='XYZ', privatekey_file='filename.key')

If you'd like to enter a sandbox, simply add domain='test' to your Salesforce() call.

For example:

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token', domain='test')

Note that specifying if you want to use a domain is only necessary if you are using the built-in username/password/security token authentication and is used exclusively during the authentication step.

If you'd like to keep track where your API calls are coming from, simply add client_id='My App' to your Salesforce() call.

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token', client_id='My App', domain='test')

If you view the API calls in your Salesforce instance by Client Id it will be prefixed with RestForce/, for example RestForce/My App.

When instantiating a Salesforce object, it's also possible to include an instance of requests.Session. This is to allow for specialized session handling not otherwise exposed by simple_salesforce.

For example:

from simple_salesforce import Salesforce
import requests

session = requests.Session()
# manipulate the session instance (optional)
sf = Salesforce(
   username='[email protected]', password='password', organizationId='OrgId',
   session=session)

Record Management

To create a new 'Contact' in Salesforce:

sf.Contact.create({'LastName':'Smith','Email':'[email protected]'})

This will return a dictionary such as {u'errors': [], u'id': u'003e0000003GuNXAA0', u'success': True}

To get a dictionary with all the information regarding that record, use:

contact = sf.Contact.get('003e0000003GuNXAA0')

To get a dictionary with all the information regarding that record, using a custom field that was defined as External ID:

contact = sf.Contact.get_by_custom_id('My_Custom_ID__c', '22')

To change that contact's last name from 'Smith' to 'Jones' and add a first name of 'John' use:

sf.Contact.update('003e0000003GuNXAA0',{'LastName': 'Jones', 'FirstName': 'John'})

To delete the contact:

sf.Contact.delete('003e0000003GuNXAA0')

To retrieve a list of Contact records deleted over the past 10 days (datetimes are required to be in UTC):

import pytz
import datetime
end = datetime.datetime.now(pytz.UTC)  # we need to use UTC as salesforce API requires this!
sf.Contact.deleted(end - datetime.timedelta(days=10), end)

To retrieve a list of Contact records updated over the past 10 days (datetimes are required to be in UTC):

import pytz
import datetime
end = datetime.datetime.now(pytz.UTC) # we need to use UTC as salesforce API requires this
sf.Contact.updated(end - datetime.timedelta(days=10), end)

Note that Update, Delete and Upsert actions return the associated Salesforce HTTP Status Code

Use the same format to create any record, including 'Account', 'Opportunity', and 'Lead'. Make sure to have all the required fields for any entry. The Salesforce API has all objects found under 'Reference -> Standard Objects' and the required fields can be found there.

Queries

It's also possible to write select queries in Salesforce Object Query Language (SOQL) and search queries in Salesforce Object Search Language (SOSL).

SOQL queries are done via:

sf.query("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")

If, due to an especially large result, Salesforce adds a nextRecordsUrl to your query result, such as "nextRecordsUrl" : "/services/data/v26.0/query/01gD0000002HU6KIAW-2000", you can pull the additional results with either the ID or the full URL (if using the full URL, you must pass 'True' as your second argument)

sf.query_more("01gD0000002HU6KIAW-2000")
sf.query_more("/services/data/v26.0/query/01gD0000002HU6KIAW-2000", True)

As a convenience, to retrieve all of the results in a single local method call use

sf.query_all("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")

While query_all materializes the whole result into a Python list, query_all_iter returns an iterator, which allows you to lazily process each element separately

data = sf.query_all_iter("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")
for row in data:
  process(row)

Values used in SOQL queries can be quoted and escaped using format_soql:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName = {}", "Jones"))
sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName = {last_name}", last_name="Jones"))
sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName IN {names}", names=["Smith", "Jones"]))

To skip quoting and escaping for one value while still using the format string, use :literal:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE Income > {:literal}", "USD100"))

To escape a substring used in a LIKE expression while being able to use % around it, use :like:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE Name LIKE '{:like}%'", "Jones"))

SOSL queries are done via:

sf.search("FIND {Jones}")

There is also 'Quick Search', which inserts your query inside the {} in the SOSL syntax. Be careful, there is no escaping!

sf.quick_search("Jones")

Search and Quick Search return None if there are no records, otherwise they return a dictionary of search results.

More details about syntax is available on the Salesforce Query Language Documentation Developer Website

File Based Metadata API Calls

You can use simple_salesforce to make file-based calls to the Metadata API, to deploy a zip file to an org.

First, convert and zip the file with:

sfdx force:source:convert -r src/folder_name -d dx

Then navigate into the converted folder and zip it up:

zip -r -X package.zip *

Then you can use this to deploy that zipfile:

result = sf.deploy("path/to/zip", sandbox=False, **kwargs)
asyncId = result.get('asyncId')
state = result.get('state')

Both deploy and checkDeployStatus take keyword arguements. The single package arguement is not currently available to be set for deployments. More details on the deploy options can be found at https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy.htm

You can check on the progress of the deploy which returns a dictionary with status, state_detail, deployment_detail, unit_test_detail:

sf.checkDeployStatus(asyncId)

Example of a use-case:

from simple_salesforce import Salesforce

deployment_finished = False
successful = False

sf = Salesforce(session_id="id", instance="instance")
sf.deploy("path/to/zip", sandbox=False ,**kwargs)

while not deployment_finished:
    result = sf.checkDeployStatus(asyncId)
    if result.get('status') in ["Succeeded", "Completed", "Error", "Failed", None]:
        deployment_finished = True
    if result.get('status') in ["Succeeded", "Completed"]:
        successful = True

if successful:
    print("✅")
else:
    print("🥔")

Other Options

To insert or update (upsert) a record using an external ID, use:

sf.Contact.upsert('customExtIdField__c/11999',{'LastName': 'Smith','Email': '[email protected]'})

To format an external ID that could contain non-URL-safe characters, use:

external_id = format_external_id('customExtIdField__c', 'this/that & the other')

To retrieve basic metadata use:

sf.Contact.metadata()

To retrieve a description of the object, use:

sf.Contact.describe()

To retrieve a description of the record layout of an object by its record layout unique id, use:

sf.Contact.describe_layout('39wmxcw9r23r492')

To retrieve a list of top level description of instance metadata, user:

sf.describe()

for x in sf.describe()["sobjects"]:
  print x["label"]

Using Bulk

You can use this library to access Bulk API functions. The data element can be a list of records of any size and by default batch sizes are 10,000 records and run in parrallel concurrency mode. To set the batch size for insert, upsert, delete, hard_delete, and update use the batch_size argument. To set the concurrency mode for the salesforce job the use_serial argument can be set to use_serial=True.

Create new records:

data = [
      {'LastName':'Smith','Email':'[email protected]'},
      {'LastName':'Jones','Email':'[email protected]'}
    ]

sf.bulk.Contact.insert(data,batch_size=10000,use_serial=True)

Update existing records:

data = [
      {'Id': '0000000000AAAAA', 'Email': '[email protected]'},
      {'Id': '0000000000BBBBB', 'Email': '[email protected]'}
    ]

sf.bulk.Contact.update(data,batch_size=10000,use_serial=True)

Upsert records:

data = [
      {'Id': '0000000000AAAAA', 'Email': '[email protected]'},
      {'Email': '[email protected]'}
    ]

sf.bulk.Contact.upsert(data, 'Id', batch_size=10000, use_serial=True)

Query records:

query = 'SELECT Id, Name FROM Account LIMIT 10'

sf.bulk.Account.query(query)

To retrieve large amounts of data, use

query = 'SELECT Id, Name FROM Account'

# generator on the results page
fetch_results = sf.bulk.Account.query(query, lazy_operation=True)

# the generator provides the list of results for every call to next()
all_results = []
for list_results in fetch_results:
  all_results.extend(list_results)

Query all records:

QueryAll will return records that have been deleted because of a merge or delete. QueryAll will also return information about archived Task and Event records.

query = 'SELECT Id, Name FROM Account LIMIT 10'

sf.bulk.Account.query_all(query)

To retrieve large amounts of data, use

query = 'SELECT Id, Name FROM Account'

# generator on the results page
fetch_results = sf.bulk.Account.query_all(query, lazy_operation=True)

# the generator provides the list of results for every call to next()
all_results = []
for list_results in fetch_results:
  all_results.extend(list_results)

Delete records (soft deletion):

data = [{'Id': '0000000000AAAAA'}]

sf.bulk.Contact.delete(data,batch_size=10000,use_serial=True)

Hard deletion:

data = [{'Id': '0000000000BBBBB'}]

sf.bulk.Contact.hard_delete(data,batch_size=10000,use_serial=True)

Using Apex

You can also use this library to call custom Apex methods:

payload = {
  "activity": [
    {"user": "12345", "action": "update page", "time": "2014-04-21T13:00:15Z"}
  ]
}
result = sf.apexecute('User/Activity', method='POST', data=payload)

This would call the endpoint https://<instance>.salesforce.com/services/apexrest/User/Activity with data= as the body content encoded with json.dumps

You can read more about Apex on the Force.com Apex Code Developer's Guide

Additional Features

There are a few helper classes that are used internally and available to you.

Included in them are SalesforceLogin, which takes in a username, password, security token, optional version and optional domain and returns a tuple of (session_id, sf_instance) where session_id is the session ID to use for authentication to Salesforce and sf_instance is the domain of the instance of Salesforce to use for the session.

For example, to use SalesforceLogin for a sandbox account you'd use:

from simple_salesforce import SalesforceLogin
session_id, instance = SalesforceLogin(
    username='[email protected]',
    password='password',
    security_token='token',
    domain='test')

Simply leave off the final domain if you do not wish to use a sandbox.

Also exposed is the SFType class, which is used internally by the __getattr__() method in the Salesforce() class and represents a specific SObject type. SFType requires object_name (i.e. Contact), session_id (an authentication ID), sf_instance (hostname of your Salesforce instance), and an optional sf_version

To add a Contact using the default version of the API you'd use:

from simple_salesforce import SFType
contact = SFType('Contact','sesssionid','na1.salesforce.com')
contact.create({'LastName':'Smith','Email':'[email protected]'})

To use a proxy server between your client and the SalesForce endpoint, use the proxies argument when creating SalesForce object. The proxy argument is the same as what requests uses, a map of scheme to proxy URL:

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}
SalesForce(instance='na1.salesforce.com', session_id='', proxies=proxies)

All results are returned as JSON converted OrderedDict to preserve order of keys from REST responses.

Authors & License

This package is released under an open source Apache 2.0 license. Simple-Salesforce was originally written by Nick Catalano but most newer features and bugfixes come from community contributors. Pull requests submitted to the GitHub Repo are highly encouraged!

Authentication mechanisms were adapted from Dave Wingate's RestForce and licensed under a MIT license

The latest build status can be found at Travis CI

Comments
  • Not a issue more of a question: Have you used simple salesforce in an AWS Lambda

    Not a issue more of a question: Have you used simple salesforce in an AWS Lambda

    Hi was wondering if you can use simple saleforce in a aws lambda script. Was trying to use the api gateway to pass a customer id to a lambda script and have that lambda script use simple salesforce to grab the customer info?

    Thanks in advance for any help

    opened by OtisHarrison24 15
  • How would I download download a .tar or .tgz file attachment instead of an image?

    How would I download download a .tar or .tgz file attachment instead of an image?

    Based on another question I figured out how to download an image file from SF.com, but I get an error after doing the same with a tar or tgz file. When I try to unpack the files, I get an error saying that the file doesn't appear to be a tar archive. Has anyone had success with this? Thanks again for all the help and support.

    opened by ghost 15
  • v1.11.1 bulk results

    v1.11.1 bulk results

    Prior to v1.11.1 (and v1.11.0 I think, though I haven't tested because that version is absent on PyPI), the following code would have produced a one dimensional list of dicts, like so:

    >>> results = sf.bulk.Contact.insert([{'LastName': 'Test'}])
    >>> print(results)
    [{'success': True, 'created': True, 'id': '0031g00000nakPdAAI', 'errors': []}]
    

    With v1.11.1, a two-dimensional list is returned; I believe the top level represents batches:

    >>> results = sf.bulk.Contact.insert([{'LastName': 'Test'}])
    >>> print(results)
    [[{'success': True, 'created': True, 'id': '0031g00000nakAEAAY', 'errors': []}]]
    

    Was this an intentional change, and if so, where is it documented?

    opened by mhogg-srg 14
  • Possibility to respect a maximum request rate?

    Possibility to respect a maximum request rate?

    Our organisation has a very low request limit spread across a few scripts and programs that interface with Salesforce.

    Is there a way to ensure that a script using this will remain under X requests per time interval?

    This would allow me to spend less time handling this issue via manually waiting before each Salesforce request

    opened by savingschampion 14
  • simple-salesforce report import error

    simple-salesforce report import error

    Hi,

    I have been using the code below to import my reports and manipulate them as a pandas dataframe.

    `#Salesforce Connection sf = Salesforce(username = '------',password= '-------' , security_token= '--------')

    sf_instance = '------' #Your Salesforce Instance URL reportId = '------' # add report id export = '?isdtp=p1&export=1&enc=UTF-8&xf=csv' sfUrl = sf_instance + reportId + export session = requests.Session() session.max_redirects = 1000 response = session.get(sfUrl, headers=sf.headers, cookies={'sid': sf.session_id}) download_report = response.content.decode('utf-8') sf_data = pd.read_csv(StringIO(download_report)) sf_data.head(n=1000)`

    For the last 2 days, I have been encountering the following error: 'TooManyRedirects: Exceeded 30 redirects.'

    I have also tried to increase max_redirects cap to 1000, still the same outcome unfortunately.

    I haven't edited the code, my credentials are unchanged. What might be the cause of the problem, is anyone else experiencing this?

    opened by ycicek-firefly 13
  • Deprecated sandbox flag in favor of domain flag

    Deprecated sandbox flag in favor of domain flag

    This is useful for cases where the login domain is a custom domain, meaning that it is neither 'login' nor 'test'. Default is 'login', however, so the default behavior is unchanged. Updated current api appropriately (along with non-fatal deprecation warning), while allowing SalesforceApi class to stay the same.

    opened by shawalli 13
  • Breaking change in 1.12.0

    Breaking change in 1.12.0

    We are experiencing a breaking change in the latest release.

    A stack trace of the error we are observing, from one of our QA logs, is shown below.

    Traceback (most recent call last):
    File "/var/runtime/urllib3/connection.py", line 174, in _new_conn
    conn = connection.create_connection(
    File "/var/runtime/urllib3/util/connection.py", line 72, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
    File "/var/lang/lib/python3.9/socket.py", line 954, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
    socket.gaierror: [Errno -2] Name or service not known
    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
    File "/var/runtime/urllib3/connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
    File "/var/runtime/urllib3/connectionpool.py", line 386, in _make_request
    self._validate_conn(conn)
    File "/var/runtime/urllib3/connectionpool.py", line 1040, in _validate_conn
    conn.connect()
    File "/var/runtime/urllib3/connection.py", line 358, in connect
    self.sock = conn = self._new_conn()
    File "/var/runtime/urllib3/connection.py", line 186, in _new_conn
    raise NewConnectionError(
    urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7f3e18c2f7f0>: Failed to establish a new connection: [Errno -2] Name or service not known
    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
    File "/var/task/./vendored/requests/adapters.py", line 439, in send
    resp = conn.urlopen(
    File "/var/runtime/urllib3/connectionpool.py", line 785, in urlopen
    retries = retries.increment(
    File "/var/runtime/urllib3/util/retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
    urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='00d8a000000mq7f!ar4aqnejwqivxwyazqgc2n3zxhl9z2.bei5uyy8ib1imnehtutvjixgzky9drsjovr0ecf.bvh6m4sesjmdci.nnn55_0zey', port=443): Max retries exceeded with url: /services/data/v52.0/sobjects/Kidney_Donor__c/xnid__c/transplant.kidney:XNID.2A8AFDX7SIF04IHV0TRLKBR5P (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f3e18c2f7f0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
    During handling of the above exception, another exception occurred:
    Traceback (most recent call last):
    File "/var/task/index.py", line 785, in upsertSalesforceSurveyRecord
    response = bsurvey.upsert('xnid__c/%s' % (xnid),objrecord)
    File "/var/task/./vendored/simple_salesforce/api.py", line 841, in upsert
    result = self._call_salesforce(
    File "/var/task/./vendored/simple_salesforce/api.py", line 935, in _call_salesforce
    result = self.session.request(method, url, headers=headers, **kwargs)
    File "/var/task/./vendored/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
    File "/var/task/./vendored/requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
    File "/var/task/./vendored/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
    requests.exceptions.ConnectionError: HTTPSConnectionPool(host='00d8a000000mq7f!ar4aqnejwqivxwyazqgc2n3zxhl9z2.bei5uyy8ib1imnehtutvjixgzky9drsjovr0ecf.bvh6m4sesjmdci.nnn55_0zey', port=443): Max retries exceeded with url: /services/data/v52.0/sobjects/Kidney_Donor__c/xnid__c/transplant.kidney:XNID.2A8AFDX7SIF04IHV0TRLKBR5P (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f3e18c2f7f0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
    

    We are using Python3.9 in AWS.

    Hoping this is helpful.

    opened by hermanmedsleuth 12
  • Issue #375: Feature Request: File Based Metadata API Calls

    Issue #375: Feature Request: File Based Metadata API Calls

    Documentation for use provided in README.RST

    Following up on @Kevmo92's feature request for File Based Metadata API Calls (https://github.com/simple-salesforce/simple-salesforce/issues/375)

    • Ported source code from https://github.com/Kevmo92/sfdclib

      • messages.py: XML message templates for Salesforce API
      • metadata.py: functionality to deploy zipfile and check deployment status
      • session.py -(renamed)> sfdc_session.py: existing session class to deal with mdapi interaction
    • Added functionality to Salesforce class

      • examples for using deploy() and checkDeployStatus() available in README.RST
    • Added unit tests to test_api.py

      • Covers various cases for deploy() and checkDeployStatus()
    • Additional testing was done to ensure compatibility with @nCino's internal tools.

    opened by cmitzel 12
  • Feature/bulk api

    Feature/bulk api

    Adding support for Bulk API according to discussions in #112 .

    • Split out bulk classes into bulk.py as a proposed model for splitting other services; this way, the handling classes for each API type would live in it's own file, making the code more manageable.
    • Added example docs to README
    • I've tested in the sandbox available to me, with a few object types.

    Potential edits/discussion:

    • To get the format mentioned in #112, sf.bulk.Contact.do_something(), had to create two new classes, SFBulkHandler and SFBulkType. I'm not entirely happy with SFBulkHandler as a solution, since it's only purpose is to parse out the SF object type and pass to SFBulkType. If anyone has ideas on how to better handle this, let me know and I'll work it in. Another alternative is to use the format sf.bulk.do_something(object='Contact'), but that is less intuitive.
    • _call_salesforce() now gets defined three times in this codebase (twice in api.py and once in bulk.py). As a todo, might be nice to generalize and move to the util.py file, along with _exception_handler(); especially would make split services easier to implement.
    • What types of unit tests are appropriate here? Looking at the current tests, I'm not sure there's much we could add that wouldn't be dependent on API version. If any suggestions, let me know and I can either build into this PR or create a separate PR later.
    opened by colemanja91 12
  • Split services

    Split services

    There's currently a mix of services implemented directly in simple_salesforce.Salesforce (i.e. apex and sobjects). This should be changed such that simple_salesforce.Salesforce contains references to service implementations, such as Salesforce.bulk, Salesforce.sobjects, Salesforce.reports, Salesforce.apex, etc.

    As a part of this, the existing APIs should be deprecated (use warnings.warn(..., DeprecationWarning)) and removed in a future version.

    enhancement 
    opened by demianbrecht 12
  • INVALID_LOGIN

    INVALID_LOGIN

    Hello,

    Im trying to login use simple-salesforce and getting below error: simple_salesforce.login.SalesforceAuthenticationFailed: INVALID_LOGIN: Invalid username, password, security token; or user locked out. I am using python 2.7.11. I am using security_token and its valid. Should I be passing a domain ?

    Error: File "/apps/salesforce/simple_salesforce/api.py", line 147, in init domain=self.domain) File "/apps/salesforce/simple_salesforce/login.py", line 175, in SalesforceLogin raise SalesforceAuthenticationFailed(except_code, except_msg) simple_salesforce.exceptions.SalesforceAuthenticationFailed: INVALID_LOGIN: Invalid username, password, security token; or user locked out.

    opened by alal2255 11
  • Unable to add new Custom Field to existing object (custom/standard)?

    Unable to add new Custom Field to existing object (custom/standard)?

    When trying to UPDATE CustomObject with newly added custom field, I get this error message: TestCustomObject__c.AccountNumber_NEW__c: (INVALID_CROSS_REFERENCE_KEY, In field: fullName - no CustomField named TestCustomObject__c.AccountNumber_NEW__c found)

    My code looks like this:

    custom_object = mdapi.CustomObject.read(custom_object_fullname)
    custom_field = mdapi.CustomField(
                            fullName="AccountNumber_NEW__c", description="Not used"
                        )
                        new_fields = [custom_field]
    custom_object.fields += new_fields
    mdapi.CustomObject.update(custom_object)
    

    Any idea, what I'm doing wrong here? Thanks.

    opened by janb-frontify 0
  • Escaped password is causing authentication error

    Escaped password is causing authentication error

    lib ver: 1.12.2 api ver: 52.0

    Suppose you have password&123, it becomes password&amp;123 in the payload that gets POSTed to the server.

    I'm on version 52.0 and it's rejecting escaped passwords. When I submit the token_login call without the escape, it works fine.

    The escapes are here at lines 83 in simple_salesforce/login.py:

    # pylint: disable=E0012,deprecated-method
    username = escape(username) if username else None
    password = escape(password) if password else None
    
    opened by knguyen1 1
  • Use pathlib.Path

    Use pathlib.Path

    also:

    • default encoding in bytes.decode is already 'utf-8'. See https://docs.python.org/3.6/library/stdtypes.html#bytes.decode

    Available at least since Python 3.6, which is the minimal supported version. See https://docs.python.org/3.6/library/pathlib.html#pathlib.Path.read_bytes

    opened by ericbn 1
  • "X-PrettyPrint: 1" header produces JSON that would be equally parsed if it was not pretty printed

    Hi! 👋

    I see the code is adding "X-PrettyPrint: 1" to the request headers. That seems to just add extra characters to the response (spaces, newlines).

    Example: '{"totalSize":0,"done":true,"records":[]}' (40 characters) vs. "{\n \"totalSize\" : 0,\n \"done\" : true,\n \"records\" : [ ]\n}" (54 characters, 35% bigger).

    The advantage of removing "X-PrettyPrint: 1" is that responses are smaller, so there's less to transmit over HTTP. What do you think about removing it?

    Wanted to open this for discussion before a PR is submitted.

    opened by ericbn 0
  • Auto-sized batching misses out a single record when more than one record

    Auto-sized batching misses out a single record when more than one record

    With this test:

        @mock.patch('simple_salesforce.bulk.SFBulkType._add_batch')
        def test_add_autosized_batches(self, add_batch):
            # a sneaky way to get the data back so we can test it
            add_batch.side_effect = lambda job_id, data, operation: data
            sf_bulk_type = SFBulkType(None, None, None, None)
            data = [
                # Expected serialized record size of 13 to 1513. Idea is that earlier record batches are split on record
                # count, whereas later batches are split for hitting the byte limit.
                {'key': 'value' * random.randint(0, i // 50)}
                for i in range(30000)
            ]
            result = sf_bulk_type._add_autosized_batches(data=data, operation="update", job="Job-1")
            reconstructed_data = list(itertools.chain(*result))
            print([len(batch) for batch in result])
            # all data was put in a batch
            self.assertEqual(len(data), len(reconstructed_data))
    

    we have the following result:

    [9999, 10000, 8208, 1792]
    
    Ran 1 test in 1.526s
    
    FAILED (failures=1)
    
    29999 != 30000
    
    Expected :30000
    Actual   :29999
    <Click to see difference>
    
    Traceback (most recent call last):
      File "/usr/lib64/python3.10/unittest/mock.py", line 1369, in patched
        return func(*newargs, **newkeywargs)
      File ".../simple-salesforce/simple_salesforce/tests/test_bulk.py", line 637, in test_add_autosized_batches
        self.assertEqual(len(data), len(reconstructed_data))
    AssertionError: 30000 != 29999
    

    It seems that a single record is skipped somewhere, and that the first batch is not quite 10,000 records.

    I have a fix and a couple of tests I will make into a PR very shortly...

    opened by alexdutton 0
Releases(v1.12.2)
  • v1.12.2(Sep 15, 2022)

    Remove leftover reference to Python 3.5 support (https://github.com/simple-salesforce/simple-salesforce/pull/563) Support for Python 3.5 was dropped in https://github.com/simple-salesforce/simple-salesforce/pull/556, so it should not be listed as a supported version in the README.

    add connected app support

    Bug/v1.12.2/bulk autosize batch (https://github.com/simple-salesforce/simple-salesforce/pull/575)

    add waits as arguments to exposed bulk api methods (https://github.com/simple-salesforce/simple-salesforce/pull/565)

    add ability to bypass results being returned

    Avoid a ZeroDivisionError in bulk.py (https://github.com/simple-salesforce/simple-salesforce/pull/568)

    Raise an error if no data provided in bulk queries

    Source code(tar.gz)
    Source code(zip)
  • v1.12.1(Jul 18, 2022)

  • v1.12.0(Jul 13, 2022)

    v1.12.0

    Drop support for Python 3.5

    Features

    • [#507] Added automatic session refresh
    • [#546] Added dynamic sized batches to respect bulk api limits
    • [#548] Added optional dict flag for results returned in queries
    • [#550] Added support for OAuth2 endpoints
    Source code(tar.gz)
    Source code(zip)
  • v1.11.6(Feb 28, 2022)

  • v1.11.5(Feb 1, 2022)

    v1.11.5 Changes as of 2022-01-05

    Bugs #518 Fix hanging batch results in bulk api Features #475 Added support for all metadata api operations #485 Added base64 function to support upload, update, and get Other #495 Add parse_float to allow parsing of floats to decimal.Decimal #512 Updated README information for JWT Token Remove pypy3 from Travis-ci build

    Source code(tar.gz)
    Source code(zip)
  • v1.11.4(Sep 9, 2021)

    Other

    • [#494] Raise exception when bulk query returns a failure status
    • [#503] Fix lint warnings
    • [#497] Support non-standard https port number
    Source code(tar.gz)
    Source code(zip)
  • v1.11.3(Jul 27, 2021)

  • v1.11.2(Jun 10, 2021)

    v1.11.2: Fix bulk call results (#474) Update api.py Fix Trailing Whitespace Travis CI Update version.py Update CHANGES Update bulk.py Update version.py Update test_bulk.py Refactor bulk thread pool ("with concurrent.futures.ThreadPoolExecutor() as pool:")

    Source code(tar.gz)
    Source code(zip)
  • v1.11.1(Mar 31, 2021)

    Features

    • [445] Added wrapper for Tooling API
    • [451] Support JWT without file for private key

    Bugs

    • [454] Fixed typo in metadata file
    • [443] Fix to prevent silent failure by preventing NaN in payload
    Source code(tar.gz)
    Source code(zip)
  • v1.11.0(Dec 7, 2020)

  • v1.10.1(Jul 14, 2020)

  • v1.10.0(Jun 5, 2020)

    Added batch size and concurrency mode for bulk operations Added lazy loading for query operation Updated readme dropping python 2.x and 3.3,3.4 support and additional updates for new bulk function.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Mar 20, 2020)

    1.0.0 release of library

    • Bumps version to 1.0 and changes from "Beta" to "Production"
    • Removes support for 2.6, 2.7, 3.3, and 3.4
    • Increases default API version to 42.0
    • Removes depreciated interfaces
    • Ran isort on library to clean up imports
    • Replace requests[security] with requests above 2.22.0
    • Fixes an issue with Travis's config not being formatted correctly
    Source code(tar.gz)
    Source code(zip)
  • v0.75.2(Feb 26, 2020)

  • v0.75.0(Feb 25, 2020)

  • v0.74.1(May 5, 2018)

  • v0.73.0(Jul 2, 2017)

  • v0.67.2(May 11, 2015)

Owner
simple salesforce
simple salesforce
A simple python script for rclone. Use multiple Google Service Accounts and cycle through them.

About GSAclone GSAclone is a simple python script for rclone, written with the purpose of using multiple Google service accounts on Google Drive and "

Shiro39 6 Feb 25, 2022
An Async Bot/API wrapper for Twitch made in Python.

TwitchIO is an asynchronous Python wrapper around the Twitch API and IRC, with a powerful command extension for creating Twitch Chat Bots. TwitchIO co

TwitchIO 590 Jan 03, 2023
Automatically Forward files from groups to channel & FSub

Backup & ForceSub Automatically Forward files from groups to channel & Do force sub on members Variables API_ID : Get from my.telegram.org API_HASH :

Arunkumar Shibu 7 Nov 06, 2022
Singer Tap for dbt Artifacts built with the Meltano SDK

tap-dbt-artifacts tap-dbt-artifacts is a Singer tap for dbtArtifacts. Built with the Meltano SDK for Singer Taps.

Prratek Ramchandani 9 Nov 25, 2022
Drcom-pt-client - Drcom Pt version client with refresh timer

drcom-pt-client Drcom Pt version client with refresh timer Dr.com Pt版本客户端 可用于网页认

4 Nov 16, 2022
A Powerful Telethon Based Telegram Spam Bot.

Yukki Multi Spam Bot 🚀 Deploy on Heroku You can Use these API ID and API HASH while deploying String Session No Requirement of API ID and API HASH Ge

46 Dec 23, 2022
Group Chat Spammer For Discord

Group Chat Spammer For Discord Free and public gc spammer

Dreamy 20 Dec 27, 2022
Gorrabot is a bot made to automate checks and processes in the development process.

Gorrabot is a Gitlab bot made to automate checks and processes in the Faraday development. Features Check that the CHANGELOG is modified By default, m

Faraday 7 Dec 14, 2022
Telegram music & video bot direct play music

Telegram music & video bot direct play music

noinoi-X 1 Dec 28, 2021
A simple script that will watch a stream for you and earn the channel points.

Credits Main idea: https://github.com/gottagofaster236/Twitch-Channel-Points-Miner Bet system (Selenium): https://github.com/ClementRoyer/TwitchAutoCo

Alessandro Maggio 1.1k Jan 08, 2023
Spotify Top Lists - get the current top lists of a user from the Spotify API and display them in a Flask app

Spotify Top Lists This is a simple script that will get the current top lists of a user from the Spotify API and display them in a Flask app. Requirem

Yasin 0 Oct 16, 2022
Slack->DynamDB->Some applications

slack-event-subscriptions About The Project Do you want to get simple attendance checks? If you are using Slack, participants can just react on a spec

UpstageAI 26 May 28, 2022
Who are we? We are the Hunters of all Torrent in this world.🗡️.Fork from SlamDevs

MIRROR HUNTER This Mirror Bot is a multipurpose Telegram Bot writen in Python for mirroring files on the Internet to our beloved Google Drive. Repo la

Anime Republic 130 May 28, 2022
A basic template for Creating Odoo Module

Odoo ERP Boilerplate A basic template for Creating Odoo Module. Folders inside this repository consist of snippet code and a module example. Folders w

Altela Eleviansyah Pramardhika 1 Feb 06, 2022
A results generator and automatic token checker for Yandex Contest

Yandex contest Python checking tools A results generator and automatic token checker for Yandex Contest. Версия на русском языке Installation Clone th

Nikolay Chechulin 9 Dec 14, 2022
A Powerfull Userbot Telegram PandaX_Userbot, Vc Music Userbot + Bot Manager based Telethon

Support ☑ CREDITS THANKS YOU VERRY MUCH FOR ALL Telethon Pyrogram TeamUltroid TeamUserge CatUserbot pytgcalls Dan Lainnya

22 Dec 25, 2022
The most annoying bot on Discord

FBot The most annoying bot on discord Features Lots of fun stuff Message responses, sort of our main feature, no big deal. FBot can respond to a wide

Jude 33 Jun 25, 2022
Cleaning Tiktok Hacks With Python

Cleaning Tiktok Hacks With Python

13 Jan 06, 2023
StringSessionGenerator - A Telegram bot to generate pyrogram and telethon string session

⭐️ String Session Generator ⭐️ Genrate String Session Using this bot. Made by TeamUltronX 🔥 String Session Demo Bot: Environment Variables Mandatory

TheUltronX 1 Dec 31, 2021
AminoAutoRegFxck/AutoReg For AminoApps.com

AminoAutoRegFxck AminoAutoRegFxck/AutoReg For AminoApps.com Termux apt update -y apt upgrade -y pkg install python git clone https://github.com/deluvs

3 Jan 18, 2022