Skip to content

Commit 84dd10d

Browse files
authored
SOQL toList Fix + Unit Test (#98)
* SOQL toList Fix + Unit Test * Code refactoring * dynamicFiltersGroupOnSoqlInstance Test * Conditions order fix * Add more asserts to multipleToStringExecutions Test
1 parent 3acc472 commit 84dd10d

File tree

2 files changed

+87
-45
lines changed

2 files changed

+87
-45
lines changed

force-app/main/default/classes/SOQL.cls

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ public virtual inherited sharing class SOQL implements Queryable {
275275
}
276276

277277
public SOQL(String ofObject) {
278-
binder = new Binder();
279278
builder = new QueryBuilder(ofObject);
280279
executor = new Executor(ofObject, builder);
281280
}
@@ -577,11 +576,13 @@ public virtual inherited sharing class SOQL implements Queryable {
577576
}
578577

579578
public SOQL preview() {
580-
executor.withPreview();
579+
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Preview ============\n' + toString() + '\n=======================================\n');
580+
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Binding ============\n' + JSON.serializePretty(binding()) + '\n=======================================\n');
581581
return this;
582582
}
583583

584-
public Map<String, Object> binding() {
584+
@TestVisible
585+
private Map<String, Object> binding() {
585586
return binder.getBindingMap();
586587
}
587588

@@ -768,6 +769,8 @@ public virtual inherited sharing class SOQL implements Queryable {
768769
}
769770

770771
public override String toString() {
772+
binder = new Binder();
773+
771774
List<String> soqlParts = new List<String>();
772775

773776
for (QueryClause clause : clauses) {
@@ -1079,7 +1082,7 @@ public virtual inherited sharing class SOQL implements Queryable {
10791082

10801083
private virtual class SoqlFilterGroup implements FilterGroup {
10811084
private List<FilterClause> queryConditions = new List<FilterClause>();
1082-
private String order;
1085+
private String customOrder;
10831086
private String connector = 'AND';
10841087

10851088
public FilterGroup add(FilterGroup filterGroup) {
@@ -1108,7 +1111,7 @@ public virtual inherited sharing class SOQL implements Queryable {
11081111
}
11091112

11101113
public FilterGroup conditionLogic(String order) {
1111-
this.order = order;
1114+
customOrder = order;
11121115
return this;
11131116
}
11141117

@@ -1127,31 +1130,32 @@ public virtual inherited sharing class SOQL implements Queryable {
11271130
return '(' + buildNested() + ')';
11281131
}
11291132

1130-
private void setDefaultOrderWhenNotSpecified() {
1131-
if (String.isNotEmpty(order)) {
1132-
return;
1133-
}
1133+
public String buildNested() {
1134+
return String.format(getOrderWithSpecialCharacters(), queryConditions);
1135+
}
11341136

1135-
List<String> defaultOrder = new List<String>();
1137+
private String getOrderWithSpecialCharacters() {
1138+
String orderWithSpecialCharacters = getConditionsLogic();
11361139

11371140
for (Integer i = 0; i < queryConditions.size(); i++) {
1138-
defaultOrder.add(String.valueOf(i + 1));
1141+
orderWithSpecialCharacters = orderWithSpecialCharacters.replace(String.valueOf(i + 1), '{' + i + '}');
11391142
}
11401143

1141-
order = String.join(defaultOrder, ' ' + connector + ' ');
1144+
return orderWithSpecialCharacters; // e.g ({0} AND ({1} AND {2}))
11421145
}
11431146

1144-
public String buildNested() {
1145-
setDefaultOrderWhenNotSpecified(); // e.g (0 AND 1 AND 2)
1146-
addSpecialCharactersToOrder(); // e.g ({0} AND ({1} AND {2}))
1147+
private String getConditionsLogic() {
1148+
if (String.isNotEmpty(customOrder)) {
1149+
return customOrder;
1150+
}
11471151

1148-
return String.format(order, queryConditions);
1149-
}
1152+
List<String> defaultOrder = new List<String>();
11501153

1151-
private void addSpecialCharactersToOrder() {
11521154
for (Integer i = 0; i < queryConditions.size(); i++) {
1153-
order = order.replace(String.valueOf(i + 1), '{' + i + '}');
1155+
defaultOrder.add(String.valueOf(i + 1));
11541156
}
1157+
1158+
return String.join(defaultOrder, ' ' + connector + ' '); // e.g (0 AND 1 AND 2)
11551159
}
11561160
}
11571161

@@ -1622,7 +1626,6 @@ public virtual inherited sharing class SOQL implements Queryable {
16221626
private AccessType accessType;
16231627
private String mockId;
16241628
private String ofObject;
1625-
private Boolean preview = false;
16261629
private QueryBuilder builder;
16271630

16281631
public Executor(String ofObject, QueryBuilder builder) {
@@ -1650,27 +1653,6 @@ public virtual inherited sharing class SOQL implements Queryable {
16501653
mockId = id;
16511654
}
16521655

1653-
public void withPreview() {
1654-
preview = true;
1655-
}
1656-
1657-
private String buildSOQL() {
1658-
if (preview) {
1659-
String soql = builder.toString();
1660-
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Preview ============\n' + soql + '\n=======================================\n');
1661-
return soql;
1662-
}
1663-
1664-
return builder.toString();
1665-
}
1666-
1667-
private Map<String,Object> buildBinding() {
1668-
if (preview) {
1669-
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Binding ============\n' + JSON.serializePretty(binder.getBindingMap()) + '\n=======================================\n');
1670-
}
1671-
return binder.getBindingMap();
1672-
}
1673-
16741656
public SObject toObject() {
16751657
List<SObject> records = toList();
16761658

@@ -1693,12 +1675,12 @@ public virtual inherited sharing class SOQL implements Queryable {
16931675
}
16941676

16951677
if (accessType == null) {
1696-
return sharingExecutor.toSObjects(buildSOQL(), buildBinding(), accessMode);
1678+
return sharingExecutor.toSObjects(builder.toString(), binder.getBindingMap(), accessMode);
16971679
}
16981680

16991681
return Security.stripInaccessible(
17001682
accessType,
1701-
sharingExecutor.toSObjects(buildSOQL(), buildBinding(), accessMode)
1683+
sharingExecutor.toSObjects(builder.toString(), binder.getBindingMap(), accessMode)
17021684
).getRecords();
17031685
}
17041686

@@ -1765,11 +1747,11 @@ public virtual inherited sharing class SOQL implements Queryable {
17651747
return mock.getCountMock(mockId);
17661748
}
17671749

1768-
return sharingExecutor.toInteger(buildSOQL(), buildBinding(), accessMode);
1750+
return sharingExecutor.toInteger(builder.toString(), binder.getBindingMap(), accessMode);
17691751
}
17701752

17711753
public Database.QueryLocator toQueryLocator() {
1772-
return Database.getQueryLocatorWithBinds(buildSOQL(), buildBinding(), accessMode);
1754+
return Database.getQueryLocatorWithBinds(builder.toString(), binder.getBindingMap(), accessMode);
17731755
}
17741756
}
17751757

force-app/main/default/classes/SOQL_Test.cls

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,35 @@ private class SOQL_Test {
12411241
Assert.areEqual('Krakow', binding.get('v2'));
12421242
}
12431243

1244+
@IsTest
1245+
static void dynamicFiltersGroupOnSoqlInstance() {
1246+
// Setup
1247+
SOQL.FilterGroup filterGroup = SOQL.FilterGroup;
1248+
1249+
filterGroup.add(SOQL.Filter.with(Account.Name).equal('Test'));
1250+
filterGroup.add(SOQL.Filter.with(Account.BillingCity).equal('Krakow'));
1251+
1252+
// Test
1253+
SOQL builder = SOQL.of(Account.SObjectType)
1254+
.whereAre(SOQL.Filter.with(Account.Industry).equal('IT'));
1255+
1256+
Assert.areEqual('SELECT Id FROM Account WHERE Industry = :v1', builder.toString());
1257+
1258+
builder.whereAre(
1259+
SOQL.FilterGroup
1260+
.add(SOQL.Filter.with(Account.Name).equal('Test'))
1261+
.add(SOQL.Filter.with(Account.BillingCity).equal('Krakow'))
1262+
);
1263+
1264+
// Verify
1265+
Assert.areEqual('SELECT Id FROM Account WHERE Industry = :v1 AND (Name = :v2 AND BillingCity = :v3)', builder.toString());
1266+
1267+
Map<String, Object> binding = builder.binding();
1268+
Assert.areEqual('IT', binding.get('v1'));
1269+
Assert.areEqual('Test', binding.get('v2'));
1270+
Assert.areEqual('Krakow', binding.get('v3'));
1271+
}
1272+
12441273
@IsTest
12451274
static void anyConditionMatchingForInnerGroup() {
12461275
// Test
@@ -1928,6 +1957,37 @@ private class SOQL_Test {
19281957
Assert.isTrue(isRecordExist);
19291958
}
19301959

1960+
@IsTest
1961+
static void multipleToStringExecutions() {
1962+
// Setup
1963+
Exception soqlException = null;
1964+
1965+
SOQL builder = SOQL.of(Account.SObjectType)
1966+
.whereAre(SOQL.FilterGroup
1967+
.add(SOQL.Filter.with(Account.Name).equal('Test'))
1968+
.add(SOQL.Filter.with(Account.Industry).equal('IT'))
1969+
);
1970+
1971+
// Test
1972+
try {
1973+
builder.preview();
1974+
builder.toString();
1975+
builder.toString();
1976+
builder.toList();
1977+
} catch (Exception e) {
1978+
soqlException = e;
1979+
}
1980+
1981+
// Verify
1982+
Assert.isNull(soqlException);
1983+
1984+
Assert.areEqual('SELECT Id FROM Account WHERE (Name = :v1 AND Industry = :v2)', builder.toString());
1985+
1986+
Map<String, Object> binding = builder.binding();
1987+
Assert.areEqual('Test', binding.get('v1'));
1988+
Assert.areEqual('IT', binding.get('v2'));
1989+
}
1990+
19311991
@IsTest
19321992
static void toValueOf() {
19331993
// Setup

0 commit comments

Comments
 (0)