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
Spore REST API asyncio client

Spore REST API asyncio client

LEv145 16 Aug 2, 2022
A client library for the REST API of DocuWare's DMS

docuware-client This is a client library for the REST API of DocuWare DMS. Since DocuWare's documentation regarding the REST API is very sparse (at th

Stefan Schönberger 1 Feb 23, 2022
TM1py is a Python package that wraps the TM1 REST API in a simple to use library.

By wrapping the IBM Planning Analytics (TM1) REST API in a concise Python framework, TM1py facilitates Python developments for TM1. Interacting with T

Cubewise CODE 147 Dec 15, 2022
pyDuinoCoin is a simple python integration for the DuinoCoin REST API, that allows developers to communicate with DuinoCoin Master Server

PyDuinoCoin PyDuinoCoin is a simple python integration for the DuinoCoin REST API, that allows developers to communicate with DuinoCoin Main Server. I

BackrndSource 6 Jul 14, 2022
wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3) for Python

wyscoutapi wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3). Usage Install with pip install wyscoutapi. To connect to the Wys

Ben Torvaney 11 Nov 22, 2022
Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Beyonic API Python official client library simplified examples using Flask, Django and Fast API.

Harun Mbaabu Mwenda 46 Sep 1, 2022
Python API Client for Twitter API v2

?? Python Client For Twitter API v2 ?? Why Twitter Stream ? Twitter-Stream.py a python API client for Twitter API v2 now supports FilteredStream, Samp

Twitivity 31 Nov 19, 2022
Dns-Client-Server - Dns Client Server For Python

Dns-client-server DNS Server: supporting all types of queries and replies. Shoul

Nishant Badgujar 1 Feb 15, 2022
Raphtory-client - The python client for the Raphtory project

Raphtory Client This is the python client for the Raphtory project Install via p

Raphtory 5 Apr 28, 2022
An simple python script for remove rockstar account for fivem, very useful for cheating or change account for unban from an server, remember change steam account.

This tool is used for be unbanned from servers, u need disconnect the discord, use other steam account and uninstall xbox for be unbanned 100%, it only work for unban in server, not global

MiguDEV 4 Oct 10, 2022
Drcom-pt-client - Drcom Pt version client with refresh timer

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

null 4 Nov 16, 2022
A Very Simple Telegram Files Rename Bot by @AbirHasan2005

Rename-Bot This is a very simple Telegram Files Rename Bot by @AbirHasan2005. Features Rename Videos, Files or Audios. Permanent Custom Thumbnail Supp

Abir Hasan 124 Dec 25, 2022
ESOLinuxAddonManager - Very simple addon manager for Elder Scrolls Online running on Linux.

ESOLinuxAddonManager Very simple addon manager for Elder Scrolls Online running on Linux. Well, more a downloader for now. Currently it's quite ugly b

Akseli 25 Aug 28, 2022
This is a very simple botnet with a CnC server, made by me. Feel free to change anything

This is a very simple botnet with a CnC server, made by me. Feel free to change anything

null 8 Nov 12, 2022
A Python Library to interface with Flickr REST API, OAuth & JSON Responses

Python-Flickr Python-Flickr is A Python library to interface with Flickr REST API & OAuth Features Photo Uploading Retrieve user information Common Fl

Mike Helmick 40 Sep 25, 2021
A Python Library to interface with Tumblr v2 REST API & OAuth

Tumblpy Tumblpy is a Python library to help interface with Tumblr v2 REST API & OAuth Features Retrieve user information and blog information Common T

Mike Helmick 125 Jun 20, 2022
Python library for interacting with the Wunderlist 2 REST API

Overview Wunderpy2 is a thin Python library for accessing the official Wunderlist 2 API. What does a thin library mean here? Only the bare minimum of

mieubrisse 24 Dec 29, 2020
📦 Opensource Python wrapper for Hiven's REST and WebSocket API

hiven.py ?? Opensource Python wrapper for Hiven's REST and WebSocket API Installation pip install -U hiven.py Usage hiven.py is currently under devel

Kevin Thomas 3 Sep 3, 2021
NiceHash Python Library and Command Line Rest API

NiceHash Python Library and Command Line Rest API Requirements / Modules pip install requests Required data and where to get it Following data is nee

Ashlin Darius Govindasamy 2 Jan 2, 2022