R/utils-control.R

Defines functions return_matching_controls filter_valid_controls accepted_controls_by_operation accepted_controls_by_api sf_control

Documented in accepted_controls_by_api accepted_controls_by_operation filter_valid_controls return_matching_controls sf_control

#' Auxiliary for Controlling Calls to Salesforce APIs
#' 
#' Typically only used internally by functions when control parameters are passed 
#' through via dots (...), but it can be called directly to control the behavior 
#' of API calls. This function behaves exactly like \code{\link[stats]{glm.control}} 
#' for the \code{\link[stats]{glm}} function.
#' 
#' @importFrom purrr modify modify_if map
#' @param AllOrNoneHeader \code{list}; containing the \code{allOrNone} element with 
#' a value of \code{TRUE} or \code{FALSE}. This control specifies whether a call rolls back all changes 
#' unless all records are processed successfully. This control is available in 
#' SOAP, REST, and Metadata APIs for the following functions: \code{\link{sf_create}}, 
#' \code{\link{sf_delete}}, \code{\link{sf_update}}, \code{\link{sf_upsert}}, \code{\link{sf_create_metadata}}, 
#' \code{\link{sf_delete_metadata}}, \code{\link{sf_update_metadata}}, \code{\link{sf_upsert_metadata}}. 
#' For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_allornoneheader.htm}{here}.
#' @param AllowFieldTruncationHeader \code{list}; containing the \code{allowFieldTruncation} 
#' element with a value of \code{TRUE} or \code{FALSE}. This control specifies the truncation behavior 
#' for some field types in SOAP API version 15.0 and later for the following functions: 
#' \code{\link{sf_create}}, \code{\link{sf_update}}, \code{\link{sf_upsert}}. For 
#' more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_allowfieldtruncation.htm}{here}.
#' @param AssignmentRuleHeader \code{list}; containing the \code{useDefaultRule} 
#' element with a value of \code{TRUE} or \code{FALSE} or the \code{assignmentRuleId} element. 
#' This control specifies the assignment rule to use when creating or updating an 
#' Account, Case, or Lead for the following functions: \code{\link{sf_create}}, 
#' \code{\link{sf_update}}, \code{\link{sf_upsert}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_assignmentruleheader.htm}{here}.
#' @param DisableFeedTrackingHeader \code{list}; containing the \code{disableFeedTracking} 
#' element with a value of \code{TRUE} or \code{FALSE}. This control specifies whether 
#' the changes made in the current call are tracked in feeds for SOAP API calls made 
#' with the following functions: \code{\link{sf_create}}, \code{\link{sf_delete}}, 
#' \code{\link{sf_update}}, \code{\link{sf_upsert}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_disablefeedtracking.htm}{here}.
#' @param DuplicateRuleHeader \code{list}; containing the \code{allowSave}, 
#' \code{includeRecordDetails}, and \code{runAsCurrentUser} elements each with a 
#' value of \code{TRUE} or \code{FALSE}. This control specifies how duplicate rules should be applied 
#' when using the following functions: \code{\link{sf_create}}, \code{\link{sf_update}}, 
#' \code{\link{sf_upsert}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_duplicateruleheader.htm}{here}.
#' @param EmailHeader \code{list}; containing the \code{triggerAutoResponseEmail}, 
#' \code{triggerOtherEmail}, and \code{triggerUserEmail} elements each with a 
#' value of \code{TRUE} or \code{FALSE}. This control determines if an email notification should be sent 
#' when a request is processed by SOAP API calls made with the following functions: 
#' \code{\link{sf_create}}, \code{\link{sf_delete}}, \code{\link{sf_update}}, \code{\link{sf_upsert}}, 
#' \code{\link{sf_reset_password}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_emailheader.htm}{here}.
#' @param LocaleOptions \code{list}; containing the \code{language} element. This control 
#' specifies the language of the labels returned by the \code{\link{sf_describe_objects}} 
#' function using the SOAP API. The value must be a valid user locale (language and country), such as 
#' \code{de_DE} or \code{en_GB}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_localeheader.htm}{here}. 
#' The list of valid user locales is available 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_categorynodelocalization.htm#languagelocalekey_desc}{here}.
#' @param MruHeader \code{list}; containing the \code{updateMru} element with a value 
#' of \code{TRUE} or \code{FALSE}. This control indicates whether to update the list 
#' of most recently used items (\code{TRUE}) or not (\code{FALSE}) in the Recent Items 
#' section of the sidebar in the Salesforce user interface. This works for SOAP API calls 
#' made with the following functions: \code{\link{sf_create}}, \code{\link{sf_update}}, 
#' \code{\link{sf_upsert}}, \code{\link{sf_retrieve}}, \code{\link{sf_query}}. For more 
#' information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_mruheader.htm}{here}.
#' @param OwnerChangeOptions \code{list}; containing the \code{options} element. 
#' This control specifies the details of ownership of attachments and notes when a 
#' record’s owner is changed. This works for SOAP API calls made with the following functions: 
#' \code{\link{sf_update}}, \code{\link{sf_upsert}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_ownerchangeoptions.htm}{here}.
#' @param QueryOptions \code{list}; containing the \code{batchSize} element. 
#' This control specifies the batch size for query results . This works for SOAP or 
#' REST API calls made with the following functions: \code{\link{sf_query}}, 
#' \code{\link{sf_retrieve}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_queryoptions.htm}{here}.
#' @param UserTerritoryDeleteHeader \code{list}; containing the \code{transferToUserId} element. 
#' This control specifies a user to whom open opportunities are assigned when the current 
#' owner is removed from a territory. This works for the \code{\link{sf_delete}} function 
#' using the SOAP API. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_userterritorydeleteheader.htm}{here}.
#' @param BatchRetryHeader \code{list}; containing the \code{Sforce-Disable-Batch-Retry} element. 
#' When you create a bulk job, the Batch Retry control lets you disable retries 
#' for unfinished batches included in the job. This works for most operations run through 
#' the Bulk 1.0 API (e.g. \code{sf_create(., api_type = "Bulk 1.0")}) or creating 
#' a Bulk 1.0 job with \code{\link{sf_create_job_bulk}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/async_api_headers_disable_batch_retry.htm}{here}.
#' @param LineEndingHeader \code{list}; containing the \code{Sforce-Line-Ending} element. 
#' When you’re creating a bulk upload job, the Line Ending control lets you 
#' specify whether line endings are read as line feeds (LFs) or as carriage returns 
#' and line feeds (CRLFs) for fields of type Text Area and Text Area (Long). This 
#' works for most operations run through the Bulk APIs and/or creating a Bulk
#' job from scratch with \code{\link{sf_create_job_bulk}}. However, note that 
#' as of \code{readr v1.3.1} all CSV files end with the line feed character 
#' ("\\n") regardless of the operating system. So it is usually best to not specify 
#' this argument. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/async_api_headers_line_ending.htm}{here}.
#' @param PKChunkingHeader \code{list}; containing the \code{Sforce-Enable-PKChunking} element. 
#' Use the PK Chunking control to enable automatic primary key (PK) chunking 
#' for a bulk query job. This works for queries run through the Bulk 1.0 API either via 
#' \code{sf_query(., api_type = "Bulk 1.0")}) or \code{\link{sf_query_bulk}}. For 
#' more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/async_api_headers_enable_pk_chunking.htm}{here}.
#' @template api_type
#' @template operation
#' @examples 
#' \dontrun{
#' this_control <- sf_control(DuplicateRuleHeader=list(allowSave=TRUE, 
#'                                                     includeRecordDetails=FALSE, 
#'                                                     runAsCurrentUser=TRUE))
#' new_contact <- c(FirstName = "Test", LastName = "Contact-Create")
#' new_record <- sf_create(new_contact, "Contact", control = this_control)
#' 
#' # specifying the controls directly and are picked up by dots
#' new_record <- sf_create(new_contact, "Contact", 
#'                         DuplicateRuleHeader = list(allowSave=TRUE, 
#'                                                    includeRecordDetails=FALSE, 
#'                                                    runAsCurrentUser=TRUE))
#' }
#' @export
sf_control <- function(AllOrNoneHeader=list(allOrNone=FALSE), 
                       AllowFieldTruncationHeader=list(allowFieldTruncation=FALSE), 
                       AssignmentRuleHeader=list(useDefaultRule=TRUE),
                       DisableFeedTrackingHeader=list(disableFeedTracking=FALSE), 
                       DuplicateRuleHeader=list(allowSave=FALSE, 
                                                includeRecordDetails=FALSE, 
                                                runAsCurrentUser=TRUE), 
                       EmailHeader=list(triggerAutoResponseEmail=FALSE, 
                                        triggerOtherEmail=FALSE, 
                                        triggerUserEmail=TRUE),
                       LocaleOptions=list(language="en_US"),
                       MruHeader=list(updateMru=FALSE), 
                       OwnerChangeOptions=list(options=list(list(execute=TRUE, 
                                                                 type="EnforceNewOwnerHasReadAccess"),
                                                            list(execute=FALSE, 
                                                                 type="KeepAccountTeam"),
                                                            list(execute=FALSE, 
                                                                 type="KeepSalesTeam"),
                                                            list(execute=FALSE, 
                                                                 type="KeepSalesTeamGrantCurrentOwnerReadWriteAccess"),
                                                            list(execute=FALSE, 
                                                                 type="SendEmail"),
                                                            list(execute=FALSE, 
                                                                 type="TransferAllOwnedCases"),
                                                            list(execute=TRUE, 
                                                                 type="TransferContacts"),
                                                            list(execute=TRUE, 
                                                                 type="TransferContracts"),
                                                            list(execute=FALSE, 
                                                                 type="TransferNotesAndAttachments"),
                                                            list(execute=TRUE, 
                                                                 type="TransferOpenActivities"),
                                                            list(execute=TRUE, 
                                                                 type="TransferOrders"),
                                                            list(execute=FALSE, 
                                                                 type="TransferOtherOpenOpportunities"),
                                                            list(execute=FALSE, 
                                                                 type="TransferOwnedClosedOpportunities"),
                                                            list(execute=FALSE, 
                                                                 type="TransferOwnedOpenCases"), 
                                                            list(execute=FALSE, 
                                                                 type="TransferOwnedOpenOpportunities"))),
                       QueryOptions=list(batchSize=500),
                       UserTerritoryDeleteHeader=list(transferToUserId=NA), 
                       BatchRetryHeader=list(`Sforce-Disable-Batch-Retry`=FALSE), 
                       LineEndingHeader=list(`Sforce-Line-Ending`=NA), 
                       PKChunkingHeader=list(`Sforce-Enable-PKChunking`=FALSE), 
                       api_type = NULL,
                       operation = NULL){
  
  # determine which elements were supplied, dropping the function call itself
  supplied_arguments <-  as.list(match.call()[-1])
  
  # now eval them to no longer be objects of class "call"
  supplied_arguments <- supplied_arguments %>% 
    map(eval) %>% 
    # convert boolean args to lowercase
    modify(~modify_if(., is.logical, tolower))
  
  # check that they are all lists
  list_argument <- sapply(supplied_arguments, is.list)
  if(!all(list_argument)){
    if(!all(names(which(!list_argument)) %in% c("api_type", "operation"))){
      mismatched_warn_str <- c()
      for(n in names(which(!list_argument))){
        if(!(n %in% c("api_type", "operation"))){
          mismatched_warn_str <- c(mismatched_warn_str, n)
        }
      }
      mismatched_warn_str <- paste0(mismatched_warn_str, collapse=", ")  
      stop(
        sprintf(paste0("The following control arguments were not provided as lists: \n%s\n\n",
                       "Review the argument defaults in 'sf_control()' for help formatting."), 
                mismatched_warn_str)
        , call. = FALSE
      )
    }
  }
  
  # check that the controls valid for the API and operation
  supplied_arguments <- filter_valid_controls(supplied_arguments, 
                                              api_type = api_type, 
                                              operation = operation)
  
  return(supplied_arguments)
}

