Skip to content

Commit 64feb15

Browse files
pgajek2patrykaccsalberski
authored
release/v4.2.0 (#146)
* refactoring (#145) Signed-off-by: Piotr PG Gajek <[email protected]> * toLabel with alias (#148) * toLabel with alias Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * 144 tomap for fields from a lookup relationship (#149) * toMap draft Signed-off-by: Piotr PG Gajek <[email protected]> * toMap with related field Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * Feature/refactoring (#150) * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Refactoring Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * Feature/refactoring (#151) * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring + pmd Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * Code Coverage Signed-off-by: Piotr PG Gajek <[email protected]> * Implemented invalidateRecords() + Unit tests (#143) * Implemented invalidateRecords() + tests * Changed the way how entries are removed from cache * Changed the way how sobject type is deferred to assume there is collection of single type passed in. Refactoring. * Changed key in unit tests + removed static string key * Changed unit test object to profile * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Fix Unit Test Signed-off-by: Piotr PG Gajek <[email protected]> * invalidateRecords documentation Signed-off-by: Piotr PG Gajek <[email protected]> * calculate total number of queries issued [DRAFT] (#134) * calculate total number of queries issued Signed-off-by: Piotr PG Gajek <[email protected]> * queryIssued Signed-off-by: Piotr PG Gajek <[email protected]> * QueryException Signed-off-by: Piotr PG Gajek <[email protected]> * synchronous query issued count Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> * refactoring Signed-off-by: Piotr PG Gajek <[email protected]> * Fix validation Signed-off-by: Piotr PG Gajek <[email protected]> * Dynamic Query Improvements: aggregate function exceptions, Filter Group bulkification, SubQuery dynamic order (#153) * refactoring + additional tests Signed-off-by: Piotr PG Gajek <[email protected]> * documentation Signed-off-by: Piotr PG Gajek <[email protected]> * additional test Signed-off-by: Piotr PG Gajek <[email protected]> * dynamic sort Signed-off-by: Piotr PG Gajek <[email protected]> * removeFromCache Signed-off-by: Piotr PG Gajek <[email protected]> * fix Signed-off-by: Piotr PG Gajek <[email protected]> * fix Signed-off-by: Piotr PG Gajek <[email protected]> --------- Signed-off-by: Piotr PG Gajek <[email protected]> Co-authored-by: patrykacc <[email protected]> Co-authored-by: Sebastian Alberski <[email protected]>
1 parent f87e473 commit 64feb15

File tree

8 files changed

+1290
-529
lines changed

8 files changed

+1290
-529
lines changed

force-app/main/default/classes/main/cached-soql/SOQLCache.cls

Lines changed: 67 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
6161
mock.setMock(mockId, new List<SObject>{ record });
6262
}
6363

64+
public static void removeFromCache(List<SObject> records) {
65+
if (records.isEmpty()) {
66+
return;
67+
}
68+
69+
String ofObject = records[0].getSObjectType().toString();
70+
71+
// Record deletion will trigger an automatic cache refresh when the query is executed.
72+
new CacheStorageProxy(ofObject).apexTransaction().removeRecordsFromCache(records);
73+
new CacheStorageProxy(ofObject).orgCache().removeRecordsFromCache(records);
74+
new CacheStorageProxy(ofObject).sessionCache().removeRecordsFromCache(records);
75+
}
76+
6477
public static Cacheable of(SObjectType ofObject) {
6578
return new SOQLCache(ofObject);
6679
}
@@ -94,17 +107,17 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
94107
}
95108

96109
public Cacheable cacheInApexTransaction() {
97-
this.cache.storage.cacheInApexTransaction();
110+
this.cache.storage.apexTransaction();
98111
return this;
99112
}
100113

101114
public Cacheable cacheInOrgCache() {
102-
this.cache.storage.cacheInOrgCache();
115+
this.cache.storage.orgCache();
103116
return this;
104117
}
105118

106119
public Cacheable cacheInSessionCache() {
107-
this.cache.storage.cacheInSessionCache();
120+
this.cache.storage.sessionCache();
108121
return this;
109122
}
110123

@@ -223,16 +236,19 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
223236
this.ofObject = ofObject;
224237
}
225238

