Learn R Programming

⚠️There's a newer version (1.0.1) of this package.Take me there.

salesforcer

{salesforcer} is an R package that connects to Salesforce Platform APIs using tidy principles. The package implements actions from the SOAP, REST, Bulk 1.0, Bulk 2.0, and Metadata APIs.

Package features include:

  • OAuth 2.0 (Single Sign On) and Basic (Username-Password) Authentication methods (sf_auth())
  • CRUD (Create, Retrieve, Update, Delete) methods for records using the SOAP, REST, and Bulk APIs
  • Query records via the SOAP, REST, Bulk 1.0, and Bulk 2.0 APIs using sf_query()
  • Manage and execute reports and dashboards with:
    • sf_list_reports(), sf_create_report(), sf_run_report(), and more
  • Retrieve and modify metadata (Custom Objects, Fields, etc.) using the Metadata API with:
    • sf_describe_objects(), sf_create_metadata(), sf_update_metadata(), and more
  • Utilize backwards compatible functions for the {RForcecom} package, such as:
    • rforcecom.login(), rforcecom.getObjectDescription(), rforcecom.query(), rforcecom.create()
  • Basic utility calls (sf_user_info(), sf_server_timestamp(), sf_list_objects())
  • Functions to assist with master data management (MDM) or data integrity of records by finding duplicates (sf_find_duplicates(), sf_find_duplicates_by_id()), merging records (sf_merge()), and converting leads (sf_convert_lead())
  • Recover (sf_undelete()) or delete from the Recycle Bin (sf_empty_recycle_bin()) and list ids of records deleted (sf_get_deleted()) or updated (sf_get_updated()) within a specific timeframe
  • Passing API call control parameters such as, “All or None”, “Duplicate Rule”, “Assignment Rule” execution and many more!

Table of Contents

Installation

# install the current CRAN version (0.2.0)
install.packages("salesforcer")

# or get the development version on GitHub
# install.packages("devtools")
devtools::install_github("StevenMMortimer/salesforcer")

If you encounter an issue while using this package, please file a minimal reproducible example on GitHub.

Vignettes

The README below outlines the basic package functionality. For more information please feel free to browse the pkgdown site at https://StevenMMortimer.github.io/salesforcer which contains the following vignettes:

Usage

Authenticate

First, load the {salesforcer} package and login. There are two ways to authenticate:

  1. OAuth 2.0
  2. Basic Username-Password

It is recommended to use OAuth 2.0 so that passwords do not have to be shared or embedded within scripts. User credentials will be stored in locally cached file entitled “.httr-oauth-salesforcer” in the current working directory. Also, note that if you use OAuth 2.0 authentication then the package will automatically refresh it so you will not have to call sf_auth() during each session if you have a cached “.httr-oauth-salesforcer” file in the working directory. The cache file is named that way to not conflict with the “.httr-oauth” files created by other packages.

library(dplyr, warn.conflicts = FALSE)
library(salesforcer)

# Using OAuth 2.0 authentication
sf_auth()

# Using Basic Username-Password authentication
sf_auth(username = "test@gmail.com", 
        password = "{PASSWORD_HERE}",
        security_token = "{SECURITY_TOKEN_HERE}")

After logging in with sf_auth(), you can check your connectivity by looking at the information returned about the current user. It should be information about you!

# pull down information of person logged in
# it's a simple easy call to get started 
# and confirm a connection to the APIs
user_info <- sf_user_info()
sprintf("Organization Id: %s", user_info$organizationId)
#> [1] "Organization Id: 00D6A0000003dN3UAI"
sprintf("User Id: %s", user_info$userId)
#> [1] "User Id: 0056A000000MPRjQAO"

Create

Salesforce has objects and those objects contain records. One default object is the “Contact” object. This example shows how to create two records in the Contact object.

n <- 2
new_contacts <- tibble(FirstName = rep("Test", n),
                       LastName = paste0("Contact-Create-", 1:n))
created_records <- sf_create(new_contacts, object_name = "Contact")
created_records
#> # A tibble: 2 x 2
#>   id                 success
#>   <chr>              <lgl>  
#> 1 0033s000013Y9dsAAC TRUE   
#> 2 0033s000013Y9dtAAC TRUE

Query

Salesforce has proprietary form of SQL called SOQL (Salesforce Object Query Language). SOQL is a powerful tool that allows you to return the attributes of records on almost any object in Salesforce including Accounts, Contacts, Tasks, Opportunities, even Attachments! Below is an example where we grab the data we just created including Account object information for which the Contact record is associated with.

