---
title: "Getting Started"
author: "Steven M. Mortimer"
date: "2020-08-16"
output:
rmarkdown::html_vignette:
toc: true
toc_depth: 4
keep_md: true
vignette: >
%\VignetteIndexEntry{Getting Started}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, echo = FALSE}
NOT_CRAN <- identical(tolower(Sys.getenv("NOT_CRAN")), "true")
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
purl = NOT_CRAN,
eval = NOT_CRAN
)
options(tibble.print_min = 5L, tibble.print_max = 5L)
```
## Authentication
First, load the {salesforcer} package and log in. There are two ways to
authenticate:
1. OAuth 2.0
2. ~~Basic Username-Password~~
**NOTE**: Beginning February 1st, 2022, Salesforce will be requiring customers
to enable multi-factor authentication, which means that basic authentication
using password and security token will no longer work. The function `sf_auth()`
will return the error message:
```sh
INVALID_LOGIN: Invalid username, password, security token; or user locked out.
```
Also, it is a best practice to use OAuth 2.0 so that passwords do not have to be
embedded within scripts. OAuth 2.0 credentials are stored locally in a cached
file titled `.httr-oauth-salesforcer` in the current working directory. The
package will automatically refresh this cached token when it expires so you will
not have to call `sf_auth()` during every R session if the
`.httr-oauth-salesforcer` file is present in the working directory.
```{r auth, include = FALSE}
suppressWarnings(suppressMessages(library(dplyr)))
suppressWarnings(suppressMessages(library(here)))
library(salesforcer)
token_path <- Sys.getenv("SALESFORCER_TOKEN_PATH")
sf_auth(token = paste0(token_path, "salesforcer_token.rds"))
```
```{r load-package, eval=FALSE}
library(dplyr, warn.conflicts = FALSE)
library(salesforcer)
sf_auth()
```
**Setting up your own Connected App for OAuth**
Just a note, that it's not necessary to setup your own Connected App in Salesforce
to use OAuth 2.0 authentication. The only difference is that the authentication
will be run through the client created and associated with the {salesforcer}
package. By using the package client, you will **NOT** be giving access to Salesforce
to anyone, the package is just the medium for you to connect to your own data.
If you wanted more control you would specify those options like so:
```{r other-params1, eval=FALSE}
options(salesforcer.consumer_key = "012345678901-99thisisatest99connected33app22key")
options(salesforcer.consumer_secret = "Th1s1sMyConsumerS3cr3t")
sf_auth()
```
**Using a proxy connection**
If you are required to connect to Salesforce via proxy you are able to specify
all of those parameters as options, as well. For each call via **httr** these
proxy settings will be passed along with the Salesforce authentication.
```{r other-params2, eval=FALSE}
options(salesforcer.proxy_url = "64.251.21.73") # IP or a named domain
options(salesforcer.proxy_port = 8080)
options(salesforcer.proxy_username = "user")
options(salesforcer.proxy_password = "pass")
options(salesforcer.proxy_auth = "ntlm")
sf_auth()
```
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!
```{r}
# 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)
sprintf("User Id: %s", user_info$userId)
```
## Creating records
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.
```{r}
n <- 2
new_contacts <- tibble(FirstName = rep("Test", n),
LastName = paste0("Contact-Create-", 1:n))
created_records <- sf_create(new_contacts, "Contact")
created_records
```
## Retrieving records
Retrieve pulls down a specific set of records and fields. It's very similar to
running a query, but doesn't use SOQL. Here is an example where we retrieve the
data we just created.
```{r}
retrieved_records <- sf_retrieve(ids=created_records$id,
fields=c("FirstName", "LastName"),
object_name="Contact")
retrieved_records
```
## Querying records
Salesforce has proprietary form of SQL called SOQL (Salesforce Object Query
Language). SOQL is a powerful tool that allows you to return the fields of
records in 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.
```{r query-records}
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
```
**NOTE**: In the example above, you'll notice that the `"Account.Name"` column
does not appear in the results. This is because the SOAP and REST APIs only
return an empty Account object for the record if there is no relationship to an
account ( see #78). 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.
## Updating records
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".
```{r update-records}
# Update some of those records
queried_records <- queried_records %>%
mutate(FirstName = "TestTest")
updated_records <- sf_update(queried_records, object_name="Contact")
updated_records
```
## Deleting records
You can also delete records in Salesforce. The method implements a "soft" delete
meaning that the deleted records go to the Recycle Bin which can be emptied or
queried against later in the event that the record needed.
```{r}
deleted_records <- sf_delete(updated_records$id)
deleted_records
```
## Upserting records
Finally, Salesforce has a unique method called "upsert" that allows you to
create and/or update records at the same time. More specifically, if the record
is not found based an an "External Id" field, then Salesforce will create the
record instead of updating one. Below is an example where we create 2 records,
then upsert 3, where 2 are matched and updated and one is created. **NOTE**: You
will need to create a custom field on the target object and ensure it is labeled as
an "External Id" field.
```{r}
n <- 2
new_contacts <- tibble(FirstName = rep("Test", n),
LastName = paste0("Contact-Create-", 1:n),
My_External_Id__c=letters[1:n])
created_records <- sf_create(new_contacts, "Contact")
upserted_contacts <- tibble(FirstName = rep("Test", n),
LastName = paste0("Contact-Upsert-", 1:n),
My_External_Id__c=letters[1:n])
new_record <- tibble(FirstName = "Test",
LastName = paste0("Contact-Upsert-", n+1),
My_External_Id__c=letters[n+1])
upserted_contacts <- bind_rows(upserted_contacts, new_record)
upserted_records <- sf_upsert(input_data=upserted_contacts,
object_name="Contact",
external_id_fieldname="My_External_Id__c")
upserted_records
```
```{r, include = FALSE}
deleted_records <- sf_delete(upserted_records$id, object_name = "Contact")
```
## Check out the Tests
The {salesforcer} package has quite a bit of unit test coverage to track any
changes made between newly released versions of the Salesforce API (typically 4
each year). These tests are an excellent source of examples because they cover
most all cases of utilizing the package functions. You can access them here: https://github.com/StevenMMortimer/salesforcer/tree/main/tests/testthat/