Skip to content

Commit dad67a4

Browse files
authored
Refactored again - core logic lives in abstract class SObjectRepository
Implement a refactor of my recent refactor. Perhaps create IRefactorFactory for future refactor scalability * Started to refactor my refactor from earlier this week - This is starting to grow bigger than I anticipated, but it's fun, so I'll proceed for now - This refactor is all new classes - the old refactor has been left untouched for now * Changed orderBy() to build store the individual 'order by' field strings, instead of trying to track the field & sort * Simplified how conditions are added - Ran into problems with scope & variable binding, so I'm now taking the approach of delegating the query execution to the concrete SObject repository classes so they can execute the queries with the variables in context. - Tried to show how to leverage the Schema namespace to reference fields so that custom fields can't be deleted when reference by a repo class * Added control for sorting nulls * Fixed a bug with the getLeadById method If only there was a way to write automated tests that could check these things for me... *sign* perhaps one day :-P * Added SObjectRepository.addCommonQueryFields() This adds some of the common SF fields to the set, if they exist for the specified SObject Type * Fixed a comment, added a line break for readability * Forgot to add StringUtils.cls * Minified the enums - no need for them to take up so many lines * Renamed parse to addFieldSetMembers This makes it consistent with addCommonQueryFields() * Added a boolean to disable adding the common fields Only fields in the field set will be included in the query * Relocated and renamed buildQuery() to getQuery() * Updated calls from buildQuery() to getQuery() * Added an example of disabling the common query fields in LeadRepository.cls * Changed which sample static method calls setAsUpdate() * Forgot to commit the latest version of ISObjectRepository.cls * Added a second field set to Lead.object * Forgot to commit CollectionUtils.cls * Made things 47.3% less silly - scrapped using static methods Now, any packaged query methods should be added as public methods. These should call any private/protected builder methods and return the results of the query, casted to the appropriate data type * Changed some method names in LeadRepository.cls Playing around with what naming convention would work best * Added TaskRepository.cls as another example class * Added 'IsClosed' to the list of common fields (available on task, opportunity & case) * Added a comment to LeadRepository.cls to describe usage If you don't want external code to have this control, only have make the LeadRepository() constructor public and internally set the field set to use * Fixed SObjectRepository so FOR UPDATE is only used when there is no ORDER BY I was unintenionally checking limitCount instead of the orderByList * Fixed the getList method so it actually... ya know, returns a list * Deleted the previous implementation of the project because I hate it now * Added CollectionUtils_Test.cls * Updated CollectionUtils_Test.cls to API v38.0 * Added new test classes, finished some existing test classes * Added another test method in LeadRepository_Tests.cls
1 parent 8ca19a1 commit dad67a4

22 files changed

+516
-136
lines changed

