Skip to content

Instantly share code, notes, and snippets.

@pcon
Created December 5, 2013 16:47
Show Gist options
  • Save pcon/7808940 to your computer and use it in GitHub Desktop.
Save pcon/7808940 to your computer and use it in GitHub Desktop.
/**
* Utility methods for use with the Object Snapshot object
*
* @author Patrick Connelly
*/
public with sharing class ObjectSnapshotUtils {
public static String JSON_DELIMITER = '[delimiter]';
public static Integer JSON_FIELD_SIZE = 32768 - (JSON_DELIMITER.length() * 2);
public static Integer JSON_FIELD_COUNT = 10;
public static final String JSON_FIELD_TEMPLATE = 'JSON_Data_{0}__c';
public static Boolean SPARSE_JSON = true;
public static final String SNAPSHOT_VERSION = '20120625';
public static final String SNAPSHOT_VERSION_LABEL = 'snapshot_version';
public static final String SNAPSHOT_SUCCESS_LABEL = 'snapshot_success';
public static final String SNAPSHOT_MESSAGE_LABEL = 'snapshot_message';
public static final Map<String, Object> SNAPSHOT_INFO = new Map<String, Object> {
SNAPSHOT_VERSION_LABEL => SNAPSHOT_VERSION,
SNAPSHOT_SUCCESS_LABEL => true,
SNAPSHOT_MESSAGE_LABEL => ''
};
public static final Map<String, Object> SNAPSHOT_INFO_JSON_TO_LARGE = new Map<String, Object> {
SNAPSHOT_VERSION_LABEL => SNAPSHOT_VERSION,
SNAPSHOT_SUCCESS_LABEL => false,
SNAPSHOT_MESSAGE_LABEL => GenericUtils.MSG_JSON_TO_LARGE
};
// These fields should never be added into the field map
public static final Map<String, Set<String>> FIELD_BLACKLIST_MAP = new Map<String, Set<String>>{
'Case' => new Set<String>{
'lastvieweddate',
'lastreferenceddate'
}
};
private static Map<String, Map<String, Object>> objectDescription = new Map<String, Map<String, Schema.sObjectField>>();
/**
* Gets a map of field name to their values
*
* NOTE: Before calling this method, make sure that objectDescription has been populated
* for the called object. getSnapshot handles this, but if you are going to call
* this directly, make sure it's populated.
*
* @param obj The sObject to build the map from
* @return A map of field name to value
*/
private static Map<String, Object> getMapOfAllFields(String objName, sObject obj) {
Map<String, Object> result = new Map<String, Object>();
Set<String> fieldNames = objectDescription.get(objName).keySet();
if (FIELD_BLACKLIST_MAP.containsKey(objName)) {
fieldNames.removeAll(FIELD_BLACKLIST_MAP.get(objName));
}
for (String fieldName: fieldNames) {
if (
!SPARSE_JSON || (
obj.get(fieldName) != null &&
String.valueOf(obj.get(fieldName)).trim() != ''
)
) {
result.put(fieldName, obj.get(fieldName));
}
}
return result;
}
/**
* Adds the delimiter to the start and end of the string
*
* NOTE: This is done because SFDC trims whitespace from the start/end of
* All fields
*
* @param data The string
* @return The appended data
*/
public static String appendDelimiter(String data) {
return JSON_DELIMITER + data + JSON_DELIMITER;
}
/**
* Converts a string of json data into an object snapshot
*
* @param jsonData The json data
* @return The object snapshot
*/
private static ObjectSnapshot__c jsonToSnapshot(Map<String, Object> fieldMap) {
ObjectSnapshot__c result = new ObjectSnapshot__c();
String jsonData = JSON.serialize(fieldMap);
// Figure out if we have enough room in our fields for all the json data
Integer numberOfFieldsRequired = (Integer)(Math.floor(jsonData.length() / JSON_FIELD_SIZE));
if (numberOfFieldsRequired >= JSON_FIELD_COUNT) {
fieldMap = SNAPSHOT_INFO_JSON_TO_LARGE;
jsonData = JSON.serialize(fieldMap);
}
for (Integer lowerBound = 0; lowerBound < jsonData.length(); lowerBound += JSON_FIELD_SIZE) {
Integer upperBound = ((lowerBound + JSON_FIELD_SIZE) > jsonData.length()) ? jsonData.length() : lowerBound + JSON_FIELD_SIZE;
Integer index = (Integer)(Math.floor(lowerBound / JSON_FIELD_SIZE));
String fieldName = String.format(JSON_FIELD_TEMPLATE, new List<String>{String.valueOf(index)});
String field = appendDelimiter(jsonData.subString(lowerBound, upperBound));
result.put(fieldName, field);
}
return result;
}
/**
* Removes the delimiter from the start and end of the string
*
* @param data The data
* @return The stipped down data
*/
public static String removeDelimiter(String data) {
if (data == null) {
return data;
}
return data.removeStart(JSON_DELIMITER).removeEnd(JSON_DELIMITER);
}
/**
* Converts a Object Snapshot object to a json string
*
* @param snapshot The snapshot to convert
* @return The json data
*/
public static String snapshotToJson(ObjectSnapshot__c snapshot) {
List<String> JSONDataList = new List<String>();
for (Integer i = 0; i < ObjectSnapshotUtils.JSON_FIELD_COUNT; i += 1) {
String fieldName = String.format(ObjectSnapshotUtils.JSON_FIELD_TEMPLATE, new List<String>{String.valueOf(i)});
String field = (String)(snapshot.get(fieldName));
field = removeDelimiter(field);
JSONDataList.add(field);
}
return String.join(JSONDataList, '');
}
/**
* Gets the snapshot object of an sObject
*
* @param obj The sObject to snapshot
* @return The snapshot
* @throws GenericUtils.InvalidException if the resultant json would be too long to store
*/
public static ObjectSnapshot__c getSnapshot(sObject obj) {
ObjectSnapshot__c result = new ObjectSnapshot__c();
Schema.DescribeSObjectResult describeResult = obj.getSobjectType().getDescribe();
String objName = describeResult.getName();
// Doing this to reduce the number of field queries we make so we don't hit the limit of 100
if (!objectDescription.containsKey(objName)) {
objectDescription.put(objName, describeResult.fields.getMap());
}
Map<String, Object> fieldMap = getMapOfAllFields(objName, obj);
fieldMap.putAll(SNAPSHOT_INFO);
result = jsonToSnapshot(fieldMap);
result.Object_Name__c = objName;
return result;
}
/**
* Creates and inserts the snapshots of a case
*
* @param cases A list of cases to snapshot
*/
public static void createSnapshots(List<Case> cases) {
List<ObjectSnapshot__c> snapshots = new List<ObjectSnapshot__c>();
for (Case newCase: cases) {
ObjectSnapshot__c snapshot = ObjectSnapshotUtils.getSnapshot(newCase);
snapshot.Case__c = newCase.Id;
snapshots.add(snapshot);
}
if (!snapshots.isEmpty()) {
insert snapshots;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment