Skip to content

Commit 2391490

Browse files
authored
release v2.0.2 [IN PROGRESS] (#54)
* Feature/to values of (#52) * toValuesOf * refactoring * refactoring * refactoring * documentation update * field aliasing * Feature/query exceptions (#53) * query exception * refactoring * Catch only query exception * code review * refactoring * soql error handling
1 parent 07116cf commit 2391490

File tree

7 files changed

+350
-107
lines changed

7 files changed

+350
-107
lines changed

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

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public inherited sharing class SOQL implements Queryable {
5454
Queryable with(SObjectField field1, SObjectField field2, SObjectField field3, SObjectField field4, SObjectField field5);
5555
Queryable with(List<SObjectField> fields); // For more than 5 fields
5656
Queryable with(String fields); // Dynamic SOQL
57+
Queryable with(SObjectField field, String alias); // Only aggregate expressions use field aliasing
5758
Queryable with(String relationshipName, SObjectField field);
5859
Queryable with(String relationshipName, SObjectField field1, SObjectField field2);
5960
Queryable with(String relationshipName, SObjectField field1, SObjectField field2, SObjectField field3);
@@ -114,8 +115,9 @@ public inherited sharing class SOQL implements Queryable {
114115
Queryable byIds(List<SObject> records);
115116

116117
String toString();
117-
Object toField(SObjectField fieldToExtract);
118-
Integer toInteger();
118+
Object toValueOf(SObjectField fieldToExtract);
119+
Set<String> toValuesOf(SObjectField fieldToExtract);
120+
Integer toInteger(); // For COUNT query
119121
SObject toObject();
120122
List<SObject> toList();
121123
List<AggregateResult> toAggregated();
@@ -277,6 +279,11 @@ public inherited sharing class SOQL implements Queryable {
277279
return this;
278280
}
279281

282+
public SOQL with(SObjectField field, String alias) {
283+
builder.fields.with(field, alias);
284+
return this;
285+
}
286+
280287
public SOQL with(String relationshipName, SObjectField field) {
281288
return with(relationshipName, new List<SObjectField>{ field });
282289
}
@@ -480,10 +487,16 @@ public inherited sharing class SOQL implements Queryable {
480487
return builder.toString();
481488
}
482489

483-
public Object toField(SObjectField fieldToExtract) {
490+
public Object toValueOf(SObjectField fieldToExtract) {
491+
builder.fields.clearAllFields(); // other fields not needed
484492
return with(fieldToExtract).toObject()?.get(fieldToExtract);
485493
}
486494

495+
public Set<String> toValuesOf(SObjectField fieldToExtract) {
496+
// https://salesforce.stackexchange.com/questions/393308/get-a-list-of-one-column-from-a-soql-result
497+
return new Map<String, SObject>(with(fieldToExtract, 'Id').groupBy(fieldToExtract).toAggregated()).keySet();
498+
}
499+
487500
public Integer toInteger() {
488501
return executor.toInteger(builder.toString(), binder.getBindingMap());
489502
}
@@ -652,12 +665,19 @@ public inherited sharing class SOQL implements Queryable {
652665
}
653666

654667
public void count(String countSoql) {
655-
// clear all default fields to avoid "Field must be grouped or aggregated"
656-
fields.clear();
668+
// Clear all default fields to avoid "Field must be grouped or aggregated"
669+
clearAllFields();
657670
counts.add(countSoql);
658671
}
659672

673+
public void with(SObjectField field, String alias) {
674+
// Only aggregate expressions use field aliasing. Clear all default fields to avoid "Field must be grouped or aggregated"
675+
clearAllFields();
676+
fields.add(field + ' ' + alias);
677+
}
678+
660679
public void with(String stringFields) {
680+
// To avoid field duplicates in query
661681
fields.addAll(stringFields.deleteWhitespace().split(','));
662682
}
663683

@@ -681,6 +701,10 @@ public inherited sharing class SOQL implements Queryable {
681701
fields.add(relationshipPath + '.' + field);
682702
}
683703

704+
public void clearAllFields() {
705+
fields.clear();
706+
}
707+
684708
public override String toString() {
685709
if (fields.isEmpty() && counts.isEmpty()) {
686710
return 'SELECT Id';
@@ -1119,25 +1143,25 @@ public inherited sharing class SOQL implements Queryable {
11191143
}
11201144

11211145
public Filter includesAll(Iterable<String> iterable) {
1122-
//Bind expressions can't be used with other clauses, such as INCLUDES.
1146+
// Bind expressions can't be used with other clauses, such as INCLUDES.
11231147
skipBinding = true;
11241148
return set('INCLUDES', '(\'' + String.join(iterable, ';') + '\')');
11251149
}
11261150

11271151
public Filter includesSome(Iterable<String> iterable) {
1128-
//Bind expressions can't be used with other clauses, such as INCLUDES.
1152+
// Bind expressions can't be used with other clauses, such as INCLUDES.
11291153
skipBinding = true;
11301154
return set('INCLUDES', '(\'' + String.join(iterable, '\', \'') + '\')');
11311155
}
11321156

11331157
public Filter excludesAll(Iterable<String> iterable) {
1134-
//Bind expressions can't be used with other clauses, such as EXCLUDES.
1158+
// Bind expressions can't be used with other clauses, such as EXCLUDES.
11351159
skipBinding = true;
11361160
return set('EXCLUDES', '(\'' + String.join(iterable, '\', \'') + '\')');
11371161
}
11381162

11391163
public Filter excludesSome(Iterable<String> iterable) {
1140-
//Bind expressions can't be used with other clauses, such as EXCLUDES.
1164+
// Bind expressions can't be used with other clauses, such as EXCLUDES.
11411165
skipBinding = true;
11421166
return set('EXCLUDES', '(\'' + String.join(iterable, ';') + '\')');
11431167
}
@@ -1341,27 +1365,27 @@ public inherited sharing class SOQL implements Queryable {
13411365
}
13421366

13431367
private class Mock {
1344-
private final Map<String, List<SObject>> mocks = new Map<String, List<SObject>>();
1368+
private final Map<String, List<SObject>> sObjectsMocks = new Map<String, List<SObject>>();
13451369
private final Map<String, Integer> countMocks = new Map<String, Integer>();
13461370

13471371
public void setMock(String mockId, List<SObject> records) {
1348-
mocks.put(mockId, records);
1372+
sObjectsMocks.put(mockId, records);
13491373
}
13501374

13511375
public void setCountMock(String mockId, Integer amount) {
13521376
countMocks.put(mockId, amount);
13531377
}
13541378

13551379
public Boolean hasMock(String mockId) {
1356-
return mocks.containsKey(mockId);
1380+
return sObjectsMocks.containsKey(mockId);
13571381
}
13581382

13591383
public Boolean hasCountMock(String mockId) {
13601384
return countMocks.containsKey(mockId);
13611385
}
13621386

1363-
public List<SObject> getMocks(String mockId) {
1364-
return mocks.get(mockId);
1387+
public List<SObject> getSObjectsMock(String mockId) {
1388+
return sObjectsMocks.get(mockId);
13651389
}
13661390

13671391
public Integer getCountMock(String mockId) {
@@ -1396,72 +1420,80 @@ public inherited sharing class SOQL implements Queryable {
13961420
}
13971421

13981422
public SObject toObject(String query, Map<String, Object> binding) {
1399-
try {
1400-
return toList(query, binding)[0];
1401-
} catch (ListException e) {
1402-
return null; // List index out of bounds: 0
1423+
List<SObject> records = toList(query, binding);
1424+
1425+
if (records.size() > 1) {
1426+
QueryException e = new QueryException();
1427+
e.setMessage('List has more than 1 row for assignment to SObject');
1428+
throw e;
14031429
}
1404-
}
14051430

1406-
public Integer toInteger(String query, Map<String, Object> binding) {
1407-
if (mock.hasCountMock(mockId)) {
1408-
return mock.getCountMock(mockId);
1431+
if (records.size() == 0) {
1432+
return null; // handle: List has no rows for assignment to SObject
14091433
}
14101434

1411-
return sharingExecutor.executeCount(query, binding, accessMode);
1435+
return records[0];
14121436
}
14131437

14141438
public List<SObject> toList(String query, Map<String, Object> binding) {
14151439
if (mock.hasMock(mockId)) {
1416-
return mock.getMocks(mockId);
1440+
return mock.getSObjectsMock(mockId);
14171441
}
14181442

14191443
if (accessType == null) {
1420-
return sharingExecutor.execute(query, binding, accessMode);
1444+
return sharingExecutor.toSObjects(query, binding, accessMode);
14211445
}
14221446

14231447
return Security.stripInaccessible(
14241448
accessType,
1425-
sharingExecutor.execute(query, binding, accessMode)
1449+
sharingExecutor.toSObjects(query, binding, accessMode)
14261450
).getRecords();
14271451
}
14281452

1453+
public Integer toInteger(String query, Map<String, Object> binding) {
1454+
if (mock.hasCountMock(mockId)) {
1455+
return mock.getCountMock(mockId);
1456+
}
1457+
1458+
return sharingExecutor.toInteger(query, binding, accessMode);
1459+
}
1460+
14291461
public Database.QueryLocator toQueryLocator(String query, Map<String, Object> binding) {
14301462
return Database.getQueryLocatorWithBinds(query, binding, accessMode);
14311463
}
14321464
}
14331465

14341466
private interface DatabaseQuery {
1435-
List<SObject> execute(String query, Map<String, Object> binding, AccessLevel accessLevel);
1436-
Integer executeCount(String query, Map<String, Object> binding, AccessLevel accessLevel);
1467+
List<SObject> toSObjects(String query, Map<String, Object> binding, AccessLevel accessLevel);
1468+
Integer toInteger(String query, Map<String, Object> binding, AccessLevel accessLevel);
14371469
}
14381470

14391471
private inherited sharing class InheritedSharing implements DatabaseQuery {
1440-
public List<SObject> execute(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1472+
public List<SObject> toSObjects(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14411473
return Database.queryWithBinds(query, binding, accessLevel);
14421474
}
14431475

1444-
public Integer executeCount(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1476+
public Integer toInteger(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14451477
return Database.countQueryWithBinds(query, binding, accessLevel);
14461478
}
14471479
}
14481480

14491481
private without sharing class WithoutSharing implements DatabaseQuery {
1450-
public List<SObject> execute(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1482+
public List<SObject> toSObjects(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14511483
return Database.queryWithBinds(query, binding, accessLevel);
14521484
}
14531485

1454-
public Integer executeCount(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1486+
public Integer toInteger(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14551487
return Database.countQueryWithBinds(query, binding, accessLevel);
14561488
}
14571489
}
14581490

14591491
private with sharing class WithSharing implements DatabaseQuery {
1460-
public List<SObject> execute(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1492+
public List<SObject> toSObjects(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14611493
return Database.queryWithBinds(query, binding, accessLevel);
14621494
}
14631495

1464-
public Integer executeCount(String query, Map<String, Object> binding, AccessLevel accessLevel) {
1496+
public Integer toInteger(String query, Map<String, Object> binding, AccessLevel accessLevel) {
14651497
return Database.countQueryWithBinds(query, binding, accessLevel);
14661498
}
14671499
}

0 commit comments

Comments
 (0)