Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.6.29 - Grandparent rollup full recalc bugfixes #601

Merged
merged 4 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ sfge-*.log.gz
.pmdCache
#OSX
.DS_Store
.config
.config
.vscode
5 changes: 0 additions & 5 deletions .vscode/settings.json

This file was deleted.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ As well, don't miss [the Wiki](../../wiki), which includes even more info for co

## Deployment & Setup

<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObN8AAK">
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNNAA0">
<img alt="Deploy to Salesforce"
src="./media/deploy-package-to-prod.png">
</a>

<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObN8AAK">
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNNAA0">
<img alt="Deploy to Salesforce Sandbox"
src="./media/deploy-package-to-sandbox.png">
</a>
Expand Down
45 changes: 45 additions & 0 deletions extra-tests/classes/RollupFullRecalcTests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,51 @@ private class RollupFullRecalcTests {
System.assertEquals(events[0].Subject + ', ' + events[1].Subject, updatedUser.AboutMe, 'Grandparent rollup should have worked!');
}

@IsTest
static void shouldMapChildrenWithDifferentGrandparentWhereClauses() {
Account acc = [SELECT Id, OwnerId FROM Account];
List<Event> events = new List<Event>{
new Event(WhatId = acc.Id, Subject = 'Two', ActivityDatetime = System.now(), DurationInMinutes = 30),
new Event(WhatId = acc.Id, Subject = 'One', ActivityDatetime = System.now(), DurationInMinutes = 60)
};
insert events;

Rollup.defaultControl = Rollup.getDefaultControl();
Rollup.defaultControl.ShouldSkipResettingParentFields__c = true;
Rollup.rollupMetadata = new List<Rollup__mdt>{
new Rollup__mdt(
CalcItem__c = 'Event',
RollupFieldOnCalcItem__c = 'Subject',
LookupFieldOnCalcItem__c = 'WhatId',
LookupObject__c = 'User',
LookupFieldOnLookupObject__c = 'Id',
RollupFieldOnLookupObject__c = 'AboutMe',
RollupOperation__c = 'CONCAT',
GrandparentRelationshipFieldPath__c = 'What.Owner.AboutMe',
CalcItemWhereClause__c = 'DurationInMinutes <= 30'
),
new Rollup__mdt(
CalcItem__c = 'Event',
RollupFieldOnCalcItem__c = 'Subject',
LookupFieldOnCalcItem__c = 'WhatId',
LookupObject__c = 'User',
LookupFieldOnLookupObject__c = 'Id',
RollupFieldOnLookupObject__c = 'CompanyName',
RollupOperation__c = 'CONCAT',
GrandparentRelationshipFieldPath__c = 'What.Owner.CompanyName',
CalcItemWhereClause__c = 'DurationInMinutes > 30'
)
};

Test.startTest();
Rollup.performBulkFullRecalc(Rollup.rollupMetadata, Rollup.InvocationPoint.FROM_FULL_RECALC_LWC.name());
Test.stopTest();

User updatedUser = [SELECT AboutMe, CompanyName FROM User WHERE Id = :acc.OwnerId];
System.assertEquals(events[0].Subject, updatedUser.AboutMe);
System.assertEquals(events[1].Subject, updatedUser.CompanyName);
}