226-
public void cacheInApexTransaction() {
239+
public CacheStorageProxy apexTransaction() {
227240
this.storage = CacheManager.ApexTransaction;
241+
return this;
228242
}
229243

230-
public void cacheInOrgCache() {
244+
public CacheStorageProxy orgCache() {
231245
this.storage = CacheManager.SOQLOrgCache;
246+
return this;
232247
}
233248

234-
public void cacheInSessionCache() {
249+
public CacheStorageProxy sessionCache() {
235250
this.storage = CacheManager.SOQLSessionCache;
251+
return this;
236252
}
237253

238254
public void putInitialRecordsToCache(List<SObject> records) {
@@ -246,19 +262,54 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
246262
}
247263

248264
public void putRecordsToCache(List<CacheItem> records) {
249-
this.storage.put(this.key(), records);
265+
this.storage.put(this.ofObject, records);
250266
}
251267

252268
public Boolean hasCachedRecords() {
253-
return this.storage.contains(this.key());
269+
return this.storage.contains(this.ofObject);
254270
}
255271

256272
public List<CacheItem> getCachedRecords() {
257-
return (List<CacheItem>) (this.storage.get(this.key()) ?? new List<CacheItem>());
273+
return (List<CacheItem>) (this.storage.get(this.ofObject) ?? new List<CacheItem>());
258274
}
259275

260-
private String key() {
261-
return Test.isRunningTest() ? this.ofObject + 'Test' : this.ofObject;
276+
public void addRecordsToCache(List<SObject> recordsToAdd) {
277+
List<CacheItem> allCachedRecords = getCachedRecords();
278+
279+
for (SObject databaseRecord : recordsToAdd) {
280+
allCachedRecords.add(new CacheItem(databaseRecord));
281+
}
282+
283+
putRecordsToCache(allCachedRecords);
284+
}
285+
286+
public void updateRecordsInCache(List<SObject> recordsToUpdate) {
287+
List<CacheItem> allCachedRecords = getCachedRecords();
288+
289+
for (SObject updatedRecord : recordsToUpdate) {
290+
for (CacheItem cachedRecord : allCachedRecords) {
291+
if (updatedRecord.Id == cachedRecord.id) {
292+
cachedRecord.record = updatedRecord;
293+
cachedRecord.cachedDate = System.now();
294+
}
295+
}
296+
}
297+
298+
putRecordsToCache(allCachedRecords);
299+
}
300+
301+
public void removeRecordsFromCache(List<SObject> recordsToRemove) {
302+
Set<Id> recordsToRemoveIds = new Map<Id, SObject>(recordsToRemove).keySet();
303+
List<CacheItem> allCachedRecords = getCachedRecords();
304+
List<CacheItem> filteredCachedItems = new List<CacheItem>();
305+
306+
for (CacheItem cachedRecord : allCachedRecords) {
307+
if (!recordsToRemoveIds.contains(cachedRecord.id)) {
308+
filteredCachedItems.add(cachedRecord);
309+
}
310+
}
311+
312+
putRecordsToCache(filteredCachedItems);
262313
}
263314
}
264315

@@ -296,12 +347,14 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
296347
}
297348

298349
public void save(List<SObject> databaseRecords) {
350+
List<CacheItem> newCacheRecords = new List<CacheItem>();
351+
299352
if (this.isRecordMissingFromCache()) {
300-
new InsertAction().execute(this.storage, databaseRecords);
353+
storage.addRecordsToCache(databaseRecords);
301354
} else if (databaseRecords.isEmpty()) { // record does not exist in database anymore
302-
new DeleteAction().execute(this.storage, this.toList());
355+
storage.removeRecordsFromCache(this.toList());
303356
} else if (this.areRequestedFieldsMissing() || this.areRecordsOutdated()) {
304-
new UpdateAction().execute(this.storage, databaseRecords);
357+
storage.updateRecordsInCache(databaseRecords);
305358
}
306359
}
307360

@@ -318,60 +371,6 @@ public virtual inherited sharing class SOQLCache implements Cacheable {
318371
}
319372
}
320373

321-
private interface CacheAction {
322-
void execute(CacheStorageProxy storage, List<SObject> records);
323-
}
324-
325-
private class InsertAction implements CacheAction {
326-
public void execute(CacheStorageProxy storage, List<SObject> records) {
327-
List<CacheItem> allObjectCachedRecords = storage.getCachedRecords();
328-
329-
for (SObject databaseRecord : records) {
330-
allObjectCachedRecords.add(new CacheItem(databaseRecord));
331-
}
332-
333-
storage.putRecordsToCache(allObjectCachedRecords);
334-
}
335-
}
336-
337-
private class UpdateAction implements CacheAction {
338-
public void execute(CacheStorageProxy storage, List<SObject> records) {
339-
List<CacheItem> allObjectCachedRecords = storage.getCachedRecords();
340-
341-
for (SObject updatedRecord : records) {
342-
for (CacheItem cachedRecord : allObjectCachedRecords) {
343-
if (updatedRecord.Id == cachedRecord.id) {
344-
cachedRecord.record = updatedRecord;
345-
cachedRecord.cachedDate = System.now();
346-
}
347-
}
348-
}
349-
350-
storage.putRecordsToCache(allObjectCachedRecords);
351-
}
352-
}
353-
354-
private class DeleteAction implements CacheAction {
355-
public void execute(CacheStorageProxy storage, List<SObject> records) {
356-
List<CacheItem> allObjectCachedRecords = storage.getCachedRecords();
357-
358-
Set<Id> recordsToRemoveFromCacheIds = new Set<Id>();
359-
360-
for (SObject obsoleteCachedRecord : records) {
361-
recordsToRemoveFromCacheIds.add(obsoleteCachedRecord.id);
362-
}
363-
364-
List<CacheItem> clearedCacheItems = new List<CacheItem>();
365-
for (CacheItem cachedRecord : allObjectCachedRecords) {
366-
if (!recordsToRemoveFromCacheIds.contains(cachedRecord.id)) {
367-
clearedCacheItems.add(cachedRecord);
368-
}
369-
}
370-
371-
storage.putRecordsToCache(clearedCacheItems);
372-
}
373-
}
374-
375374
@TestVisible
376375
private class CacheItem {
377376
public Id id;

force-app/main/default/classes/main/cached-soql/SOQLCache_Test.cls

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private class SOQLCache_Test {
2424

2525
// Test
2626
new SOQL_ProfileCache().whereEqual('Name', 'System Administrator').toObject(); // initial query will be executed
27-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
27+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
2828

2929
// Verify
3030
Assert.areEqual(mockedProfiles.size(), cachedProfiles.size(), 'The cached profiles should be identical to those in the initial query.');
@@ -34,7 +34,7 @@ private class SOQLCache_Test {
3434
static void noInitialQuery() {
3535
// Test
3636
new SOQL_ProfileCacheDefault().whereEqual('Name', 'System Administrator').toObject();
37-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
37+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
3838
Profile cachedProfile = (Profile) cachedProfiles[0].record;
3939

4040
// Verify
@@ -75,7 +75,7 @@ private class SOQLCache_Test {
7575
.whereEqual(Profile.Name, 'System Administrator')
7676
.toObject();
7777

78-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
78+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
7979
Profile cachedProfile = (Profile) cachedProfiles[0].record;
8080

8181
// Verify
@@ -93,7 +93,7 @@ private class SOQLCache_Test {
9393
.whereEqual(Profile.Name, 'System Administrator')
9494
.toObject();
9595

96-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('ProfileTest');
96+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('Profile');
9797
Profile cachedProfile = (Profile) cachedProfiles[0].record;
9898

9999
// Verify
@@ -111,7 +111,7 @@ private class SOQLCache_Test {
111111
.whereEqual(Profile.Name, 'System Administrator')
112112
.toObject();
113113

114-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.SOQLSessionCache.get('ProfileTest');
114+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.SOQLSessionCache.get('Profile');
115115
Profile cachedProfile = (Profile) cachedProfiles[0].record;
116116

117117
// Verify
@@ -127,7 +127,7 @@ private class SOQLCache_Test {
127127
SELECT Id, Name FROM Profile WHERE Name = 'System Administrator' LIMIT 1
128128
]);
129129

130-
CacheManager.ApexTransaction.put('ProfileTest', new List<SOQLCache.CacheItem>{ cachedItem });
130+
CacheManager.ApexTransaction.put('Profile', new List<SOQLCache.CacheItem>{ cachedItem });
131131

132132
// Test
133133
Profile profile = (Profile) SOQLCache.of(Profile.SObjectType)
@@ -150,7 +150,7 @@ private class SOQLCache_Test {
150150
]);
151151
cachedItem.cachedDate = DateTime.now().addHours(-6);
152152

153-
CacheManager.ApexTransaction.put('ProfileTest', new List<SOQLCache.CacheItem>{ cachedItem });
153+
CacheManager.ApexTransaction.put('Profile', new List<SOQLCache.CacheItem>{ cachedItem });
154154

155155
// Test
156156
Profile profile = (Profile) SOQLCache.of(Profile.SObjectType)
@@ -159,12 +159,12 @@ private class SOQLCache_Test {
159159
.maxHoursWithoutRefresh(3)
160160
.toObject();
161161

162-
List<SOQLCache.CacheItem> updatedCacheItems = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
162+
List<SOQLCache.CacheItem> updatedCacheItems = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
163163

164164
// Verify
165165
Assert.areEqual(2, Limits.getQueries(), 'Two queries should be issued. The second query should update cached record.');
166166
Assert.areEqual(1, updatedCacheItems.size(), 'The Apex transaction cache should contain exactly one record.');
167-
Assert.areEqual(System.now(), updatedCacheItems[0].cachedDate, 'The cached record should be updated.');
167+
Assert.isTrue(Math.abs((System.now().getTime() - updatedCacheItems[0].cachedDate.getTime()) / 1000) < 10, 'The cached record should be updated. The time difference should be less than 10 seconds.');
168168
Assert.isNotNull(profile, 'Profile should be not null.');
169169
Assert.areEqual('System Administrator', profile.Name, 'The cached profile record should be "System Administrator".');
170170
}
@@ -176,7 +176,7 @@ private class SOQLCache_Test {
176176
cachedItem1.cachedDate = DateTime.now().addHours(-6);
177177
SOQLCache.CacheItem cachedItem2 = new SOQLCache.CacheItem(new Profile(Id = '00e3V000000Nme3QAD', Name = 'System Administrator'));
178178

179-
CacheManager.ApexTransaction.put('ProfileTest', new List<SOQLCache.CacheItem>{ cachedItem1, cachedItem2 });
179+
CacheManager.ApexTransaction.put('Profile', new List<SOQLCache.CacheItem>{ cachedItem1, cachedItem2 });
180180

181181
// Test
182182
Profile profile = (Profile) SOQLCache.of(Profile.SObjectType)
@@ -185,7 +185,7 @@ private class SOQLCache_Test {
185185
.maxHoursWithoutRefresh(3)
186186
.toObject();
187187

188-
List<SOQLCache.CacheItem> updatedCacheItems = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
188+
List<SOQLCache.CacheItem> updatedCacheItems = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
189189
Profile cachedProfile = (Profile) updatedCacheItems[0].record;
190190

191191
// Verify
@@ -353,7 +353,7 @@ private class SOQLCache_Test {
353353
@IsTest
354354
static void stripInaccessible() {
355355
// Setup
356-
Task testTask = new Task(Subject = 'Test', Type = 'Other');
356+
Task testTask = new Task(Subject = 'Test Task', Type = 'Other');
357357
insert testTask;
358358

359359
System.runAs(minimumAccessUser()) {
@@ -545,7 +545,7 @@ private class SOQLCache_Test {
545545
.whereEqual(Profile.Name, 'System Administrator')
546546
.toObject();
547547

548-
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('ProfileTest');
548+
List<SOQLCache.CacheItem> cachedProfiles = (List<SOQLCache.CacheItem>) CacheManager.ApexTransaction.get('Profile');
549549

550550
// Verify
551551
Assert.areEqual(2, Limits.getQueries(), 'Two queries should be issued. The second query should retrieve the missing fields.');
@@ -559,6 +559,48 @@ private class SOQLCache_Test {
559559
Assert.isTrue(profile2.isSet('UserLicenseId'), 'The profile UserLicenseId should not be set.');
560560
}
561561

562+
@IsTest
563+
static void recordsClearedFromCache() {
564+
// Setup
565+
Profile profile = (Profile) SOQLCache.of(Profile.SObjectType)
566+
.with(Profile.Id, Profile.Name)
567+
.whereEqual(Profile.Name, 'System Administrator')
568+
.cacheInOrgCache()
569+
.toObject();
570+
571+
// Verify initial setup
572+
Assert.isTrue(CacheManager.SOQLOrgCache.contains('Profile'), 'Key should exist.');
573+
Assert.isFalse(((List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('Profile')).isEmpty(), 'Cache item should be present.');
574+
575+
// Test
576+
SOQLCache.removeFromCache(new List<Profile>{ profile });
577+
578+
// Verify
579+
Assert.isTrue(CacheManager.SOQLOrgCache.contains('Profile'), 'Key should still exist.');
580+
Assert.isTrue(((List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('Profile')).isEmpty(), 'Cache items should be empty.');
581+
}
582+
583+
@IsTest
584+
static void emptyRecordsClearedFromCache() {
585+
// Setup
586+
Profile profile = (Profile) SOQLCache.of(Profile.SObjectType)
587+
.with(Profile.Id, Profile.Name)
588+
.whereEqual(Profile.Name, 'System Administrator')
589+
.cacheInOrgCache()
590+
.toObject();
591+
592+
// Verify initial setup
593+
Assert.isTrue(CacheManager.SOQLOrgCache.contains('Profile'), 'Key should exist.');
594+
Assert.isFalse(((List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('Profile')).isEmpty(), 'Cache item should be present.');
595+
596+
// Test
597+
SOQLCache.removeFromCache(new List<Profile>());
598+
599+
// Verify
600+
Assert.isTrue(CacheManager.SOQLOrgCache.contains('Profile'), 'Key should still exist.');
601+
Assert.isFalse(((List<SOQLCache.CacheItem>) CacheManager.SOQLOrgCache.get('Profile')).isEmpty(), 'Cache items should be not empty.');
602+
}
603+
562604
static User minimumAccessUser() {
563605
return new User(
564606
Alias = 'newUser',

0 commit comments

Comments
 (0)