Salesforce is a very flexible platform in that it provides the Metadata API for users to create, read, update and delete their entire Salesforce environment from objects to 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.). First, load the {salesforcer}, {dplyr}, and {purrr} packages and login, if needed.
library(dplyr, warn.conflicts = FALSE)
library(purrr)
library(salesforcer)
sf_auth()
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.
<- sf_read_metadata(metadata_type = "CustomObject",
read_obj_result object_names = c("Account", "Contact"))
1]][c("fullName", "label", "sharingModel", "enableHistory")]
read_obj_result[[#> $fullName
#> [1] "Account"
#>
#> $label
#> [1] "Account"
#>
#> $sharingModel
#> [1] "ReadWrite"
#>
#> $enableHistory
#> [1] "false"
<- head(which(names(read_obj_result[[1]]) == "fields"), 2)
first_two_fields_idx
# show the first two returned fields of the Account object
1]][first_two_fields_idx]
read_obj_result[[#> $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
sf_describe_object_fields()
. Here is an example using sf_describe_object_fields()
where we get a tbl_df
with one row for each field on the Account object:
<- sf_describe_object_fields("Account")
acct_fields %>% select(name, label, length, soapType, type)
acct_fields #> # A tibble: 68 × 5
#> name label length soapType type
#> <chr> <chr> <chr> <chr> <chr>
#> 1 Id Account ID 18 tns:ID id
#> 2 IsDeleted Deleted 0 xsd:boolean boolean
#> 3 MasterRecordId Master Record ID 18 tns:ID reference
#> 4 Name Account Name 255 xsd:string string
#> 5 Type Account Type 255 xsd:string picklist
#> # … with 63 more rows
# show the picklist selection options for the Account Type field
%>%
acct_fields filter(label == "Account Type") %>%
$picklistValues
.#> [[1]]
#> # A tibble: 7 × 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 / Reseller
#> 5 true false Installation Partner Installation Partner
#> # … with 2 more rows
If you prefer to be more precise about collecting and formatting the field data you can work directly with the nested lists that the APIs return. In this example we look at the picklist values of fields on the Account object.
<- sf_describe_objects(object_names=c('Account', 'Contact'))
describe_obj_result
# confirm that the Account object is queryable
1]][c('label', 'queryable')]
describe_obj_result[[#> $label
#> [1] "Account"
#>
#> $queryable
#> [1] "true"
# now show the different picklist values for the Account Type field
<- describe_obj_result[[1]][names(describe_obj_result[[1]]) == "fields"]
all_fields
<- which(sapply(all_fields,
acct_type_field_idx FUN=function(x){x$label}) == "Account Type")
<- all_fields[[acct_type_field_idx]]
acct_type_field which(names(acct_type_field) == "picklistValues")] %>%
acct_type_field[map_df(as_tibble)
#> # A tibble: 7 × 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 / Reseller
#> 5 true false Installation Partner Installation Partner
#> # … with 2 more rows
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.
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
<- "Custom_Account1"
base_obj_name <- 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}',
custom_objectlabel = paste0(base_obj_name, ' Number'),
type = 'AutoNumber')
$deploymentStatus <- 'Deployed'
custom_object$sharingModel <- 'ReadWrite'
custom_object$enableActivities <- 'true'
custom_object$description <- paste0(base_obj_name, " created by the Metadata API")
custom_object<- sf_create_metadata(metadata_type = 'CustomObject',
custom_object_result metadata = custom_object)
# add fields to the object
<- tibble(fullName=c(paste0(base_obj_name, '__c.CustomField3__c'),
custom_fields paste0(base_obj_name, '__c.CustomField4__c')),
label=c('Test Field3', 'Test Field4'),
length=c(100, 100),
type=c('Text', 'Text'))
<- sf_create_metadata(metadata_type = 'CustomField',
create_fields_result metadata = custom_fields)
# delete the object
<- sf_delete_metadata(metadata_type = 'CustomObject',
delete_obj_result object_names = c('Custom_Account1__c'))
Note that newly created custom fields are not editable by default, meaning that you will not be able to insert records into them until updating the field level security of your user profile. Run the following code to determine the user profiles in your org and updating the field permissions on an object that you may have created with the example code above.
# get list of user proviles in order to get the "fullName"
# parameter correct in the next call
<- list(list(type = 'Profile'))
my_queries <- sf_list_metadata(queries = my_queries)
profiles_list
<- list(fullName = 'Admin',
admin_editable fieldPermissions=list(field=paste0(custom_object$fullName,
'.CustomField3__c'),
editable='true'),
fieldPermissions=list(field=paste0(custom_object$fullName,
'.CustomField4__c'),
editable='true'))
# update the field level security to "editable" for your fields
<- sf_update_metadata(metadata_type = 'Profile',
prof_update metadata = admin_editable)
# now try inserting values into that custom object's fields
= tibble(CustomField3__c = "Hello World", CustomField4__c = "Hello World")
my_new_data <- sf_create(my_new_data, object_name = custom_object$fullName) added_record