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 0033s000012MKV9AAO true
# replicated in salesforcer package
result2 <- salesforcer::rforcecom.create(session, objectName=object, fields)
result2
#> id success
#> 1 0033s000012MKVEAA4 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 0033s000012MKVJAA4 true
#> 2 0033s000012MKVOAA4 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 0033s000012MKVTAA4 TRUE
#> 2 0033s000012MKVUAA4 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 0033s000012MKTNAA4
#> 2 0033s000012MGJZAA4
#> 3 0033s000012MGcXAAW
#> 4 0033s000012MG7oAAG
#> 5 0033s000012MIlnAAG
# replicated in salesforcer package
result2 <- salesforcer::rforcecom.query(session, soqlQuery = this_soql)
result2
#> # A tibble: 5 x 2
#> Id Email
#> <chr> <lgl>
#> 1 0033s000012MKTNAA4 NA
#> 2 0033s000012MGJZAA4 NA
#> 3 0033s000012MGcXAAW NA
#> 4 0033s000012MG7oAAG NA
#> 5 0033s000012MIlnAAG NA
# the better way in salesforcer to query
salesforcer_results <- sf_query(this_soql)
salesforcer_results
#> # A tibble: 5 x 2
#> Id Email
#> <chr> <lgl>
#> 1 0033s000012MKTNAA4 NA
#> 2 0033s000012MGJZAA4 NA
#> 3 0033s000012MGcXAAW NA
#> 4 0033s000012MG7oAAG NA
#> 5 0033s000012MIlnAAG NA
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
#> 6 true false false 18 false false
#> 7 true false false 765 false false
#> 8 true false false 120 false false
#> 9 true false false 240 false false
#> 10 true false false 60 false false
#> # … with 58 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.