src/classes/CollectionUtils.cls

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
public without sharing class CollectionUtils {
2+
3+
public static String toString(List<SObject> recordList) {
4+
List<String> recordIdStringList = new List<String>();
5+
for(SObject record : recordList) recordIdStringList.add(record.Id);
6+
return toString(recordIdStringList);
7+
}
8+
9+
public static String toString(Set<Id> recordIdSet) {
10+
List<String> stringList = new List<String>();
11+
for(Id recordId : new List<Id>(recordIdSet)) stringList.add(recordId);
12+
return toString(stringList);
13+
}
14+
15+
public static String toString(Set<String> stringSet) {
16+
return toString(new List<String>(stringSet));
17+
}
18+
19+
public static String toString(List<String> stringList) {
20+
return toString(stringList, true);
21+
}
22+
23+
public static String toString(List<String> stringList, Boolean wrapWithParentheses) {
24+
List<String> parsedStringList = new List<String>();
25+
for(String str : stringList) parsedStringList.add('\'' + str + '\'');
26+
27+
String parsedString = String.join(parsedStringList, ',');
28+
29+
if(wrapWithParentheses) parsedString = '(' + parsedString + ')';
30+
31+
return parsedString;
32+
}
33+
34+
}
File renamed without changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
@isTest
2+
private class CollectionUtils_Test {
3+
4+
@testSetup
5+
static void setupData() {
6+
List<Lead> leadToInsertList = new List<Lead>();
7+
for(Integer i = 0; i < 5; i++) {
8+
Lead leadToInsert = new Lead(
9+
Company = 'My Test Company',
10+
LastName = 'Gillespie ' + i
11+
);
12+
leadToInsertList.add(leadToInsert);
13+
}
14+
15+
insert leadToInsertList;
16+
}
17+
18+
@isTest
19+
static void toString_WHEN_recordList() {
20+
List<Lead> leadList = [SELECT Id FROM Lead];
21+
List<Id> leadIdList = new List<Id>(new Map<Id, Lead>(leadList).keySet());
22+
23+
String expectedString = '(\'' + String.join(leadIdList, '\',\'') + '\')';
24+
String returnedString = CollectionUtils.toString(leadList);
25+
26+
System.assertEquals(expectedString, returnedString);
27+
}
28+
29+
@isTest
30+
static void toString_WHEN_recordIdSet() {
31+
List<Lead> leadList = [SELECT Id FROM Lead];
32+
List<Id> leadIdList = new List<Id>(new Map<Id, Lead>(leadList).keySet());
33+
34+
String expectedString = '(\'' + String.join(leadIdList, '\',\'') + '\')';
35+
String returnedString = CollectionUtils.toString(new Map<Id, Lead>(leadList).keySet());
36+
37+
System.assertEquals(expectedString, returnedString);
38+
}
39+
40+
@isTest
41+
static void toString_WHEN_recordIdList() {
42+
List<Lead> leadList = [SELECT Id FROM Lead];
43+
List<Id> leadIdList = new List<Id>(new Map<Id, Lead>(leadList).keySet());
44+
45+
String expectedString = '(\'' + String.join(leadIdList, '\',\'') + '\')';
46+
String returnedString = CollectionUtils.toString(leadIdList);
47+
48+
System.assertEquals(expectedString, returnedString);
49+
}
50+
51+
@isTest
52+
static void toString_WHEN_stringSet() {
53+
Set<String> stringSet = new Set<String>{'Hola', 'Hello'};
54+
55+
String expectedString = '(\'' + String.join(new List<String>(stringSet), '\',\'') + '\')';
56+
String returnedString = CollectionUtils.toString(stringSet);
57+
58+
System.assertEquals(expectedString, returnedString);
59+
}
60+
61+
@isTest
62+
static void toString_WHEN_stringList() {
63+
List<String> stringList = new List<String>{'Hola', 'Hello'};
64+
65+
String expectedString = '(\'' + String.join(stringList, '\',\'') + '\')';
66+
String returnedString = CollectionUtils.toString(stringList);
67+
68+
System.assertEquals(expectedString, returnedString);
69+
}
70+
71+
}
File renamed without changes.

src/classes/ISObjectRepository.cls

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public interface ISObjectRepository {
2+
3+
SObject getRecord(Id recordId);
4+
List<SObject> getList(List<Id> recordIdList);
5+
6+
}
File renamed without changes.

