Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 231 additions & 7 deletions force-app/main/default/classes/standard-soql/SOQL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ global virtual inherited sharing class SOQL implements Queryable {
Queryable with(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
Queryable with(String relationshipName, Iterable<SObjectField> fields);
Queryable with(SubQuery subQuery);
Queryable with(TypeOf typeOf);
Queryable withFieldSet(String fieldSetName);
// SELECT - AGGREGATE FUNCTIONS
Queryable count();
Expand Down Expand Up @@ -229,6 +230,32 @@ global virtual inherited sharing class SOQL implements Queryable {
String getChildRelationshipName();
}

global interface TypeOf {
// TYPEOF
Comment thread
pgajek2 marked this conversation as resolved.
TypeOf of(String ofField);
TypeOf of(SObjectField ofField);
// WHEN
Typeof when(SObjectType whenObject);
// THEN
Typeof then(SObjectField field);
Typeof then(SObjectField field1, SObjectField field2);
Typeof then(SObjectField field1, SObjectField field2, SObjectField field3);
Typeof then(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4);
Typeof then(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
Typeof then(List<SObjectField> fields);
Typeof then(Iterable<String> fields);
Typeof then(String fields);
Typeof then(String relationshipName, SObjectField field);
Typeof then(String relationshipName, SObjectField field1, SObjectField field2);
Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3);
Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4);
Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
Typeof then(String relationshipName, Iterable<SObjectField> fields);
// ELSE
Typeof whenElse(Iterable<String> fields);
Typeof whenElse(String fields);
}

global interface FilterGroup {
// ADD CONDITION
FilterGroup add(FilterGroup filterGroup);
Expand Down Expand Up @@ -375,6 +402,10 @@ global virtual inherited sharing class SOQL implements Queryable {
get { return new SoqlSubQuery(); }
}

Comment thread
pgajek2 marked this conversation as resolved.
global static Typeof Type {
get { return new SoqlTypeof(); }
}

global static FilterGroup FilterGroup {
get { return new SoqlFilterGroup(); }
}
Expand Down Expand Up @@ -558,6 +589,11 @@ global virtual inherited sharing class SOQL implements Queryable {
return this;
}

global Queryable with(TypeOf typeof) {
this.builder.fields.typeofFields.add(typeof);
return this;
}

global Queryable count() {
this.builder.fields.count();
return this;
Expand Down Expand Up @@ -1223,6 +1259,7 @@ global virtual inherited sharing class SOQL implements Queryable {
public RelationshipFields relationshipFields = new RelationshipFields();
public FunctionsFields functionsFields = new FunctionsFields(); // toLabel, FORMAT
public AggregateFunctionsFields aggregateFields = new AggregateFunctionsFields();
public TypeOfFields typeofFields = new TypeOfFields();

private String ofObject;
private Set<String> groupedFields = new Set<String>();
Expand Down Expand Up @@ -1297,7 +1334,8 @@ global virtual inherited sharing class SOQL implements Queryable {
this.plainFields.isEmpty() &&
this.relationshipFields.isEmpty() &&
this.aggregateFields.isEmpty() &&
this.functionsFields.isEmpty()
this.functionsFields.isEmpty() &&
this.typeofFields.isEmpty()
) {
this.plainFields.add('Id');
}
Expand All @@ -1315,6 +1353,7 @@ global virtual inherited sharing class SOQL implements Queryable {
selectFields.addAll(this.relationshipFields.get());
selectFields.addAll(this.functionsFields.get());
selectFields.addAll(this.aggregateFields.get());
selectFields.addAll(this.typeofFields.get());

return 'SELECT ' + String.join(selectFields, ', ');
}
Expand Down Expand Up @@ -1414,6 +1453,66 @@ global virtual inherited sharing class SOQL implements Queryable {
}
}

Comment thread
pgajek2 marked this conversation as resolved.
private class TypeOfFields extends SelectFields {
public void add(TypeOf typeof) {
this.add(typeof.toString());
}
}

private class TypeOfWhenThen {
// WHEN
private SObjectType whenObject;
// THEN
public PlainFields plainFields = new PlainFields();
public RelationshipFields relationshipFields = new RelationshipFields();

public TypeOfWhenThen(SObjectType whenObject){
this.whenObject = whenObject;
}

public TypeOfWhenThen(){
this(null);
}

public Boolean isEmpty() {
return this.plainFields.isEmpty() &&
this.relationshipFields.isEmpty();
}

public void with(String commaSeparatedFields) {
// Add to Set to avoid "duplicate field selected" error
for (String splitField : commaSeparatedFields.split(',')) {
this.classifyField(splitField);
}
}

private void classifyField(String field) {
String trimmedField = field.trim();

if (this.relationshipFields.isQualified(trimmedField)) {
this.relationshipFields.add(trimmedField);
} else {
this.plainFields.add(trimmedField);
}
}

public override String toString() {
if (this.isEmpty()){
return '';
}

String prefix = this.whenObject != null
? 'WHEN ' + this.whenObject.toString() + ' THEN '
: 'ELSE ';

Set<String> fields = new Set<String>();
fields.addAll(plainFields.get());
fields.addAll(relationshipFields.get());

return prefix + String.join(fields, ',');
}
}

private class SoqlSubQuery implements SubQuery {
private SoqlBuilder builder;
private String childRelationshipName;
Expand Down Expand Up @@ -1534,6 +1633,131 @@ global virtual inherited sharing class SOQL implements Queryable {
}
}

private class SoqlTypeOf implements Typeof {
private String ofField;
private Map<SObjectType, TypeOfWhenThen> fieldsByType = new Map<SObjectType, TypeOfWhenThen>();
private List<SObjectType> whenTypes = new List<SObjectType>();
private TypeOfWhenThen elseFields = new TypeOfWhenThen();

public SoqlTypeOf of(String ofField) {
if (this.ofField != null) {
throw new QueryException('TYPEOF has already been initialized for: ' + this.ofField);
}
this.ofField = ofField;

return this;
}
public SoqlTypeOf of(SObjectField ofField) {
return this.of(ofField.getDescribe().getRelationshipName());
Comment thread
pgajek2 marked this conversation as resolved.
}

public SoqlTypeOf when(SObjectType whenObject) {
if (fieldsByType.containsKey(whenObject)){
throw new QueryException('TYPEOF already contains configuration for: ' + whenObject.toString());
}

this.fieldsByType.put(whenObject, new TypeOfWhenThen(whenObject));
this.whenTypes.add(whenObject);

return this;
}

public Typeof then(SObjectField field) {
this.fieldsByType.get(this.latestWhen()).plainFields.add(field.toString());
return this;
}

public Typeof then(SObjectField field1, SObjectField field2) {
return this.then(field1).then(field2);
}

public Typeof then(SObjectField field1, SObjectField field2, SObjectField field3) {
return this.then(field1, field2).then(field3);
}

public Typeof then(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4) {
return this.then(field1, field2, field3).then(field4);
}

public Typeof then(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5) {
return this.then(field1, field2, field3, field4).then(field5);
}

public Typeof then(List<SObjectField> fields) {
this.fieldsByType.get(this.latestWhen()).plainFields.add(fields);
return this;
}

public Typeof then(Iterable<String> fields) {
return this.then(String.join(fields, ','));
}

public Typeof then(String fields) {
this.fieldsByType.get(this.latestWhen()).with(fields);
return this;
}

public Typeof then(String relationshipName, SObjectField field) {
return then(relationshipName, new List<SObjectField>{field});
}

public Typeof then(String relationshipName, SObjectField field1, SObjectField field2) {
return then(relationshipName, new List<SObjectField>{field1, field2});
}

public Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3) {
return then(relationshipName, new List<SObjectField>{field1, field2, field3});
}

public Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4) {
return then(relationshipName, new List<SObjectField>{field1, field2, field3, field4});
}

public Typeof then(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5) {
return then(relationshipName, new List<SObjectField>{field1, field2, field3, field4, field5});
}

public Typeof then(String relationshipName, Iterable<SObjectField> fields) {
this.fieldsByType.get(this.latestWhen()).relationshipFields.add(relationshipName, fields);
return this;
}

public Typeof whenElse(Iterable<String> fields) {
return this.whenElse(String.join(fields, ','));
}

public Typeof whenElse(String fields) {
this.elseFields.with(fields);
return this;
}

public override String toString() {
String prefix = 'TYPEOF ' + this.ofField + ' ';
List<String> clauses = new List<String>();
String suffix = ' END';

for (SObjectType t : this.whenTypes){
TypeOfWhenThen fields = this.fieldsByType.get(t);

if (!fields.isEmpty()){
clauses.add(fields.toString());
}
}

if (!clauses.isEmpty() && !this.elseFields.isEmpty()){
clauses.add(this.elseFields.toString());
}

return !clauses.isEmpty()
? prefix + String.join(clauses, ' ') + suffix
: '';
}

private SObjectType latestWhen() {
return this.whenTypes.get(this.whenTypes.size()-1);
}
}

private class SoqlSubQueries implements QueryClause {
private List<SubQuery> subQueries = new List<SubQuery>();

Expand Down Expand Up @@ -2561,7 +2785,7 @@ global virtual inherited sharing class SOQL implements Queryable {
private List<SObject> stripAdditionalFields(Set<String> requestedFields, Set<String> subQueriesRelationshipNames) {
List<SObject> cleanedRecords = new List<SObject>();

Type objectTypeName = Type.forName(this.mockedRecords[0].getSObjectType().toString());
Type objectTypeName = System.Type.forName(this.mockedRecords[0].getSObjectType().toString());

for (SObject record : this.mockedRecords) {
Map<String, Object> recordFilteredFields = new Map<String, Object>{ 'Id' => record.Id };
Expand Down Expand Up @@ -2908,7 +3132,7 @@ global virtual inherited sharing class SOQL implements Queryable {
}

public Map<Id, SObject> toMap() {
Map<Id, SObject> recordPerId = (Map<Id, SObject>) Type.forName('Map<Id, ' + this.ofObject + ' >').newInstance();
Map<Id, SObject> recordPerId = (Map<Id, SObject>) System.Type.forName('Map<Id, ' + this.ofObject + ' >').newInstance();
recordPerId.putAll(this.recordsToTransform);
return recordPerId;
}
Expand All @@ -2922,7 +3146,7 @@ global virtual inherited sharing class SOQL implements Queryable {
}

public Map<Id, SObject> toIdMapBy(String fieldPath) {
Map<Id, SObject> recordPerCustomKey = (Map<Id, SObject>) Type.forName('Map<Id, ' + this.ofObject + ' >').newInstance();
Map<Id, SObject> recordPerCustomKey = (Map<Id, SObject>) System.Type.forName('Map<Id, ' + this.ofObject + ' >').newInstance();
List<String> relationshipPathFields = fieldPath.split('\\.');
String targetField = relationshipPathFields.remove(relationshipPathFields.size() - 1);

Expand All @@ -2942,7 +3166,7 @@ global virtual inherited sharing class SOQL implements Queryable {
}

private Map<Id, List<SObject>> toAggregatedIdMapBy(String keyFieldPath) {
Map<Id, List<SObject>> recordsPerCustomKey = (Map<Id, List<SObject>>) Type.forName('Map<Id, List<' + this.ofObject + ' >>').newInstance();
Map<Id, List<SObject>> recordsPerCustomKey = (Map<Id, List<SObject>>) System.Type.forName('Map<Id, List<' + this.ofObject + ' >>').newInstance();
List<String> relationshipPathFields = keyFieldPath.split('\\.');
String targetField = relationshipPathFields.remove(relationshipPathFields.size() - 1);

Expand All @@ -2968,7 +3192,7 @@ global virtual inherited sharing class SOQL implements Queryable {
}

public Map<String, SObject> toMap(String fieldPath) {
Map<String, SObject> recordPerCustomKey = (Map<String, SObject>) Type.forName('Map<String, ' + this.ofObject + ' >').newInstance();
Map<String, SObject> recordPerCustomKey = (Map<String, SObject>) System.Type.forName('Map<String, ' + this.ofObject + ' >').newInstance();
List<String> relationshipPathFields = fieldPath.split('\\.');
String targetField = relationshipPathFields.remove(relationshipPathFields.size() - 1);

Expand Down Expand Up @@ -3036,7 +3260,7 @@ global virtual inherited sharing class SOQL implements Queryable {
}

private Map<String, List<SObject>> toSObjectAggregatedMap(SObjectField keyField, String keyFieldPath) {
Map<String, List<SObject>> recordsPerCustomKey = (Map<String, List<SObject>>) Type.forName('Map<String, List<' + this.ofObject + ' >>').newInstance();
Map<String, List<SObject>> recordsPerCustomKey = (Map<String, List<SObject>>) System.Type.forName('Map<String, List<' + this.ofObject + ' >>').newInstance();
List<String> relationshipPathFields = keyFieldPath.split('\\.');
String targetField = relationshipPathFields.remove(relationshipPathFields.size() - 1);

Expand Down
Loading