Skip to content

Commit

Permalink
SOQL toList Fix + Unit Test (#98)
Browse files Browse the repository at this point in the history
* SOQL toList Fix + Unit Test

* Code refactoring

* dynamicFiltersGroupOnSoqlInstance Test

* Conditions order fix

* Add more asserts to multipleToStringExecutions Test
  • Loading branch information
pgajek2 authored Nov 22, 2023
1 parent 3acc472 commit 84dd10d
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 45 deletions.
72 changes: 27 additions & 45 deletions force-app/main/default/classes/SOQL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ public virtual inherited sharing class SOQL implements Queryable {
}

public SOQL(String ofObject) {
binder = new Binder();
builder = new QueryBuilder(ofObject);
executor = new Executor(ofObject, builder);
}
Expand Down Expand Up @@ -577,11 +576,13 @@ public virtual inherited sharing class SOQL implements Queryable {
}

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

public Map<String, Object> binding() {
@TestVisible
private Map<String, Object> binding() {
return binder.getBindingMap();
}

Expand Down Expand Up @@ -768,6 +769,8 @@ public virtual inherited sharing class SOQL implements Queryable {
}

public override String toString() {
binder = new Binder();

List<String> soqlParts = new List<String>();

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

private virtual class SoqlFilterGroup implements FilterGroup {
private List<FilterClause> queryConditions = new List<FilterClause>();
private String order;
private String customOrder;
private String connector = 'AND';

public FilterGroup add(FilterGroup filterGroup) {
Expand Down Expand Up @@ -1108,7 +1111,7 @@ public virtual inherited sharing class SOQL implements Queryable {
}

public FilterGroup conditionLogic(String order) {
this.order = order;
customOrder = order;
return this;
}

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

private void setDefaultOrderWhenNotSpecified() {
if (String.isNotEmpty(order)) {
return;
}
public String buildNested() {
return String.format(getOrderWithSpecialCharacters(), queryConditions);
}

List<String> defaultOrder = new List<String>();
private String getOrderWithSpecialCharacters() {
String orderWithSpecialCharacters = getConditionsLogic();

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

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

public String buildNested() {
setDefaultOrderWhenNotSpecified(); // e.g (0 AND 1 AND 2)
addSpecialCharactersToOrder(); // e.g ({0} AND ({1} AND {2}))
private String getConditionsLogic() {
if (String.isNotEmpty(customOrder)) {
return customOrder;
}

return String.format(order, queryConditions);
}
List<String> defaultOrder = new List<String>();

private void addSpecialCharactersToOrder() {
for (Integer i = 0; i < queryConditions.size(); i++) {
order = order.replace(String.valueOf(i + 1), '{' + i + '}');
defaultOrder.add(String.valueOf(i + 1));
}

return String.join(defaultOrder, ' ' + connector + ' '); // e.g (0 AND 1 AND 2)
}
}

Expand Down Expand Up @@ -1622,7 +1626,6 @@ public virtual inherited sharing class SOQL implements Queryable {
private AccessType accessType;
private String mockId;
private String ofObject;
private Boolean preview = false;
private QueryBuilder builder;

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

public void withPreview() {
preview = true;
}

private String buildSOQL() {
if (preview) {
String soql = builder.toString();
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Preview ============\n' + soql + '\n=======================================\n');
return soql;
}

return builder.toString();
}

private Map<String,Object> buildBinding() {
if (preview) {
System.debug(LoggingLevel.ERROR, '\n\n============ SOQL Binding ============\n' + JSON.serializePretty(binder.getBindingMap()) + '\n=======================================\n');
}
return binder.getBindingMap();
}

public SObject toObject() {
List<SObject> records = toList();

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

if (accessType == null) {
return sharingExecutor.toSObjects(buildSOQL(), buildBinding(), accessMode);
return sharingExecutor.toSObjects(builder.toString(), binder.getBindingMap(), accessMode);
}

return Security.stripInaccessible(
accessType,
sharingExecutor.toSObjects(buildSOQL(), buildBinding(), accessMode)
sharingExecutor.toSObjects(builder.toString(), binder.getBindingMap(), accessMode)
).getRecords();
}

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

return sharingExecutor.toInteger(buildSOQL(), buildBinding(), accessMode);
return sharingExecutor.toInteger(builder.toString(), binder.getBindingMap(), accessMode);
}

public Database.QueryLocator toQueryLocator() {
return Database.getQueryLocatorWithBinds(buildSOQL(), buildBinding(), accessMode);
return Database.getQueryLocatorWithBinds(builder.toString(), binder.getBindingMap(), accessMode);
}
}

Expand Down
60 changes: 60 additions & 0 deletions force-app/main/default/classes/SOQL_Test.cls
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,35 @@ private class SOQL_Test {
Assert.areEqual('Krakow', binding.get('v2'));
}

@IsTest
static void dynamicFiltersGroupOnSoqlInstance() {
// Setup
SOQL.FilterGroup filterGroup = SOQL.FilterGroup;

filterGroup.add(SOQL.Filter.with(Account.Name).equal('Test'));
filterGroup.add(SOQL.Filter.with(Account.BillingCity).equal('Krakow'));

// Test
SOQL builder = SOQL.of(Account.SObjectType)
.whereAre(SOQL.Filter.with(Account.Industry).equal('IT'));

Assert.areEqual('SELECT Id FROM Account WHERE Industry = :v1', builder.toString());

builder.whereAre(
SOQL.FilterGroup
.add(SOQL.Filter.with(Account.Name).equal('Test'))
.add(SOQL.Filter.with(Account.BillingCity).equal('Krakow'))
);

// Verify
Assert.areEqual('SELECT Id FROM Account WHERE Industry = :v1 AND (Name = :v2 AND BillingCity = :v3)', builder.toString());

Map<String, Object> binding = builder.binding();
Assert.areEqual('IT', binding.get('v1'));
Assert.areEqual('Test', binding.get('v2'));
Assert.areEqual('Krakow', binding.get('v3'));
}

@IsTest
static void anyConditionMatchingForInnerGroup() {
// Test
Expand Down Expand Up @@ -1928,6 +1957,37 @@ private class SOQL_Test {
Assert.isTrue(isRecordExist);
}

@IsTest
static void multipleToStringExecutions() {
// Setup
Exception soqlException = null;

SOQL builder = SOQL.of(Account.SObjectType)
.whereAre(SOQL.FilterGroup
.add(SOQL.Filter.with(Account.Name).equal('Test'))
.add(SOQL.Filter.with(Account.Industry).equal('IT'))
);

// Test
try {
builder.preview();
builder.toString();
builder.toString();
builder.toList();
} catch (Exception e) {
soqlException = e;
}

// Verify
Assert.isNull(soqlException);

Assert.areEqual('SELECT Id FROM Account WHERE (Name = :v1 AND Industry = :v2)', builder.toString());

Map<String, Object> binding = builder.binding();
Assert.areEqual('Test', binding.get('v1'));
Assert.areEqual('IT', binding.get('v2'));
}

@IsTest
static void toValueOf() {
// Setup
Expand Down

1 comment on commit 84dd10d

@vercel
Copy link

@vercel vercel bot commented on 84dd10d Nov 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.