An Implementation of 'Salesforce' APIs Using Tidy Principles

An implementation of the 'Salesforce' Platform APIs (REST, SOAP, Bulk 1.0, Bulk 2.0, and Metadata) < https://developer.salesforce.com/page/Salesforce_APIs>. This package is an articulation of the most API methods into R. The API calls return XML or JSON that is parsed tidy data structures. For more details please see the 'Salesforces' API references and this package's website < https://stevenmmortimer.github.io/salesforcer/> for more information, documentation, and examples.


Build Status AppVeyor Build Status CRAN_Status_Badge Coverage Status

salesforcer is an R package that connects to Salesforce Platform APIs using tidy principles. The package implements most 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, and Bulk 1.0 APIs using sf_query()
  • Retrieve and modify metadata (Custom Objects, Fields, etc.) using the Metadata API with:
    • sf_describe_objects(), sf_create_metadata(), sf_update_metadata()
  • Utilize backwards compatible functions for the RForcecom package, such as:
    • rforcecom.login(), rforcecom.query(), rforcecom.create(), rforcecom.update()
  • Basic utility calls (sf_user_info(), sf_server_timestamp(), sf_list_objects())

Table of Contents

Installation

# install.packages("devtools")
devtools::install_github("StevenMMortimer/salesforcer")

If you encounter a clear bug, please file a minimal reproducible example on GitHub.

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.

suppressWarnings(suppressMessages(library(dplyr)))
suppressWarnings(suppressMessages(library(purrr)))
library(salesforcer)
 
# Using OAuth 2.0 authentication
sf_auth()
 
# Using Basic Username-Password authentication
sf_auth(username = "[email protected]", 
        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("User Id: %s", user_info$id)
#> [1] "User Id: 0056A000000MPRjQAO"
sprintf("User Active?: %s", user_info$isActive)
#> [1] "User Active?: TRUE"

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>              <chr>  
#> 1 0036A00000SnhbfQAB true   
#> 2 0036A00000SnhbgQAB 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. The Account column is all NA since we have yet to provide information to link these Contacts with Accounts.

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 4
#>   Id                 Account FirstName LastName        
#> * <chr>              <lgl>   <chr>     <chr>           
#> 1 0036A00000SnhbfQAB NA      Test      Contact-Create-1
#> 2 0036A00000SnhbgQAB NA      Test      Contact-Create-2

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") %>% 
  select(-Account)
 
updated_records <- sf_update(queried_records, object_name="Contact")
updated_records
#> # A tibble: 2 x 2
#>   id                 success
#>   <chr>              <chr>  
#> 1 0036A00000SnhbfQAB true   
#> 2 0036A00000SnhbgQAB 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, object_name="Contact", api_type="Bulk 1.0")
 
# 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, object_name="Contact", api_type="Bulk 1.0")
 
# delete these records using the Bulk API
deleted_records <- sf_delete(queried_records$Id, object_name="Contact", api_type="Bulk 1.0")

Using the Metadata API

Salesforce is a very flexible platform. Salesforce provides the Metadata API for users to create, read, update and delete the objects, 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 the function sf_describe_object() function which is actually part of the REST and SOAP APIs. It is recommended that you try out the various metadata functions sf_read_metadata(), sf_list_metadata(), sf_describe_metadata(), and sf_describe_objects() in order to see which information best suits your use case.

describe_obj_result <- sf_describe_objects(object_names=c('Account', 'Contact'))
describe_obj_result[[1]][c('label', 'queryable')]
#> $label
#> [1] "Account"
#> 
#> $queryable
#> [1] "true"
# show the first two returned fields of the Account object
the_type_field <- describe_obj_result[[1]][[58]]
the_type_field$name
#> [1] "Type"
map_df(the_type_field[which(names(the_type_field) == "picklistValues")], as_tibble)
#> # 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 / Resell…
#> 5 true   false        Installation Partner       Installation Partner     
#> 6 true   false        Technology Partner         Technology Partner       
#> 7 true   false        Other                      Other

Where the Metadata API really shines is when it comes to CRUD operations on metadata. In this example we will create an object, add fields to it, then delete that object.

# create an object
base_obj_name <- "Custom_Account1"
custom_object <- list()
custom_object$fullName <- paste0(base_obj_name, "__c")
custom_object$label <- paste0(gsub("_", " ", base_obj_name))
custom_object$pluralLabel <- paste0(base_obj_name, "s")
custom_object$nameField <- list(displayFormat = 'AN-{0000}', 
                                label = paste0(base_obj_name, ' Number'), 
                                type = 'AutoNumber')
custom_object$deploymentStatus <- 'Deployed'
custom_object$sharingModel <- 'ReadWrite'
custom_object$enableActivities <- 'true'
custom_object$description <- paste0(base_obj_name, " created by the Metadata API")
custom_object_result <- sf_create_metadata(metadata_type = 'CustomObject',
                                           metadata = custom_object)
 
# add fields to the object
custom_fields <- tibble(fullName=c(paste0(base_obj_name, '__c.CustomField3__c'), 
                                   paste0(base_obj_name, '__c.CustomField4__c')), 
                        label=c('Test Field3', 'Test Field4'), 
                        length=c(100, 100), 
                        type=c('Text', 'Text'))
create_fields_result <- sf_create_metadata(metadata_type = 'CustomField', 
                                           metadata = custom_fields)
 
# delete the object
deleted_custom_object_result <- sf_delete_metadata(metadata_type = 'CustomObject', 
                                                   object_names = c('Custom_Account1__c'))

Future

Future APIs to support:

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 langauges (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.

Top


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.

News

salesforcer 0.1.2

Features

  • Add support for Bulk 1.0 operations of "create", "update", "upsert", "delete" and "hardDelete"
  • Bulk 2.0 operations, by default, now return a single tbl_df containing all of the successful records, error records, and unprocessed records
  • Created internal functions that explicity call each API for an operation. For example, sf_create() routes into sf_create_soap(), sf_create_rest(), and sf_bulk_operation().

salesforcer 0.1.1 release

Features

  • Add sf_search() with REST and SOAP API support for SOSL and free text search
  • Add sf_describe_objects() with REST and SOAP API to return object metadata
  • Add REST API support for upsert (sf_upsert())
  • Add SOAP API support for the following operations:
    • sf_create()
    • sf_update()
    • sf_delete()
    • sf_retrieve()
  • Add Metadata API support for the following operations:
    • sf_create_metadata()
    • sf_read_metadata()
    • sf_update_metadata()
    • sf_upsert_metadata()
    • sf_delete_metadata()
    • sf_describe_metadata()
    • sf_list_metadata()
    • sf_rename_metadata()
    • sf_retrieve_metdata()
    • sf_deploy_metdata()
  • Make the default file name for a cached token .httr-oauth-salesforcer so that it does not clash with other package token names

Bug Fixes

  • sf_user_info() returning argument is of length zero because token is not automatically refreshed before calling GET

  • sf_token() ignoring basic auth'ed sessions since it was only looking for a token using token_avaiable(). Replace with sf_auth_check() so now it considers a session or a token to be "available" (#1).


salesforcer 0.1.0 release

Features

  • OAuth 2.0 and Basic authentication methods (sf_auth())
  • Query operations via REST and Bulk APIs (sf_query())
  • CRUD operations (Create, Retrieve, Update, Delete) for REST and Bulk APIs:
    • sf_create()
    • sf_retrieve()
    • sf_update()
    • sf_upsert()
    • sf_delete()
  • Backwards compatibile versions of RForcecom package functions:
    • rforcecom.login()
    • rforcecom.getServerTimestamp()
    • rforcecom.query()
    • rforcecom.bulkQuery()
    • rforcecom.create()
    • rforcecom.update()
    • rforcecom.upsert()
    • rforcecom.delete()
  • Basic utility calls:
    • sf_user_info()
    • sf_server_timestamp()
    • sf_list_rest_api_versions()
    • sf_list_resources()
    • sf_list_api_limits()
    • sf_list_objects()

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.

install.packages("salesforcer")

0.1.2 by Steven M. Mortimer, a year ago


https://github.com/StevenMMortimer/salesforcer


Report a bug at https://github.com/StevenMMortimer/salesforcer/issues


Browse source code at https://github.com/cran/salesforcer


Authors: Steven M. Mortimer [aut, cre] , Takekatsu Hiramura [ctb] , Jennifer Bryan [ctb] , Joanna Zhao [ctb]


Documentation:   PDF Manual  


MIT + file LICENSE license


Imports methods, httr, dplyr, xml2, XML, jsonlite, purrr, readr, lubridate

Suggests knitr, testthat, rmarkdown, here, RForcecom


See at CRAN