SandboxPostCopy - Script to be executed after sandbox Create or Refresh

a new SandboxPostCopy interface. Implementing the SandboxPostCopy interface will allow a class to run after a sandbox is created or refreshed.  This feature comes in very handy when trying to make your sandbox environment development ready. Often times this process involves removing sensitive data from fields on some objects, invalidating customer emails and other data manipulations. This is done to enforce data standard policies and preventing any email automations from reaching the customers during the development phases.
In the past the processes outlined above were done manually using data loaders or partially automated with batch jobs. In this post we are going to explore how we can use the SandboxPostCopy interface to help automate invalidating all email fields in the system to prevent any email automations from accidentally reaching your customers.


global class InvalidateEmails implements SandboxPostCopy {
 global void runApexClass(SandboxContext context) {
  System.debug(context.organizationId());
  System.debug(context.sandboxId());
  System.debug(context.sandboxName());

  run();
 }

 static Map < String, Map < String, Object >> objectConditionals = new Map < String, Map < String, Object >> {
  'Lead' => new Map < String,
  Object > {
   'isConverted' => false
  }
 };

 global static void run() {
  Map < String, List < String >> soEmailFieldMap = findEmaiLFields();
  processEmailFields(soEmailFieldMap);
 }

 private static Map < String, List < String >> findEmaiLFields() {
  Map < String, List < String >> soEmailFieldMap = new Map < String, List < String >> ();
  // Iterate over all SObjects
  for (SObjectType soType: Schema.getGlobalDescribe().values()) {
   DescribeSObjectResult soDescribe = soType.getDescribe();
   if (!soDescribe.isQueryable() || !soDescribe.isUpdateable()) continue;
   String objectTypeName = soDescribe.getName();
   // Iterate over all fields found on a given SObject
   for (SObjectField soField: soDescribe.fields.getMap().values()) {
    DescribeFieldResult field = soField.getDescribe();
    // Skip non Email type fields
    if (field.getType() != Schema.DisplayType.EMAIL) continue;
    // Skip emails that cannot be filtered on
    if (!field.isFilterable()) continue;
    // Collect all Email fields found
    if (soEmailFieldMap.containsKey(objectTypeName)) {
     soEmailFieldMap.get(objectTypeName).add(field.getName());
    } else {
     soEmailFieldMap.put(objectTypeName, new List < String > {
      field.getName()
     });
    }
   }
  }
  return soEmailFieldMap;
 }

 private static void processEmailFields(Map < String, List < String >> soEmailFieldMap) {
  // Iterate over the SObject to Email Fields collection
  for (String objectName: soEmailFieldMap.keySet()) {
   // Get any specified conditionals
   Map < String, Object > conditionals = new Map < String, Object > ();
   if (objectConditionals.containsKey(objectName)) conditionals = objectConditionals.get(objectName);
   // Build a list of all fields that need to be queried
   List < String > emailFields = soEmailFieldMap.get(objectName);
   List < String > fieldList = new List < String > ();
   fieldList.addAll(emaiLFields);
   fieldList.addAll(conditionals.keySet());
   // Generate a SOQL query to get records with non null emails
   String soql = getSOQL(objectName, fieldList);
   System.debug(soql);
   List < SObject > records = new List < SObject > ();
   // Iterate over queried SObject records
   for (SObject record: Database.query(soql)) {
    // Skip records that do not match specified conditons
    if (!checkConditions(record, conditionals)) continue;
    // Iterate over Email fields found on SObject and invalidate values
    for (String field: emailFields) {
     String email = (String) record.get(field);
     if (String.isEmpty(email)) continue;
     record.put(field, email.replaceAll('\\W', '_') + '@disabled.diabled');
    }
    records.add(record);
   }
   System.debug(JSON.serializePretty(records));
   update records;
  }
 }

 private static boolean checkConditions(SObject record, Map < String, Object > conditionals) {
  for (String field: conditionals.keySet()) {
   Object value = record.get(field);
   Object condition = conditionals.get(field);
   if (value != condition) return false;
  }
  return true;
 }

 global static String getSOQL(String objectTypeName, List < String > fieldList) {
  List < String > conditionals = new List < String > ();
  for (String field: fieldList) conditionals.add(field + ' != null');
  String soql = 'SELECT {!fieldList} FROM {!objectTypeName} WHERE {!conditionals}';
  return soql
   .replace('{!fieldList}', String.join(fieldList, ','))
   .replace('{!objectTypeName}', objectTypeName)
   .replace('{!conditionals}', String.join(conditionals, ' OR '));
 }
}

Post a Comment

0 Comments