An absurdly simple package for making Salesforce Rest API calls.
by Aaron Caffrey, Colin Cheevers, Jose Garcia, Tania Prince
Salesforce.com
- Introduction
- Contributors
- Requirements
- Install
- Create a client and log in
- Query
- Query More
- Insert SObject
- Update SObject
- Delete SObject
- Query SObject Row
- Describe SObject
- Describe Global
- Insert File
- Search
- Execute Anonymous
- Approval Process
- Chatter
- Wave
- Logout
- Contributing
- FAQ
The reason this package exists is to produce:
- A Salesforce client that is reusable, minimalistic, and pythonic
- Interfaces that are closely knit to the Salesforce Rest API service specification
- Gradual support for the Salesforce API extended family (ie. Chatter, Analytics, Wave, Tooling, Bulk, Metadata, etc.)
Thanks goes to the people who have contributed code to this module, see the GitHub Contributors page.
- Python 2 or 3
pip install git+ssh://[email protected]/forcedotcom/SalesforcePy.git
- Download and extract, or clone this repo
cd
into the/SalesforcePy
directory- Run the following:
pip install .
Getting started is a three-step process:
- Import
SalesforcePy
- Create a client
- Perform a login request
import SalesforcePy as sfdc
# Create an instance of a Salesforce client, replacing the credentials below with valid ones.
client = sfdc.client(
username="[email protected]",
password="p@ssword1",
client_id="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
client_secret="123456789123456789",
login_url="test.salesforce.com",
version = "38.0", #optional parameter, defaults to the latest version for your instance
timeout = "30", # optional, defines a connect/read timeout value, if not specified requests can hang for minutes or more.
)
# Log in
login_results = client.login()
In the example above, login_results[0]
will be a dict with the response from the Salesforce OAuth resource. The only supported flow at present is username-password. For more on the response, see: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_understanding_username_password_oauth_flow.htm.
You can also use the context manager which handles login()
and logout()
automatically.
client_args = {'username' :'[email protected]',
'password' :'p@ssword1',
'client_id': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'client_secret' : '123456789123456789'
}
with sfdc.client(**client_args) as client:
search_result = client.search('FIND {"test"} RETURNING Case(Id)')
Once the login call has been performed successfully, the client will maintain the session leaving you free to make API calls. This example demonstrates how to perform a query.
query_results = client.query('SELECT Id, Name FROM Account LIMIT 1')
In the example above query_results[0]
will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm.
While the client query()
method can be useful for making requests when you're expecting a small amount of data, a single query result won't give you all records if the total size of records exceeds the standard batch size (2000).
In such scenarios, you may wish to use query_more()
. As the example shows, you simply provide the query string in the same way you did previously with query()
:
query_results = client.query_more('SELECT Id, Name FROM Account')
In the example above query_results[0]
will be a list of dicts, each of which is a query result (batch). The behaviour of query_more
is to consume "nextRecordsUrl"
of each query result recursively until it runs out. For more on this topic, check out https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm under the heading "Retrieving the Remaining SOQL Query Results".
From the client, the sobjects
class can be used to perform DML statements and file i/o with Salesforce objects. This example shows how to insert.
create_result = client.sobjects(object_type='Account').insert({"Name" : "SalesforcePy"})
In the example above create_result[0]
will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm.
Update works similarly to insert, with the main difference being that id
is a required kwarg in sobjects
so it is clear which record is to be updated.
update_result = client.sobjects(id='0010Y0000055YG7QAM',object_type='Account').update({"Name" : "SalesforcePy 2"})
In the example above update_result[0]
will be None
. This is because the HTTP method used under the hood is PATCH
, for which the expected success code is 204
. The success code can be found in update_result[1].status
. For more, read: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm.
delete_result = client.sobjects(id='0010Y0000055YG7QAM',object_type='Account').delete()
In the example above delete_result[0]
will be None
. This is because the HTTP method used under the hood is DELETE
, for which the expected success code is 204
. The success code can be found in delete_result[1].status
. For more, read: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_delete_record.htm.
If you know the ID of a record, you can easily retrieve the entire row using this method. This may be preferable to the query
call documented further up if you wish to get all fields without specifying them.
query_result = client.sobjects( object_type="Account", id="0010Y0000056ljcQAA" ).query()
The describe method retrieves the individual metadata at all levels for the specified SObject
describe_result = client.sobjects(object_type='Account').describe()
In the example above describe_result[0]
will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm. The If-Modified-Since header cannot be used with this method.
The describe global method lists the available objects and their metadata for the organization’s data. In addition, it provides the organization encoding, as well as the maximum batch size permitted in queries.
describe_global_result = client.sobjects().describe_global()
In the example above describe_global_result[0]
will be a dict with the response as documented here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_describeGlobal.htm. The If-Modified-Since header cannot be used with this method.
There are some special objects in Salesforce such as Attachment
, Document
, and ContentVersion
which allow storage of files as blobs. The following is an example of how to insert a file.
# Create a file tuple ordered like so: ( filename, body, content_type )
file = ( "SalesforcePy.txt", "Hello world", "text/plain" )
insert_result = client.sobjects(object_type = "Attachment", binary_field="Body").insert({
"Name":"SalesforcePy",
"ParentId":"0010Y0000056ljcQAA",
"Description":"An excellent package"
}, binary=file ) # Pass your file through using the binary kwarg
SOSL search statements can be made like so:
search_result = client.search('FIND {SalesforcePy} RETURNING Account(Id, Name) LIMIT 5')
Anonymous Apex can be executed in a Salesforce organisation like so:
ea_result = client.execute_anonymous('system.debug(\'Hello world.\');')
Approvals can be retrieved, submitted and approved/rejected
ap_result = client.approvals(requestBody)
See documentation for sample request body
Create a feed item (chatter post). It returns a 201 status code for a successful request. See Chatter REST api documentation for information on the expected body to create feed items.
# create chatter post
client.chatter.feed_item(body)
# create a comment on a chatter post
client.chatter.feed_comment('feed-elementid', body)
Retrieve a wave data set using the datataset()
function.
client.wave.dataset("opportunities")
Perform a SAQL query using the wave query()
function.
query = {
"query": """q = load \"0Fb0N000000XuvBSAS/0Fc0N000001M5BMSA0\";\nq = filter q by 'Account.Industry' in
[\"Apparel\", \"Banking\", \"Biotechnology\"];\nq = group q by 'Account.Industry';\nq = foreach q generate
'Account.Industry' as 'Account.Industry', count() as 'count';\nq = order q by 'Account.Industry' asc;\nq = limit q
2000;"""
}
client.wave.query(query)
Expires the session by revoking the access token. It returns a 200 status code for a successful token revocation.
client.logout()
- Fork this repo
git clone -b <yourbranch> <yourfork>
- From within the project root directory run
pip install .
- Develop
- Cover your code in
/tests
- Create a pull request to the
forcedotcom:developer
branch
We recommend following the Style Guide for Python the best you can: https://www.python.org/dev/peps/pep-0008/.
autopep8
is a great tool for automatic formatting, we encourage its use: https://pypi.python.org/pypi/autopep8.
- Log in to Workbench: https://workbench.developerforce.com/login.php
- Go to Info > Standard and Custom Objects
- In the Object dropdown, choose the object you wish to inspect (eg. Case) then click Select
- Expand Fields. You should find what you're looking for here.
- Log in to Workbench: https://workbench.developerforce.com/login.php
- Go to Queries > SOQL Query
- Enter your query or optionally use the form to help you build the query, then click Query
Yes. Here's an example of how to do it, and what to expect.
import logging
import SalesforcePy as sfdc
username = "[email protected]"
password = "p@ssword1"
client_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client_secret = "123456789123456789"
client = sfdc.client(
username=username,
password=password,
client_id=client_id,
client_secret=client_secret
)
client.debug(level=logging.INFO) # Tell the client to debug at an info level
client.login() # Outputs "POST https://login.salesforce.com/services/oauth2/token" to logs
Yes. Here's an example of how to do it.
import SalesforcePy as sfdc
username = "[email protected]"
password = "p@ssword1"
client_id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
client_secret = "123456789123456789"
client = sfdc.client(
username=username,
password=password,
client_id=client_id,
client_secret=client_secret,
proxies={"https": "localhost:8888/example/"} # `proxies` kwarg takes a dict as required by the `requests` module.
)
Some of the parameters that are optionally defined at the client level can be defined at the function level as well. Function level arguments supersede the client arguments.
For example, you may want to define an overall timeout value of "30"
for all requests but specify a higher
value for query calls.
client = sfdc.client(
username=username,
password=password,
client_id=client_id,
client_secret=client_secret,
timeout="30"
)
query_kwarg= {"timeout" : "60"}
client.query("Select Id FROM Account",**query_kwarg)
The following parameters support function level overriding:
proxies
timeout
version