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