#' Return the Accepted Control Arguments by API Type
#' 
#' @template api_type
#' @return \code{character}; a vector of strings indicating which control arguments 
#' are accepted by the specified API.
#' @note This function is meant to be used internally. Only use when debugging.
#' @keywords internal
#' @export
accepted_controls_by_api <- function(api_type = c("SOAP", "REST", "Bulk 1.0", "Bulk 2.0", "Metadata")){
  this_api_type <- match.arg(api_type)
  switch(this_api_type, 
         "SOAP" = c("AllOrNoneHeader", "AllowFieldTruncationHeader", "AssignmentRuleHeader", 
                    "DisableFeedTrackingHeader", "DuplicateRuleHeader",
                    "EmailHeader", "LocaleOptions", 
                    "MruHeader", "OwnerChangeOptions", 
                    "QueryOptions", "UserTerritoryDeleteHeader"),
         "REST" = c("AllOrNoneHeader", "AssignmentRuleHeader", "QueryOptions"),
         "Bulk 1.0" = c("LineEndingHeader", "BatchRetryHeader", "PKChunkingHeader"),
         "Bulk 2.0" = c("LineEndingHeader"), 
         "Metadata" = c("AllOrNoneHeader"),
         character(0))
}

#' Return the Accepted Control Arguments by Operation
#' 
#' @template operation
#' @return \code{character}; a vector of strings indicating which control arguments 
#' are accepted by the specified operation.
#' @note This function is meant to be used internally. Only use when debugging.
#' @keywords internal
#' @export
accepted_controls_by_operation <- function(operation = c("create" , "insert",
                                                         "update", "upsert",
                                                         "delete", "undelete", "hardDelete",
                                                         "query", "queryall", "retrieve",
                                                         "convertLead", "merge",
                                                         "describeSObjects",
                                                         "resetPassword")){
  record_creation_controls <- c("AllOrNoneHeader", "AllowFieldTruncationHeader", 
                                "AssignmentRuleHeader", "DisableFeedTrackingHeader", 
                                "DuplicateRuleHeader", "EmailHeader", "MruHeader")
  query_controls <- c("MruHeader", "QueryOptions", "PKChunkingHeader")
  bulk_controls <- c("BatchRetryHeader", "LineEndingHeader")
  this_operation <- match.arg(operation)
  switch(this_operation, 
         "create" = c(record_creation_controls, bulk_controls),
         "insert" = c(record_creation_controls, bulk_controls),
         "update" = c(record_creation_controls, bulk_controls, "OwnerChangeOptions"),
         "upsert" = c(record_creation_controls, bulk_controls, "OwnerChangeOptions"),
         "delete" = c("AllOrNoneHeader", "DisableFeedTrackingHeader", "EmailHeader", 
                      "UserTerritoryDeleteHeader", bulk_controls),
         "undelete" = c("AllOrNoneHeader", "AllowFieldTruncationHeader"), 
         "hardDelete" = bulk_controls,         
         "query" = query_controls,
         "queryall" = query_controls, 
         "retrieve" = c("MruHeader"),
         "convertLead" = c("AllowFieldTruncationHeader", "DisableFeedTrackingHeader"),
         "merge" = c("AllowFieldTruncationHeader", "DisableFeedTrackingHeader"),         
         "describeSObjects" = c("LocaleOptions"),
         "resetPassword" = c("EmailHeader"),
         character(0))
}