src/classes/LeadQueryRepository.cls

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/classes/LeadRepository.cls

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
public without sharing class LeadRepository extends SObjectRepository {
2+
3+
private static final Schema.FieldSet DEFAULT_FIELD_SET = SObjectType.Lead.FieldSets.MyFieldSet;
4+
5+
public LeadRepository() {
6+
super(LeadRepository.DEFAULT_FIELD_SET);
7+
}
8+
9+
// Overload the constructor if you want to allow other code to specify the field set used
10+
public LeadRepository(Schema.FieldSet fieldSet, Boolean addCommonQueryFields) {
11+
super(fieldSet, addCommonQueryFields);
12+
}
13+
14+
// ISObjectRepository requires at least 2 methods, getRecord & getList
15+
public Lead getRecord(Id leadId) {
16+
String query = this
17+
.addConditionIdEquals(leadId)
18+
.setAsUpdate(true)
19+
.getQuery();
20+
21+
return (Lead)Database.query(query)[0];
22+
}
23+
24+
public List<Lead> getList(List<Id> leadIdList) {
25+
String query = this
26+
.addConditionIdIn(leadIdList)
27+
.setAsUpdate(true)
28+
.getQuery();
29+
30+
return (List<Lead>)Database.query(query);
31+
}
32+
33+
// Add public methods needed that return the query results
34+
// Only methods that return an SObject or collection of SObjects should be made public
35+
public List<Lead> getListForSources(List<String> leadSourceList) {
36+
String query = this
37+
.addCondition(Schema.Lead.LeadSource + ' IN ' + CollectionUtils.toString(leadSourceList))
38+
.orderBy(Schema.Lead.CreatedDate)
39+
.getQuery();
40+
41+
return (List<Lead>)Database.query(query);
42+
}
43+
44+
public List<Lead> getListForStatus(String status, Integer limitCount) {
45+
String query = this
46+
.addConditionIsConverted(false)
47+
.addConditionStatusEquals(status)
48+
.limitCount(limitCount)
49+
.orderBy(Schema.Lead.LastModifiedDate, SObjectRepository.SortOrder.DESCENDING)
50+
.setAsUpdate(true)
51+
.getQuery();
52+
53+
return (List<Lead>)Database.query(query);
54+
}
55+
56+
// You can add additional builder methods for any commonly used filters for this SObject
57+
// All builder methods should be kept as private or protected
58+
private LeadRepository addConditionIsConverted(Boolean bool) {
59+
return (LeadRepository)this.addCondition(Schema.Lead.IsConverted + ' = ' + bool);
60+
}
61+
62+
private LeadRepository addConditionStatusEquals(String status) {
63+
return (LeadRepository)this.addCondition(Schema.Lead.Status + ' = ' + StringUtils.wrapInSingleQuotes(status));
64+
}
65+
66+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>38.0</apiVersion>
4+
<status>Active</status>
5+
</ApexClass>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
@isTest
2+
private class LeadRepository_Tests {
3+
4+
@testSetup
5+
static void setup() {
6+
List<Lead> leadList = new List<Lead>();
7+
for(Integer i = 0; i < 5; i++) {
8+
Lead lead = new Lead(
9+
Company = 'My Test Company',
10+
LastName = 'Gillespie'
11+
);
12+
leadList.add(lead);
13+
}
14+
insert leadList;
15+
}
16+
17+
@isTest
18+
static void getRecord() {
19+
Lead expectedLead = [SELECT Id FROM Lead LIMIT 1];
20+
21+
Test.startTest();
22+
23+
Lead returnedLead = new LeadRepository().getRecord(expectedLead.Id);
24+
System.assertEquals(expectedLead.Id, returnedLead.Id);
25+
26+
Test.stopTest();
27+
}
28+
29+
@isTest
30+
static void getRecord_WHEN_fieldSetIsSpecified() {
31+
Schema.FieldSet expectedFieldSet = SObjectType.Lead.FieldSets.AnotherFieldSet;
32+
Lead expectedLead = [SELECT Id FROM Lead LIMIT 1];
33+
34+
Test.startTest();
35+
36+
Lead returnedLead = new LeadRepository(expectedFieldSet, false).getRecord(expectedLead.Id);
37+
System.assertEquals(expectedLead.Id, returnedLead.Id);
38+
39+
Test.stopTest();
40+
}
41+
42+
@isTest
43+
static void getList() {
44+
List<Lead> expectedLeadList = [SELECT Id FROM Lead];
45+
List<Id> expectedLeadIdList = new List<Id>(new Map<Id, Lead>(expectedLeadList).keySet());
46+
47+
Test.startTest();
48+
49+
List<Lead> returnedLeadList = new LeadRepository().getList(expectedLeadIdList);
50+
System.assertEquals(expectedLeadList.size(), returnedLeadList.size());
51+
52+
Test.stopTest();
53+
}
54+
55+
@isTest
56+
static void getListForSources() {
57+
String expectedLeadSource = 'GitHub';
58+
59+
List<Lead> leadList = [SELECT Id, LeadSource FROM Lead LIMIT 2];
60+
for(Lead lead : leadList) lead.LeadSource = expectedLeadSource;
61+
update leadList;
62+
63+
Integer leadCountForExpectedLeadSource = [SELECT COUNT() FROM Lead WHERE LeadSource = :expectedLeadSource];
64+
65+
Test.startTest();
66+
67+
List<Lead> returnedLeadList = new LeadRepository().getListForSources(new List<String>{expectedLeadSource});
68+
System.assertEquals(leadCountForExpectedLeadSource, returnedLeadList.size());
69+
70+
Test.stopTest();
71+
}
72+
73+
@isTest
74+
static void getListForStatus() {
75+
String expectedStatus = [SELECT Status FROM Lead LIMIT 1].Status;
76+
Integer leadCountForExpectedStatus = [SELECT COUNT() FROM Lead WHERE Status = :expectedStatus];
77+
System.assert(leadCountForExpectedStatus > 0);
78+
Integer limitCount = leadCountForExpectedStatus - 1;
79+
80+
Test.startTest();
81+
82+
List<Lead> returnedLeadList = new LeadRepository().getListForStatus(expectedStatus, limitCount);
83+
System.assertEquals(limitCount, returnedLeadList.size());
84+
85+
Test.stopTest();
86+
}
87+
88+
}

0 commit comments

Comments
 (0)