my_soql <- sprintf("SELECT Id, 
                           Account.Name, 
                           FirstName, 
                           LastName 
                    FROM Contact 
                    WHERE Id in ('%s')", 
                   paste0(created_records$id , collapse = "','"))
queried_records <- sf_query(my_soql)
queried_records
#> # A tibble: 2 x 3
#>   Id                 FirstName LastName        
#>   <chr>              <chr>     <chr>           
#> 1 0033s000013Y9dsAAC Test      Contact-Create-1
#> 2 0033s000013Y9dtAAC Test      Contact-Create-2

You’ll notice that the "Account.Name" column does not appear in the results. This is because the SOAP and REST APIs do not return any Account information if it does not exist on the record and there is no reliable way to extract and rebuild the empty columns based on the query string. If there were Account information, an additional column titled "Account.Name" would appear in the results. Note, that the Bulk 1.0 and Bulk 2.0 APIs will return "Account.Name" as a column of all NA values for this query because they return results differently.

Update

After creating records you can update them using sf_update(). Updating a record requires you to pass the Salesforce Id of the record. Salesforce creates a unique 18-character identifier on each record and uses that to know which record to attach the update information you provide. Simply include a field or column in your update dataset called “Id” and the information will be matched. Here is an example where we update each of the records we created earlier with a new first name called “TestTest”.

# Update some of those records
queried_records <- queried_records %>%
  mutate(FirstName = "TestTest")

updated_records <- sf_update(queried_records, object_name = "Contact")
updated_records
#> # A tibble: 2 x 2
#>   id                 success
#>   <chr>              <lgl>  
#> 1 0033s000013Y9dsAAC TRUE   
#> 2 0033s000013Y9dtAAC TRUE

Bulk Operations

For really large operations (inserts, updates, upserts, deletes, and queries) Salesforce provides the Bulk 1.0 and Bulk 2.0 APIs. In order to use the Bulk APIs in {salesforcer} you can just add api_type = "Bulk 1.0" or api_type = "Bulk 2.0" to your functions and the operation will be executed using the Bulk APIs. It’s that simple.

The benefits of using the Bulk API for larger datasets is that the operation will reduce the number of individual API calls (organization usually have a limit on total calls) and batching the requests in bulk is usually quicker than running thousands of individuals calls when your data is large. Note: the Bulk 2.0 API does NOT guarantee the order of the data submitted is preserved in the output. This means that you must join on other data columns to match up the Ids that are returned in the output with the data you submitted. For this reason, Bulk 2.0 may not be a good solution for creating, updating, or upserting records where you need to keep track of the created Ids. The Bulk 2.0 API would be fine for deleting records where you only need to know which Ids were successfully deleted.

# create contacts using the Bulk API
n <- 2
new_contacts <- tibble(FirstName = rep("Test", n),
                       LastName = paste0("Contact-Create-", 1:n))
created_records <- sf_create(new_contacts, "Contact", api_type = "Bulk 1.0")
created_records
#> # A tibble: 2 x 4
#>   Id                 Success Created Error
#>   <chr>              <lgl>   <lgl>   <lgl>
#> 1 0033s000013Y9dwAAC TRUE    TRUE    NA   
#> 2 0033s000013Y9dxAAC TRUE    TRUE    NA

# query large recordsets using the Bulk API
my_soql <- sprintf("SELECT Id,
                           FirstName, 
                           LastName
                    FROM Contact 
                    WHERE Id in ('%s')", 
                   paste0(created_records$Id , collapse = "','"))

queried_records <- sf_query(my_soql, "Contact", api_type = "Bulk 1.0")
queried_records
#> # A tibble: 2 x 3
#>   Id                 FirstName LastName        
#>   <chr>              <chr>     <chr>           
#> 1 0033s000013Y9dwAAC Test      Contact-Create-1
#> 2 0033s000013Y9dxAAC Test      Contact-Create-2

# delete these records using the Bulk 2.0 API
deleted_records <- sf_delete(queried_records$Id, "Contact", api_type = "Bulk 2.0")
deleted_records
#> # A tibble: 2 x 4
#>   Id                 sf__Id             sf__Created sf__Error
#>   <chr>              <chr>              <lgl>       <lgl>    
#> 1 0033s000013Y9dwAAC 0033s000013Y9dwAAC FALSE       NA       
#> 2 0033s000013Y9dxAAC 0033s000013Y9dxAAC FALSE       NA

Using the Metadata API