#' Filter Out Control Arguments by API or Operation
#' 
#' @param supplied \code{list}; a list of input data regarding the control arguments 
#' along with the with API and operation information to make a complete assessment 
#' of which control arguments are applicable.
#' @template api_type
#' @template operation
#' @return \code{character}; a vector of strings returning only the control arguments 
#' that are accepted by the specified API and operation.
#' @note This function is meant to be used internally. Only use when debugging.
#' @keywords internal
#' @export
filter_valid_controls <- function(supplied, api_type = NULL, operation = NULL){
  
  if(is.null(api_type) & !is.null(supplied$api_type)){
    api_type <- supplied$api_type
  }
  # remove the api_type from the supplied args, if it exists
  supplied$api_type <- NULL
  
  if(is.null(operation) & !is.null(supplied$operation)){
    operation <- supplied$operation
  }
  # remove the api_type from the supplied args, if it exists
  supplied$operation <- NULL  
  
  if(!is.null(api_type) ){
    valid <- accepted_controls_by_api(api_type)
    # provide a warning before dropping 
    if(length(setdiff(names(supplied), valid)) >  0){
      warn_w_errors_listed(sprintf(paste0("Ignoring the following controls which ", 
                                          "are not used in the %s API: %s"),
                                   api_type, 
                                   paste0(setdiff(names(supplied), valid), collapse=", ")))
    }
    supplied <- supplied[intersect(names(supplied), valid)]
  }
  
  if(!is.null(operation)){
    valid <- accepted_controls_by_operation(operation)
    # provide a warning before dropping 
    if(length(setdiff(names(supplied), valid)) >  0){
      warn_w_errors_listed(sprintf(paste0("Ignoring the following controls which ", 
                                          "are not used in the %s operation: %s"),
                                   operation, 
                                   paste0(setdiff(names(supplied), valid), collapse=", ")))
    }
    supplied <- supplied[intersect(names(supplied), valid)]
  }
  
  return(supplied)
}

