vignettes/transitioning-from-RForcecom.Rmd
transitioning-from-RForcecom.RmdWhile 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 0033s000014B3ZeAAK true # replicated in salesforcer package result2 <- salesforcer::rforcecom.create(session, objectName=object, fields) result2 #> id success #> 1 0033s000014B3ZjAAK 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 0033s000014B3ZoAAK true #> 2 0033s000014B3ZtAAK 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 0033s000014B2YiAAK TRUE #> 2 0033s000014B2YjAAK 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 0033s000013wlpjAAA #> 2 0033s000013wlpkAAA #> 3 0033s000014AG4HAAW #> 4 0033s0000149ZoCAAU #> 5 0033s0000149ZoDAAU # replicated in salesforcer package result2 <- salesforcer::rforcecom.query(session, soqlQuery = this_soql) result2 #> # A tibble: 5 x 1 #> Id #> <chr> #> 1 0033s000013wlpjAAA #> 2 0033s000013wlpkAAA #> 3 0033s000014AG4HAAW #> 4 0033s0000149ZoCAAU #> 5 0033s0000149ZoDAAU # the better way in salesforcer to query salesforcer_results <- sf_query(this_soql) salesforcer_results #> # A tibble: 5 x 1 #> Id #> <chr> #> 1 0033s000013wlpjAAA #> 2 0033s000013wlpkAAA #> 3 0033s000014AG4HAAW #> 4 0033s0000149ZoCAAU #> 5 0033s0000149ZoDAAU
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: 68 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 63 more rows, and 33 more variables: compoundFieldName <chr>, #> # createable <chr>, custom <chr>, defaultedOnCreate <chr>, #> # defaultValue <list>, 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.