Salesforce is a very flexible platform in that it provides the Metadata API for users to create, read, update and delete their entire Salesforce environment from objects to page layouts and more. This makes it very easy to programmatically setup and teardown the Salesforce environment. One common use case for the Metadata API is retrieving information about an object (fields, permissions, etc.). You can use the sf_read_metadata() function to return a list of objects and their metadata. In the example below we retrieve the metadata for the Account and Contact objects. Note that the metadata_type argument is “CustomObject”. Standard Objects are an implementation of CustomObjects, so they are returned using that metadata type.

read_obj_result <- sf_read_metadata(metadata_type='CustomObject',
                                    object_names=c('Account', 'Contact'))
read_obj_result[[1]][c('fullName', 'label', 'sharingModel', 'enableHistory')]
#> $fullName
#> [1] "Account"
#> 
#> $label
#> [1] "Account"
#> 
#> $sharingModel
#> [1] "ReadWrite"
#> 
#> $enableHistory
#> [1] "false"
first_two_fields_idx <- head(which(names(read_obj_result[[1]]) == 'fields'), 2)
# show the first two returned fields of the Account object
read_obj_result[[1]][first_two_fields_idx]
#> $fields
#> $fields$fullName
#> [1] "AccountNumber"
#> 
#> $fields$trackFeedHistory
#> [1] "false"
#> 
#> 
#> $fields
#> $fields$fullName
#> [1] "AccountSource"
#> 
#> $fields$trackFeedHistory
#> [1] "false"
#> 
#> $fields$type
#> [1] "Picklist"

The data is returned as a list because object definitions are highly nested representations. You may notice that we are missing some really specific details, such as, the picklist values of a field with type “Picklist”. You can get that information using sf_describe_object_fields(). Here is an example using sf_describe_object_fields() where we get a tbl_df with one row for each field on the Account object:

acct_fields <- sf_describe_object_fields('Account')
acct_fields %>% select(name, label, length, soapType, type)
#> # A tibble: 69 x 5
#>   name           label            length soapType    type     
#>   <chr>          <chr>            <chr>  <chr>       <chr>    
#> 1 Id             Account ID       18     tns:ID      id       
#> 2 IsDeleted      Deleted          0      xsd:boolean boolean  
#> 3 MasterRecordId Master Record ID 18     tns:ID      reference
#> 4 Name           Account Name     255    xsd:string  string   
#> 5 Type           Account Type     40     xsd:string  picklist 
#> # … with 64 more rows

# show the picklist selection options for the Account Type field
acct_fields %>% 
  filter(label == "Account Type") %>% 
  .$picklistValues
#> [[1]]
#> # A tibble: 7 x 4
#>   active defaultValue label                      value                     
#>   <chr>  <chr>        <chr>                      <chr>                     
#> 1 true   false        Prospect                   Prospect                  
#> 2 true   false        Customer - Direct          Customer - Direct         
#> 3 true   false        Customer - Channel         Customer - Channel        
#> 4 true   false        Channel Partner / Reseller Channel Partner / Reseller
#> 5 true   false        Installation Partner       Installation Partner      
#> # … with 2 more rows

Future

Future APIs to support (roughly in priority order):

Credits

