Skip to content

Instantly share code, notes, and snippets.

@ckoppelman
Last active August 1, 2018 16:19
Show Gist options
  • Save ckoppelman/252275b8a4f5add0549e27b4b901803c to your computer and use it in GitHub Desktop.
Save ckoppelman/252275b8a4f5add0549e27b4b901803c to your computer and use it in GitHub Desktop.
public class SchemaUtils {
static final Set<String> UNCHANGEABLE_FIELDS = new Set<String>{
'CreatedById', 'CreatedDate',
'LastModifiedById', 'LastModifiedDate',
'SystemModstamp', 'IsDeleted'
};
static final String TOOLING_API_QUERY_FORMAT = (
' SELECT Id, Metadata ' +
' FROM FieldDefinition ' +
' WHERE EntityDefinition.QualifiedApiName=\'{0}\' ' +
' AND QualifiedApiName=\'{1}\' '
// A single quote itself must be represented by doubled single quotes '' throughout a String
// (see https://docs.oracle.com/javase/7/docs/api/java/text/MessageFormat.html)
).replace('\'','\'\'');
static Map<Schema.DescribeFieldResult, Boolean> IS_PROBABLY_UPDATEABLE =
new Map<Schema.DescribeFieldResult, Boolean>();
/**
* Returns whether the field is updateable by the system.
* There are going to be false positives when it comes to weird system-level SObjects
* like ApexClass or ReportType, so check for System.DmlException
* with the text "INVALID_FIELD_FOR_INSERT_UPDATE"
*/
public Boolean isProbablySystemUpdateable(Schema.DescribeSObjectResult sobjDesc,
Schema.DescribeFieldResult fldDesc) {
if (!IS_PROBABLY_UPDATEABLE.containsKey(fldDesc)) {
IS_PROBABLY_UPDATEABLE.put(fldDesc,
getIsProbablySystemUpdateable(sobjDesc, fldDesc));
}
return IS_PROBABLY_UPDATEABLE.get(fldDesc);
}
Boolean getIsProbablySystemUpdateable(Schema.DescribeSObjectResult sobjDesc,
Schema.DescribeFieldResult fldDesc) {
if (fldDesc.isCalculated() || fldDesc.isAutoNumber() ||
fldDesc.getType() == Schema.DisplayType.Id) {
return false;
} else if (!fldDesc.isCustom() && UNCHANGEABLE_FIELDS.contains(fldDesc.getName())) {
return false;
} else if (sobjDesc.getName().endsWith('__mdt') ||
sobjDesc.getName().endsWith('History') ||
sobjDesc.getName().endsWith('Share')) {
return false;
} else if (fldDesc.getType() == Schema.DisplayType.Reference) {
Map<String, Object> metadata = getToolingApiMetadata(sobjDesc, fldDesc);
String fieldType = (String)metadata.get('type');
if (fieldType == 'MasterDetail') {
Boolean reparentableMasterDetail = Boolean.valueOf(
metadata.get('reparentableMasterDetail')
);
return reparentableMasterDetail;
} else {
return true;
}
}
return true;
}
Map<String, Object> getToolingApiMetadata(Schema.DescribeSObjectResult sobjDesc,
Schema.DescribeFieldResult fldDesc) {
Url endpoint = new Url(Url.getSalesforceBaseUrl(), '/services/data/v43.0/tooling/query');
String query = String.format(
TOOLING_API_QUERY_FORMAT,
new List<String>{
sobjDesc.getName(),
fldDesc.getName()
}
);
String endpointUrl = setQueryString(endpoint, query);
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
req.setEndpoint(endpointUrl);
req.setMethod('GET');
String body = new Http().send(req).getBody();
Map<String, Object> objData = (Map<String, Object>)JSON.deserializeUntyped(body);
Map<String, Object> record = (Map<String, Object>)((List<Object>)objData.get('records')).get(0);
return (Map<String, Object>)record.get('Metadata');
}
String setQueryString(Url path, String queryString) {
return path.toExternalForm() + '?q=' + EncodingUtil.urlEncode(queryString, 'UTF-8');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment