Last active
August 1, 2018 16:19
-
-
Save ckoppelman/252275b8a4f5add0549e27b4b901803c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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