While writing the {salesforcer} package we were keenly aware that many folks are already using the RForcecom package to connect to Salesforce. In order to foster adoption and switching between the packages {salesforcer} replicates the functionality of many RForcecom functions so that you will only need to swap out library(RForcecom)
for library(salesforcer)
and still have your production tested scripts perform as usual.
{salesforcer} supports OAuth 2.0 authentication which is preferred, but for backward compatibility provides the username-password authentication routine implemented by RForcecom. Here is an example running the function from each of the packages side-by-side and producing the same result.
# the RForcecom way
session1 <- RForcecom::rforcecom.login(username, paste0(password, security_token),
apiVersion=getOption("salesforcer.api_version"))
session1['sessionID'] <- "{MASKED}"
session1
#> sessionID instanceURL
#> "{MASKED}" "https://na122.salesforce.com/"
#> apiVersion
#> "48.0"
# replicated in salesforcer package
session2 <- salesforcer::rforcecom.login(username,
paste0(password, security_token),
apiVersion = getOption("salesforcer.api_version"))
session2['sessionID'] <- "{MASKED}"
session2
#> sessionID instanceURL
#> "{MASKED}" "https://na122.salesforce.com/"
#> apiVersion
#> "48.0"
Note that we must set the API version here because calls to session will not create a new sessionId and then we are stuck with version 35.0 (the default from RForcecom::rforcecom.login). Some functions in {salesforcer} implement API calls that are only available after version 35.0.
“CRUD” operations (Create, Retrieve, Update, Delete) in the RForcecom package only operate on one record at a time. One benefit to using the {salesforcer} package is that these operations will accept a named vector (one record) or an entire data.frame
or tbl_df
of records to churn through. However, rest assured that the replicated functions behave exactly the same way if you are hesitant to making the switch.
object <- "Contact"
fields <- c(FirstName="Test", LastName="Contact-Create-Compatibility")
# the RForcecom way
result1 <- RForcecom::rforcecom.create(session, objectName=object, fields)
result1
#> id success
#> 1 0033s000013YBr0AAG true
# replicated in salesforcer package
result2 <- salesforcer::rforcecom.create(session, objectName=object, fields)
result2
#> id success
#> 1 0033s000013YBrEAAW TRUE
Here is an example showing the reduction in code of using {salesforcer} if you would like to create multiple records.
n <- 2
new_contacts <- tibble(FirstName = rep("Test", n),
LastName = paste0("Contact-Create-", 1:n))
# the RForcecom way
rforcecom_results <- NULL
for(i in 1:nrow(new_contacts)){
temp <- RForcecom::rforcecom.create(session,
objectName = "Contact",
fields = unlist(slice(new_contacts,i)))
rforcecom_results <- bind_rows(rforcecom_results, temp)
}
rforcecom_results
#> id success
#> 1 0033s000013YBrJAAW true
#> 2 0033s000013YBrOAAW true
# the better way in salesforcer to do multiple records
salesforcer_results <- sf_create(new_contacts, object_name="Contact")
salesforcer_results
#> # A tibble: 2 x 2
#> id success
#> <chr> <lgl>
#> 1 0033s000013YBZwAAO TRUE
#> 2 0033s000013YBZxAAO TRUE
{salesforcer} also has better printing and type-casting when returning query result thanks to features of the readr package.
this_soql <- "SELECT Id, Email FROM Contact LIMIT 5"
# the RForcecom way
result1 <- RForcecom::rforcecom.query(session, soqlQuery = this_soql)
result1
#> Id
#> 1 0033s000013YAdhAAG
#> 2 0033s000013YB7iAAG
#> 3 0033s000013YAnLAAW
#> 4 0033s000013YAnMAAW
#> 5 0033s000013YBFbAAO
# replicated in salesforcer package
result2 <- salesforcer::rforcecom.query(session, soqlQuery = this_soql)
result2
#> # A tibble: 5 x 1
#> Id
#> <chr>
#> 1 0033s000013YAdhAAG
#> 2 0033s000013YB7iAAG
#> 3 0033s000013YAnLAAW
#> 4 0033s000013YAnMAAW
#> 5 0033s000013YBFbAAO
# the better way in salesforcer to query
salesforcer_results <- sf_query(this_soql)
salesforcer_results
#> # A tibble: 5 x 1
#> Id
#> <chr>
#> 1 0033s000013YAdhAAG
#> 2 0033s000013YB7iAAG
#> 3 0033s000013YAnLAAW
#> 4 0033s000013YAnMAAW
#> 5 0033s000013YBFbAAO
The RForcecom package has the function rforcecom.getObjectDescription()
which returns a data.frame
with one row per field on an object. The same function in {salesforcer} is named sf_describe_object_fields()
and also has better printing and datatype casting by using tibbles.
# the RForcecom way
result1 <- RForcecom::rforcecom.getObjectDescription(session, objectName='Account')
# backwards compatible in the salesforcer package
result2 <- salesforcer::rforcecom.getObjectDescription(session, objectName='Account')
# the better way in salesforcer to get object fields
result3 <- salesforcer::sf_describe_object_fields('Account')
result3
#> # A tibble: 69 x 39
#> aggregatable aiPredictionFie… autoNumber byteLength calculated caseSensitive
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 true false false 18 false false
#> 2 false false false 0 false false
#> 3 true false false 18 false false
#> 4 true false false 765 false false
#> 5 true false false 120 false false
#> # … with 64 more rows, and 33 more variables: compoundFieldName <chr>,
#> # createable <chr>, custom <chr>, defaultValue <list>,
#> # defaultedOnCreate <chr>, deprecatedAndHidden <chr>, digits <chr>,
#> # externalId <chr>, extraTypeInfo <chr>, filterable <chr>, groupable <chr>,
#> # idLookup <chr>, label <chr>, length <chr>, name <chr>, nameField <chr>,
#> # namePointing <chr>, nillable <chr>, permissionable <chr>,
#> # picklistValues <list>, polymorphicForeignKey <chr>, precision <chr>,
#> # queryByDistance <chr>, referenceTo <chr>, relationshipName <chr>,
#> # restrictedPicklist <chr>, scale <chr>, searchPrefilterable <chr>,
#> # soapType <chr>, sortable <chr>, type <chr>, unique <chr>, updateable <chr>
In the future more features will be migrated from RForcecom to make the transition as seamless as possible.