#' Of All Args Return Ones Matching Control Arguments
#' 
#' @param args \code{character}; a vector of strings that represent the function 
#' arguments.
#' @return \code{character}; a vector of strings returning only the function arguments 
#' that match control arguments so that users can specify them directly in each 
#' function and not have to construct a control object every time in order to 
#' pass only one or two control arguments.
#' @note This function is meant to be used internally. Only use when debugging.
#' @keywords internal
#' @export
return_matching_controls <- function(args){
  possible_controls <- formals(sf_control)  
  idx <- names(args) %in% names(possible_controls)
  return(args[idx])
}


# HELD BACK CONTROL OPTIONS BECAUSE THEY MAY BE CONFUSING OR NOT FEASIBLE GIVEN 
# THE WAY THE PACKAGE IS CURRENTLY WRITTEN:

#CallOptions=list(client=NA, defaultNamespace=NA)
#' CallOptions \code{list}; containing the \code{client} and \code{defaultNamespace}
#' elements. This control specifies the call options needed to work with a specific client. 
#' This control is only available for use with the Partner WSDL and works for most all functions 
#' in this package. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_calloptions.htm}{here}.

#LimitInfoHeader=list(current="20", 
#                     limit="250", 
#                     type="API REQUESTS")
#' LimitInfoHeader \code{list}; containing the \code{current}, \code{limit}, 
#' and \code{type} elements. This control determines if limit information for the organization should 
#' be returned in the header each request response. This works for most all functions, 
#' except \code{\link{sf_auth()}}. For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_limitinfo.htm}{here}.

#PackageVersionHeader=list(packageVersions=NA)
#' PackageVersionHeader \code{list}; containing the \code{packageVersions} element. 
#' This header specifies the package version for each installed managed package in 
#' API version 16.0 and later. This works for most all functions in this package. 
#' For more information, read the Salesforce documentation 
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_header_packageversionheader.htm}{here}.

#ContentTypeHeader=list(`Content-Type`="application/xml")
#' ContentTypeHeader \code{list}; containing the \code{Content-Type} element.
#' This header specifies the format for your request and response. Set the value of
#' this header to match the contentType of the job you’re working with. This works
#' for most all functions with the Bulk 1.0 API. For more information, read the Salesforce documentation
#' \href{https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/async_api_headers_content_type.htm}{here}.


# REST Translations
#  AllOrNoneHeader =  (allOrNone, currently in our batched data process)
#  AssignmentRuleHeader = Sforce-Auto-Assign
#  DuplicateRuleHeader = NONE!
#  QueryOptions = Sforce-Query-Options

# Bulk 1.0 Translations
#  AssignmentRuleHeader = assignmentRuleId (in the job info) (only if id?)
#  AllOrNoneHeader? = Unknown if SOAP works for Bulk 1.0 as well
#  DuplicateRuleHeader? = Unknown if SOAP works for Bulk 1.0 as well

# Bulk 2.0 Translations (does not use any headers)
# Just line ending?

Try the salesforcer package in your browser

Any scripts or data that you put into this service are public.

salesforcer documentation built on March 18, 2022, 6:26 p.m.