@IsTest
static void shouldAllowGrandparentRollupFromParentWithPolymorphicFields() {
// technically shouldAllowGrandparentRollupsFromParent also tests polymorphic fields
Expand Down
23 changes: 23 additions & 0 deletions extra-tests/classes/RollupRelationshipFieldFinderTests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,29 @@ private class RollupRelationshipFieldFinderTests {
System.assertEquals(true, traversal.getIsFinished());
}

@IsTest
static void fullRecalcDoesNotBlowUpOnDynamicParentFields() {
Account acc = [SELECT Id, OwnerId, Owner.Name FROM Account];

ContactPointAddress child = new ContactPointAddress(ParentId = acc.Id, Name = 'Child');
insert child;

RollupRelationshipFieldFinder finder = new RollupRelationshipFieldFinder(
control,
new Rollup__mdt(GrandparentRelationshipFieldPath__c = 'Parent.Owner.Name'),
new Set<String>{ 'ParentId, Name' },
uniqueFieldNames,
User.SObjectType,
new Map<Id, SObject>()
);
finder.setIsFullRecalc(true);
RollupRelationshipFieldFinder.Traversal traversal = finder.getParents(new List<ContactPointAddress>{ child });

System.assertEquals(true, traversal.getParentLookupToRecords().containsKey(acc.OwnerId));
System.assertEquals(acc.Owner.Name, traversal.retrieveParents(child.Id)[0].get('Name'));
System.assertEquals(true, traversal.getIsFinished());
}

@IsTest
static void shouldNotReportFalsePositiveIfUltimateParentStaysTheSame() {
Account intermediateOne = new Account(Name = 'Intermediate 1');
Expand Down
3 changes: 0 additions & 3 deletions extra-tests/classes/RollupTests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -2899,9 +2899,6 @@ private class RollupTests {
insert cpas;

RollupTestUtils.DMLMock mock = RollupTestUtils.loadMock(cpas);
Rollup.defaultControl = new RollupControl__mdt(MaxNumberOfQueries__c = 2, MaxRollupRetries__c = 100);
// start as synchronous rollup to allow for one deferral
Rollup.specificControl = new RollupControl__mdt(ShouldRunAs__c = RollupMetaPicklists.ShouldRunAs.Synchronous, MaxNumberOfQueries__c = 2);
Rollup.rollupMetadata = new List<Rollup__mdt>{
new Rollup__mdt(
CalcItem__c = 'ContactPointAddress',
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apex-rollup",
"version": "1.6.28",
"version": "1.6.29",
"description": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -58,4 +58,4 @@
"test:apex": "sh ./scripts/runLocalTests.sh",
"test:lwc": "sfdx-lwc-jest --coverage --skipApiVersionCheck"
}
}
}
4 changes: 2 additions & 2 deletions rollup-namespaced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ For more info, see the base `README`.

## Deployment & Setup

<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNDAA0">
<a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNSAA0">
<img alt="Deploy to Salesforce"
src="./media/deploy-package-to-prod.png">
</a>

<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNDAA0">
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6g000008ObNSAA0">
<img alt="Deploy to Salesforce Sandbox"
src="./media/deploy-package-to-sandbox.png">
</a>
7 changes: 4 additions & 3 deletions rollup-namespaced/sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"default": true,
"package": "apex-rollup-namespaced",
"path": "rollup-namespaced/source/rollup",
"versionName": "Summer 24 release",
"versionNumber": "1.1.22.0",
"versionName": "Grandparent full recalc bugfixes",
"versionNumber": "1.1.23.0",
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
"unpackagedMetadata": {
Expand All @@ -25,6 +25,7 @@
"apex-rollup-namespaced@1.1.19": "04t6g000008ObCsAAK",
"apex-rollup-namespaced@1.1.20": "04t6g000008ObCxAAK",
"apex-rollup-namespaced@1.1.21": "04t6g000008ObLCAA0",
"apex-rollup-namespaced@1.1.22": "04t6g000008ObNDAA0"
"apex-rollup-namespaced@1.1.22": "04t6g000008ObNDAA0",
"apex-rollup-namespaced@1.1.23": "04t6g000008ObNSAA0"
}
}
25 changes: 12 additions & 13 deletions rollup/core/classes/RollupAsyncProcessor.cls
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
RollupRepository repo = new RollupRepository(this.runAsMode);
if (this.rollups.isEmpty() == false) {
RollupAsyncProcessor firstRollup = this.rollups.get(0);
repo.setArg(this.getCalcItemsByLookupField(firstRollup).keySet());
repo.setArg(this.getCalcItemsByLookupField(firstRollup, null).keySet());
query = RollupQueryBuilder.Current.getQuery(
firstRollup.lookupObj,
new List<String>(this.lookupObjectToUniqueFieldNames.get(firstRollup.lookupObj)),
Expand Down Expand Up @@ -713,12 +713,12 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
continue;
}

String grandparentKey = '' + roll.lookupObj + roll.calcItemType + (roll.metadata.CalcItemWhereClause__c ?? '');
if (String.isNotBlank(roll.metadata.GrandparentRelationshipFieldPath__c) && grandparentRollups.containsKey(grandparentKey)) {
String grandparentKey = '' + roll.lookupObj + roll.calcItemType + (roll.metadata.CalcItemWhereClause__c?.trim() ?? '');
if (String.isNotBlank(roll.metadata.GrandparentRelationshipFieldPath__c) && grandparentRollups.containsKey(grandparentKey) && roll.traversal == null) {
roll.traversal = grandparentRollups.get(grandparentKey);
}

Map<String, CalcItemBag> calcItemsByLookupField = this.getCalcItemsByLookupField(roll);
Map<String, CalcItemBag> calcItemsByLookupField = this.getCalcItemsByLookupField(roll, grandparentKey);
// some rollups may not finish retrieving all parent rows the first time around - and that's ok! we can keep
// trying until all necessary records have been retrieved
if (roll.traversal?.getIsFinished() == false) {
Expand Down Expand Up @@ -945,12 +945,12 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
rolls.sort();
}

private Map<String, CalcItemBag> getCalcItemsByLookupField(RollupAsyncProcessor roll) {
private Map<String, CalcItemBag> getCalcItemsByLookupField(RollupAsyncProcessor roll, String cacheKey) {
Map<String, CalcItemBag> lookupFieldToCalcItems = new Map<String, CalcItemBag>();
if (roll.calcItems == null) {
return new Map<String, CalcItemBag>();
return lookupFieldToCalcItems;
}

String cacheKey = '' + roll.lookupObj + roll.lookupFieldOnCalcItem;
switch on this.invokePoint {
when FROM_FULL_RECALC_LWC, FROM_SINGULAR_PARENT_RECALC_LWC, FROM_FULL_RECALC_FLOW {
Map<String, CalcItemBag> possiblyCachedItems = CACHED_CALC_ITEM_BAGS.get(cacheKey);
Expand All @@ -960,10 +960,9 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
}
}

Map<String, CalcItemBag> lookupFieldToCalcItems = new Map<String, CalcItemBag>();
if (String.isNotBlank(roll.metadata.GrandparentRelationshipFieldPath__c) || roll.metadata.RollupToUltimateParent__c) {
if (roll.traversal == null) {
roll.traversal = new RollupRelationshipFieldFinder(
roll.traversal = roll.traversal ??
new RollupRelationshipFieldFinder(
roll.rollupControl,
roll.metadata,
this.calcObjectToUniqueFieldNames.get(roll.calcItemType),
Expand All @@ -974,11 +973,11 @@ global virtual without sharing class RollupAsyncProcessor extends Rollup impleme
.setIsFullRecalc(this.isFullRecalc)
.setOldIntermediateGrandparents(this.fullRecalcProcessor?.getOldIntermediateGrandparents())
.getParents(roll.calcItems);
} else if (roll.traversal?.getIsFinished() == false) {
roll.traversal.recommence();
}

if (roll.traversal.getIsFinished()) {
lookupFieldToCalcItems = roll.traversal.getParentLookupToRecords();
} else {
roll.traversal.recommence();
}
} else {
for (SObject calcItem : roll.calcItems) {
Expand Down
2 changes: 1 addition & 1 deletion rollup/core/classes/RollupLogger.cls
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
global without sharing virtual class RollupLogger implements ILogger {
@TestVisible
// this gets updated via the pipeline as the version number gets incremented
private static final String CURRENT_VERSION_NUMBER = 'v1.6.28';
private static final String CURRENT_VERSION_NUMBER = 'v1.6.29';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final RollupPlugin PLUGIN = new RollupPlugin();

Expand Down
7 changes: 7 additions & 0 deletions rollup/core/classes/RollupParentResetProcessor.cls
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public without sharing class RollupParentResetProcessor extends RollupFullBatchR
return this.rollupControl.ShouldSkipResettingParentFields__c ? this.getNoProcessId() : System.enqueueJob(new QueueableResetProcessor(this));
}

protected override Map<String, String> customizeToStringEntries(Map<String, String> props) {
props = super.customizeToStringEntries(props);
props.remove('Rollup Control');
props.remove('Rollup Metadata');
return props;
}

private void runSync() {
List<SObject> parentItems = this.preStart().get();
this.execute(null, parentItems);
Expand Down
44 changes: 29 additions & 15 deletions rollup/core/classes/RollupRelationshipFieldFinder.cls
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,16 @@ public without sharing class RollupRelationshipFieldFinder {
}

private void getFullRecalcParents(List<SObject> children) {
try {
this.safelyRetrieveFullRecalcParents(children);
} catch (Exception ex) {
RollupLogger.Instance.log('Error retrieving parents all in one go: ' + ex.getMessage() + ' falling back to standard approach', System.LoggingLevel.DEBUG);
this.isFullRecalc = false;
this.getParents(children);
}
}

private void safelyRetrieveFullRecalcParents(List<SObject> children) {
this.setFirstRunFlags(children);
Set<String> parentFieldNames = new Set<String>{ this.metadata.GrandparentRelationshipFieldPath__c };
String baseGrandparentPath = this.metadata.GrandparentRelationshipFieldPath__c.substringBeforeLast('.');
Expand Down Expand Up @@ -829,6 +839,10 @@ public without sharing class RollupRelationshipFieldFinder {
}
this.prepFinishedObject(finalParents);
}
this.handleIntermediateGrandparents(splitRelationshipNames);
}

private void handleIntermediateGrandparents(List<String> splitRelationshipNames) {
for (Schema.SObjectType intermediateGrandparent : this.typeToOldIntermediateGrandparents.keySet()) {
List<Schema.SObjectField> fields = intermediateGrandparent.getDescribe().fields.getMap().values();
Boolean hasFoundMatch = false;
Expand All @@ -846,23 +860,23 @@ public without sharing class RollupRelationshipFieldFinder {
Schema.SObjectType idType = iterator.hasNext() ? iterator.next().getSObjectType() : null;
// TODO handle idType == null
if (idType != this.ultimateParent) {
continue;
continue;
}
fullParentPath = splitRelationshipName + this.metadata.GrandparentRelationshipFieldPath__c.substringAfter(splitRelationshipName);
String query = RollupQueryBuilder.Current.getQuery(
this.ultimateParent,
new List<String>{ this.metadata.RollupFieldOnLookupObject__c, this.metadata.LookupFieldOnLookupObject__c },
'Id',
'=',
this.metadata.CalcItemWhereClause__c
);

List<SObject> otherPossibleGrandparents = this.repo.setQuery(query).setArg(otherIds).get();
for (SObject otherPossibleGrandparent : otherPossibleGrandparents) {
if (this.traversal.finalParentIdToRecords.containsKey(otherPossibleGrandparent.Id) == false) {
this.traversal.oldGrandparents.add(otherPossibleGrandparent);
}
fullParentPath = splitRelationshipName + this.metadata.GrandparentRelationshipFieldPath__c.substringAfter(splitRelationshipName);
String query = RollupQueryBuilder.Current.getQuery(
this.ultimateParent,
new List<String>{ this.metadata.RollupFieldOnLookupObject__c, this.metadata.LookupFieldOnLookupObject__c },
'Id',
'=',
this.metadata.CalcItemWhereClause__c
);

List<SObject> otherPossibleGrandparents = this.repo.setQuery(query).setArg(otherIds).get();
for (SObject otherPossibleGrandparent : otherPossibleGrandparents) {
if (this.traversal.finalParentIdToRecords.containsKey(otherPossibleGrandparent.Id) == false) {
this.traversal.oldGrandparents.add(otherPossibleGrandparent);
}
}
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"package": "apex-rollup",
"path": "rollup",
"scopeProfiles": true,
"versionName": "Summer 24 release",
"versionNumber": "1.6.28.0",
"versionName": "Grandparent full recalc bugfixes",
"versionNumber": "1.6.29.0",
"versionDescription": "Fast, configurable, elastically scaling custom rollup solution. Apex Invocable action, one-liner Apex trigger/CMDT-driven logic, and scheduled Apex-ready.",
"releaseNotesUrl": "https://github.com/jamessimone/apex-rollup/releases/latest",
"unpackagedMetadata": {
Expand Down Expand Up @@ -106,6 +106,7 @@
"apex-rollup@1.6.25": "04t6g000008Ob22AAC",
"apex-rollup@1.6.26": "04t6g000008ObCnAAK",
"apex-rollup@1.6.27": "04t6g000008ObL7AAK",
"apex-rollup@1.6.28": "04t6g000008ObN8AAK"
"apex-rollup@1.6.28": "04t6g000008ObN8AAK",
"apex-rollup@1.6.29": "04t6g000008ObNNAA0"
}
}