This application uses other open source software components. The authentication components are mostly verbatim copies of the routines established in the {googlesheets} package (https://github.com/jennybc/googlesheets). Methods are inspired by the {RForcecom} package (https://github.com/hiratake55/RForcecom). We acknowledge and are grateful to these developers for their contributions to open source.

More Information

Salesforce provides client libraries and examples in many programming languages (Java, Python, Ruby, and PhP) but unfortunately R is not a supported language. However, most all operations supported by the Salesforce APIs are available via this package. This package makes requests best formatted to match what the APIs require as input. This articulation is not perfect and continued progress will be made to add and improve functionality. For details on formatting, attributes, and methods please refer to Salesforce’s documentation as they are explained better there. More information is also available on the pkgdown site at https://StevenMMortimer.github.io/salesforcer.

Get supported salesforcer with the Tidelift Subscription


Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

Top

Copy Link

Version

Install

install.packages('salesforcer')

Monthly Downloads

6,002

Version

0.2.0

License

MIT + file LICENSE

Issues

Pull Requests

Stars

Forks

Maintainer

Steven Mortimer

Last Published

July 21st, 2020

Functions in salesforcer (0.2.0)

VERB_n

Generic implementation of HTTP methods with retries and authentication
accepted_controls_by_operation

Return the Accepted Control Arguments by Operation
bind_query_resultsets

Bind the results of paginated queries
catch_errors

Function to catch and print HTTP errors
accepted_controls_by_api

Return the Accepted Control Arguments by API Type
build_manifest_xml_from_list

Bulk Binary Attachments Manifest List to XML Converter
build_proxy

Function to build a proxy object to pass along with httr requests
make_analytics_notification_operations_url

Analytics Notification operations URL generator
catch_unknown_api

Catch unknown API type
build_metadata_xml_from_list

Metadata List to XML Converter
make_analytics_notifications_limits_url

Analytics Notification limits URL generator
collapse_list_with_dupe_names

Collapse Elements in List with Same Name
drop_attributes_recursively

Recursively remove attributes data from list
combine_parent_and_child_resultsets

Bind the records from nested parent-to-child queries
drop_attributes

Remove Salesforce attributes data from list
build_soap_xml_from_list

Build XML Request Body
flatten_tbl_df

Flatten list and convert to tibble
check_and_encode_files

Check that file paths exist and data is encoded if specified
filter_valid_controls

Filter Out Control Arguments by API or Operation
compact2

Remove all zero-length elements from list ignoring AsIs elements
make_analytics_notifications_list_url

Analytics Notification list URL generator
format_headers_for_verbose

Format Headers for Printing
make_base_metadata_url

Base Metadata API URL Generator
make_bulk_get_all_jobs_url

Bulk Get All Jobs Generic URL Generator
make_bulk_get_all_query_jobs_url

Bulk Get All Query Jobs Generic URL Generator
format_report_row

Format a single "rows" element from a report fact map
make_bulk_batch_details_url

Bulk Batch Details URL Generator
make_bulk_batch_status_url

Bulk Batch Status URL Generator
make_analytics_folder_share_recipients_url

Analytics Folder share recipients URL generator
drop_empty_recursively

Remove all NULL or zero-length elements from list
make_analytics_folder_shares_url

Analytics Folder shares URL generator
drop_nested_child_records

Drop nested child records in a record
make_query_url

Query URL Generator
make_composite_url

Composite URL Generator
is_legit_token

Check that token appears to be legitimate
extract_nested_child_records

Extract nested child records in a record
extract_records_from_xml_nodeset

Pulls out a tibble of record info from an XML node
extract_records_from_xml_node

Pulls out a tibble of record info from an XML node
make_report_create_url

Report Create URL generator
make_dashboard_copy_url

Dashboard Copy URL generator
make_base_rest_url

Base REST API URL Generator
make_base_soap_url

Base SOAP API URL Generator
make_analytics_folder_child_operations_url

Analytics Folder child operations URL generator
make_report_describe_url

Report Describe URL generator
extract_records_from_xml_nodeset_of_records

Pulls out a tibble of record info from a nodeset of "records" elements
make_report_copy_url

Report Copy URL generator
make_chatter_users_url

Chatter Users URL Generator
make_report_instances_list_url

Report Instances List URL generator
get_os

Determine the host operating system
guess_object_name_from_soql

Try to Guess the Object if User Does Not Specify for Bulk Queries
make_composite_batch_url

Composite Batch URL Generator
list_extract_parent_and_child_result

Extract tibble of a parent-child record from one JSON element
make_bulk_get_job_url

Bulk Get Job Generic URL Generator
make_report_type_describe_url

Report Type Describe URL generator
make_report_types_list_url

Report Type List URL generator
make_analytics_folder_share_by_id_url

Analytics Folder share by Id URL generator
make_analytics_folder_operations_url

Analytics Folder operations URL generator
make_bulk_job_records_url

Bulk Job Records URL Generator
make_report_query_url

Report Query URL generator
make_parameterized_search_url

Parameterized Search URL Generator
make_login_url

Login URL Generator
make_dashboard_url

Dashboard URL generator
make_reports_list_url

Reports List URL generator
make_report_url

Report URL generator
make_bulk_create_job_url

Bulk Create Job URL Generator
make_bulk_delete_job_url

Bulk Delete Job Generic URL Generator
make_bulk_end_job_generic_url

Bulk End Job Generic URL Generator
make_dashboards_list_url

Dashboard list URL generator
make_analytics_folder_collections_url

Analytics Folder collections URL generator
make_bulk_batches_url

Bulk Batches URL Generator
make_bulk_query_url

Bulk Query URL Generator
make_bulk_query_result_url

Bulk Query Result URL Generator
make_dashboard_describe_url

Dashboard describe URL generator
make_dashboard_filter_operators_list_url

Dashboard filter operators list URL generator
make_report_execute_url

Report Execute URL generator
make_report_fields_url

Report Fields URL generator
make_dashboard_status_url

Dashboard status URL generator
make_report_instance_url

Report Instance URL generator
make_report_filter_operators_list_url

Report Filter Operator List URL generator
make_dashboard_filter_options_analysis_url

Dashboard filter options analysis URL generator