Skip to content

Commit

Permalink
Soql.cls Enhancements
Browse files Browse the repository at this point in the history
* Added new enum value, `Soql.FieldCategory.IGNORE_FLS`. This can be used to add a field to a query, regardless of the field-level security (FLS)
* Added 2 new overloaded versions of filterWhere method
    * `public Soql filterWhere(QueryFilter filter)`
    * `public Soql filterWhere(List<QueryFilter> filters)`
* Created 2 new subclasses
    * `Soql.QueryArgument` - takes an Object and formats it to use in a query
    * `Soql.QueryFilter` - represents a single where clause filter
  • Loading branch information
jongpie committed Jun 5, 2018
1 parent f556043 commit f9d0249
Showing 1 changed file with 101 additions and 56 deletions.
157 changes: 101 additions & 56 deletions src/classes/Soql.cls
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
public virtual class Soql implements Comparable {
public class Soql implements Comparable {

public enum Aggregate { AVG, COUNT, COUNT_DISTINCT, MAX, MIN, SUM }
public enum FieldCategory { ACCESSIBLE, UPDATEABLE, STANDARD, CUSTOM }
public enum FieldCategory { ACCESSIBLE, UPDATEABLE, STANDARD, CUSTOM, IGNORE_FLS }
public enum Scope { EVERYTHING, DELEGATED, TEAM, MINE, MY_TERRITORY, MY_TEAM_TERRITORY }
public enum SortOrder { ASCENDING, DESCENDING }

Expand Down Expand Up @@ -146,8 +146,15 @@ public virtual class Soql implements Comparable {
}

public Soql filterWhere(Soql.QueryField queryField, String operator, Object value) {
String whereFilter = queryField + ' ' + String.escapeSingleQuotes(operator) + ' ' + this.formatObjectForQueryString(value);
this.whereFilters.add(whereFilter);
return this.filterWhere(new QueryFilter(queryField, operator, value));
}

public Soql filterWhere(QueryFilter filter) {
return this.filterWhere(new List<QueryFilter>{filter});
}

public Soql filterWhere(List<QueryFilter> filters) {
for(QueryFilter filter : filters) this.whereFilters.add(filter.toString());
return this.setHasChanged();
}

Expand Down Expand Up @@ -326,60 +333,8 @@ public virtual class Soql implements Comparable {
return 'format(' + fieldApiName + ') ' + fieldApiName.replace('.', '_') + '__Formatted';
}

private String formatObjectForQueryString(Object valueToFormat) {
if(valueToFormat == null) return null;
else if(valueToFormat instanceOf List<Object>) return this.convertListToQueryString((List<Object>)valueToFormat);
else if(valueToFormat instanceOf Set<Object>) return this.convertSetToQueryString(valueToFormat);
else if(valueToFormat instanceOf Map<Object, Object>) return this.convertMapToQueryString(valueToFormat);
else if(valueToFormat instanceOf Date) return String.valueOf((Date)valueToFormat).left(10);
else if(valueToFormat instanceOf Datetime) {
Datetime datetimeValue = (Datetime)valueToFormat;
return datetimeValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time');
}
else if(valueToFormat instanceOf Sobject) {
Sobject record = (Sobject)valueToFormat;
return this.wrapInSingleQuotes(((Sobject)valueToFormat).Id);
}
else if(valueToFormat instanceOf String) {
// Escape single quotes to prevent SOQL/SOSL injection
String stringArgument = String.escapeSingleQuotes((String)valueToFormat);
return this.wrapInSingleQuotes(stringArgument);
}
else return String.valueOf(valueToFormat);
}

private String wrapInSingleQuotes(String input) {
input = input.trim();
if(input.left(1) != '\'') input = '\'' + input;
if(input.right(1) != '\'') input = input + '\'';
return input;
}

private String convertListToQueryString(List<Object> valueList) {
List<String> parsedValueList = new List<String>();
for(Object value : valueList) {
parsedValueList.add(this.formatObjectForQueryString(value));
}
return '(' + String.join(parsedValueList, ', ') + ')';
}

private String convertSetToQueryString(Object valueSet) {
String unformattedString = String.valueOf(valueSet).replace('{', '').replace('}', '');
List<String> parsedValueList = new List<String>();
for(String collectionItem : unformattedString.split(',')) {
parsedValueList.add(this.formatObjectForQueryString(collectionItem));
}
return '(' + String.join(parsedValueList, ', ') + ')';
}

private String convertMapToQueryString(Object valueMap) {
Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(valueMap));
return this.convertSetToQueryString(m.keySet());
}

private List<String> getFieldsToQuery(QueryField queryField, FieldCategory fieldCat) {
List<String> fieldsToReturn = new List<String>();
//Schema.SobjectField field = this.sobjectDescribe.fields.getMap().get(fieldApiName);

if(fieldCat == null) return fieldsToReturn;
else if(fieldCat == FieldCategory.ACCESSIBLE && !queryField.getDescribe().isAccessible()) return fieldsToReturn;
Expand Down Expand Up @@ -545,6 +500,71 @@ public virtual class Soql implements Comparable {

}

public virtual class QueryArgument {

private String value;

public QueryArgument(Object valueToFormat) {
this.value = this.formatObjectForQueryString(valueToFormat);
}

public override String toString() {
return this.value;
}

private String formatObjectForQueryString(Object valueToFormat) {
if(valueToFormat == null) return null;
else if(valueToFormat instanceOf List<Object>) return this.convertListToQueryString((List<Object>)valueToFormat);
else if(valueToFormat instanceOf Set<Object>) return this.convertSetToQueryString(valueToFormat);
else if(valueToFormat instanceOf Map<Object, Object>) return this.convertMapToQueryString(valueToFormat);
else if(valueToFormat instanceOf Date) return String.valueOf((Date)valueToFormat).left(10);
else if(valueToFormat instanceOf Datetime) {
Datetime datetimeValue = (Datetime)valueToFormat;
return datetimeValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time');
}
else if(valueToFormat instanceOf Sobject) {
Sobject record = (Sobject)valueToFormat;
return this.wrapInSingleQuotes(((Sobject)valueToFormat).Id);
}
else if(valueToFormat instanceOf String) {
// Escape single quotes to prevent SOQL/SOSL injection
String stringArgument = String.escapeSingleQuotes((String)valueToFormat);
return this.wrapInSingleQuotes(stringArgument);
}
else return String.valueOf(valueToFormat);
}

private String wrapInSingleQuotes(String input) {
input = input.trim();
if(input.left(1) != '\'') input = '\'' + input;
if(input.right(1) != '\'') input = input + '\'';
return input;
}

private String convertListToQueryString(List<Object> valueList) {
List<String> parsedValueList = new List<String>();
for(Object value : valueList) {
parsedValueList.add(this.formatObjectForQueryString(value));
}
return '(' + String.join(parsedValueList, ', ') + ')';
}

private String convertSetToQueryString(Object valueSet) {
String unformattedString = String.valueOf(valueSet).replace('{', '').replace('}', '');
List<String> parsedValueList = new List<String>();
for(String collectionItem : unformattedString.split(',')) {
parsedValueList.add(this.formatObjectForQueryString(collectionItem));
}
return '(' + String.join(parsedValueList, ', ') + ')';
}

private String convertMapToQueryString(Object valueMap) {
Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(valueMap));
return this.convertSetToQueryString(m.keySet());
}

}

public class QueryField {

private final String queryField;
Expand Down Expand Up @@ -614,4 +634,29 @@ public virtual class Soql implements Comparable {

}

public class QueryFilter implements Comparable {

private String value;

public QueryFilter(Schema.SobjectField field, String operator, Object value) {
this(new QueryField(field), operator, value);
}

public QueryFilter(QueryField queryField, String operator, Object value) {
this.value = queryField + ' ' + operator + ' ' + new QueryArgument(value);
}

public Integer compareTo(Object compareTo) {
QueryFilter compareToQueryFilter = (QueryFilter)compareTo;
if(this.toString() == compareToQueryFilter.toString()) return 0;
else if(this.toString() > compareToQueryFilter.toString()) return 1;
else return -1;
}

public override String toString() {
return this.value;
}

}

}

0 comments on commit f9d0249

Please sign in to comment.