Add class fflib_MockSOQLResult
Signed-off-by: WimVelzeboer <wimvelzeboer@protonmail.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* File Name: fflib_MockSOQLResult
|
||||||
|
*/
|
||||||
|
public with sharing class fflib_MockSOQLResult
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method works on the principle that serializing and deserialising child records is supported
|
||||||
|
*
|
||||||
|
* System.assertEquals(1, ((List<Master__c>)
|
||||||
|
* JSON.deserialize(
|
||||||
|
* JSON.serialize(
|
||||||
|
* [select Id, Name,
|
||||||
|
* (select Id, Name from Children__r) from Master__c]), List<Master__c>.class))
|
||||||
|
* [0].Children__r.size());
|
||||||
|
*
|
||||||
|
* This method results internally in constructing this JSON, before deserialising it back into SObject's
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* {
|
||||||
|
* "attributes": {
|
||||||
|
* "type": "Master__c",
|
||||||
|
* "url": "/services/data/v32.0/sobjects/Master__c/a0YG0000005Jn5uMAC"
|
||||||
|
* },
|
||||||
|
* "Name": "Fred",
|
||||||
|
* "Id": "a0YG0000005Jn5uMAC",
|
||||||
|
* "Children__r": {
|
||||||
|
* "totalSize": 1,
|
||||||
|
* "done": true,
|
||||||
|
* "records": [
|
||||||
|
* {
|
||||||
|
* "attributes": {
|
||||||
|
* "type": "Child__c",
|
||||||
|
* "url": "/services/data/v32.0/sobjects/Child__c/a0ZG0000006JGPAMA4"
|
||||||
|
* },
|
||||||
|
* "Name": "Bob",
|
||||||
|
* "Id": "a0ZG0000006JGPAMA4",
|
||||||
|
* "Master__c": "a0YG0000005Jn5uMAC"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
public static Object makeRelationship(Type parentsType, List<SObject> parents, SObjectField relationshipField, List<List<SObject>> children) {
|
||||||
|
|
||||||
|
// Find out more about this relationship...
|
||||||
|
String relationshipFieldName = relationshipField.getDescribe().getName();
|
||||||
|
DescribeSObjectResult parentDescribe = parents.getSObjectType().getDescribe();
|
||||||
|
List<Schema.ChildRelationship> childRelationships = parentDescribe.getChildRelationships();
|
||||||
|
String relationshipName = null;
|
||||||
|
for(Schema.ChildRelationship childRelationship : childRelationships) {
|
||||||
|
if(childRelationship.getField() == relationshipField) {
|
||||||
|
relationshipName = childRelationship.getRelationshipName();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream the parsed JSON representation of the parent objects back out, injecting children as it goes
|
||||||
|
JSONParser parentsParser = JSON.createParser(JSON.serialize(parents));
|
||||||
|
JSONParser childrenParser = JSON.createParser(JSON.serialize(children));
|
||||||
|
JSONGenerator combinedOutput = JSON.createGenerator(false);
|
||||||
|
streamTokens(parentsParser, combinedOutput, new InjectChildrenEventHandler(childrenParser, relationshipName, children) );
|
||||||
|
|
||||||
|
// Derserialise back into SObject list complete with children
|
||||||
|
return JSON.deserialize(combinedOutput.getAsString(), parentsType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes an instance an Id factory
|
||||||
|
**/
|
||||||
|
public static final IDGenerator Id = new IDGenerator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple Id factory
|
||||||
|
**/
|
||||||
|
public with sharing class IDGenerator
|
||||||
|
{
|
||||||
|
private Integer fakeIdCount = 0;
|
||||||
|
private final String ID_PATTERN = '000000000000';
|
||||||
|
|
||||||
|
private IDGenerator() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a fake Salesforce Id for the given SObjectType
|
||||||
|
*/
|
||||||
|
public Id generate(Schema.SObjectType sobjectType)
|
||||||
|
{
|
||||||
|
String keyPrefix = sobjectType.getDescribe().getKeyPrefix();
|
||||||
|
fakeIdCount++;
|
||||||
|
String fakeIdPrefix = ID_PATTERN.substring(0, 12 - fakeIdCount.format().length());
|
||||||
|
return System.Id.valueOf(keyPrefix + fakeIdPrefix + fakeIdCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors stream events for end of object for each SObject contained in the parent list
|
||||||
|
* then injects the respective childs record list into the stream
|
||||||
|
**/
|
||||||
|
private class InjectChildrenEventHandler implements JSONParserEvents {
|
||||||
|
private JSONParser childrenParser;
|
||||||
|
private String relationshipName;
|
||||||
|
private List<List<SObject>> children;
|
||||||
|
private Integer childListIdx = 0;
|
||||||
|
|
||||||
|
public InjectChildrenEventHandler(JSONParser childrenParser, String relationshipName, List<List<SObject>> children) {
|
||||||
|
this.childrenParser = childrenParser;
|
||||||
|
this.relationshipName = relationshipName;
|
||||||
|
this.children = children;
|
||||||
|
this.childrenParser.nextToken(); // Consume the outer array token
|
||||||
|
}
|
||||||
|
|
||||||
|
public void nextToken(JSONParser fromStream, Integer depth, JSONGenerator toStream) {
|
||||||
|
// Inject children?
|
||||||
|
JSONToken currentToken = fromStream.getCurrentToken();
|
||||||
|
if(depth == 2 && currentToken == JSONToken.END_OBJECT ) {
|
||||||
|
toStream.writeFieldName(relationshipName);
|
||||||
|
toStream.writeStartObject();
|
||||||
|
toStream.writeNumberField('totalSize', children[childListIdx].size());
|
||||||
|
toStream.writeBooleanField('done', true);
|
||||||
|
toStream.writeFieldName('records');
|
||||||
|
streamTokens(childrenParser, toStream, null);
|
||||||
|
toStream.writeEndObject();
|
||||||
|
childListIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to stream tokens from a reader to a write, while providing a basic eventing model
|
||||||
|
**/
|
||||||
|
private static void streamTokens(JSONParser fromStream, JSONGenerator toStream, JSONParserEvents events) {
|
||||||
|
Integer depth = 0;
|
||||||
|
while (fromStream.nextToken()!=null)
|
||||||
|
{
|
||||||
|
// Give event handler chance to inject
|
||||||
|
if(events!=null)
|
||||||
|
events.nextToken(fromStream, depth, toStream);
|
||||||
|
// Forward to output stream
|
||||||
|
JSONToken currentToken = fromStream.getCurrentToken();
|
||||||
|
if(currentToken == JSONToken.START_ARRAY) {
|
||||||
|
toStream.writeStartArray();
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
else if(currentToken == JSONToken.START_OBJECT) {
|
||||||
|
toStream.writeStartObject();
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
else if(currentToken == JSONToken.FIELD_NAME)
|
||||||
|
toStream.writeFieldName(fromStream.getCurrentName());
|
||||||
|
else if(currentToken == JSONToken.VALUE_STRING ||
|
||||||
|
currentToken == JSONToken.VALUE_FALSE ||
|
||||||
|
currentToken == JSONToken.VALUE_TRUE ||
|
||||||
|
currentToken == JSONToken.VALUE_NUMBER_FLOAT ||
|
||||||
|
currentToken == JSONToken.VALUE_NUMBER_INT)
|
||||||
|
toStream.writeString(fromStream.getText());
|
||||||
|
else if(currentToken == JSONToken.END_OBJECT) {
|
||||||
|
toStream.writeEndObject();
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
else if(currentToken == JSONToken.END_ARRAY) {
|
||||||
|
toStream.writeEndArray();
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
// Don't continue to stream beyond the initial starting point
|
||||||
|
if(depth==0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic event used during the above streaming
|
||||||
|
**/
|
||||||
|
private interface JSONParserEvents {
|
||||||
|
void nextToken(JSONParser fromStream, Integer depth, JSONGenerator toStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||||
|
<apiVersion>53.0</apiVersion>
|
||||||
|
<status>Active</status>
|
||||||
|
</ApexClass>
|
||||||
Reference in New Issue
Block a user