From 5bc74ad277124024acf5401f446221f4f76cbbe1 Mon Sep 17 00:00:00 2001 From: Jake Richter Date: Fri, 28 Apr 2023 11:38:37 -0400 Subject: [PATCH 1/9] fix from @dsurfleet --- .../apex-common/main/classes/fflib_SObjectUnitOfWork.cls | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index 1c38f657b61..d02ed406351 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -702,6 +702,7 @@ public virtual class fflib_SObjectUnitOfWork { for (Schema.SObjectType sObjectType : m_sObjectTypes) { + m_relationships.get(sObjectType.getDescribe().getName()).resolve(); m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values()); } } @@ -897,7 +898,9 @@ public virtual class fflib_SObjectUnitOfWork public void resolve() { - this.Record.put( this.RelatedToField, this.RelatedTo.Id); + if (this.Record.get(this.RelatedToField) == null){ + this.Record.put( this.RelatedToField, this.RelatedTo.Id); + } } } From b9288bdc6fdf80981a6e943f06bcf8761823c4c4 Mon Sep 17 00:00:00 2001 From: Jake Richter Date: Fri, 28 Apr 2023 20:50:41 -0400 Subject: [PATCH 2/9] unit test for registerDirty with relationship to new sObject --- .../classes/fflib_SObjectUnitOfWorkTest.cls | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index 582e759bc38..57cd6bfe6a5 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -608,6 +608,65 @@ private with sharing class fflib_SObjectUnitOfWorkTest ); } + @IsTest + private static void testRegisterDirtyRelatedToNewSobject() { + // GIVEN an existing opportunity + Opportunity existingOpp = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Existing Opportunity', + StageName = 'Closed', + CloseDate = System.today() + ); + // AND a new Account to which the existing opportunity will be related to + Account newAccount = new Account( + Name = 'New Account' + ); + + // WHEN + Test.startTest(); + MockDML mockDML = new MockDML(); + List mySobjects = new List{ + Account.SObjectType, + Opportunity.SObjectType + }; + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(mySobjects, mockDML); + uow.registerNew(newAccount); + uow.registerDirty(existingOpp, Opportunity.AccountId, newAccount); + uow.commitWork(); + Test.stopTest(); + + // THEN + System.assert( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Account.Id => newAccount.Id, + Account.Name => 'New Account' + } + } + ).matches(mockDML.recordsForInsert), + 'The new accoout record does not match' + ); + + // AND + + System.assert( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => existingOpp.Id, + Opportunity.Name => 'Existing Opportunity', + Opportunity.StageName => 'Closed', + Opportunity.AccountId => newAccount.Id + } + } + ).matches(mockDML.recordsForUpdate), + 'The opportunity record should be related to the new Account' + ); + } + @IsTest private static void testRegisterUpsert() { Opportunity existingOpp = new Opportunity( From 8c59274788fab2436920d935eef7badd13ab7638 Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 14:25:59 -0400 Subject: [PATCH 3/9] fix: add unit test to cover update bug --- .../main/classes/fflib_SObjectUnitOfWork.cls | 1 + .../classes/fflib_SObjectUnitOfWorkTest.cls | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index 537bf3ed78f..b6ae00d7ea1 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -707,6 +707,7 @@ public virtual class fflib_SObjectUnitOfWork { for (Schema.SObjectType sObjectType : m_sObjectTypes) { + //m_relationships.get(sObjectType.getDescribe().getName()).resolve(); m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values()); } } diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index 582e759bc38..02c25677887 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -608,6 +608,65 @@ private with sharing class fflib_SObjectUnitOfWorkTest ); } + @IsTest + private static void testRegisterDirtyWithRelationship() { + // GIVEN an existing opportunity + Opportunity existingOpp = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Existing Opportunity', + StageName = 'Closed', + CloseDate = System.today() + ); + // AND an existing Account to which the existing opportunity will be related to + Account newAccount = new Account( + Id = fflib_IDGenerator.generate(Schema.Account.SObjectType), + Name = 'New Account' + ); + + // WHEN + Test.startTest(); + MockDML mockDML = new MockDML(); + List mySobjects = new List{ + Account.SObjectType, + Opportunity.SObjectType + }; + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(mySobjects, mockDML); + uow.registerNew(newAccount); + uow.registerDirty(existingOpp, Opportunity.AccountId, newAccount); + uow.commitWork(); + Test.stopTest(); + + // THEN + System.Assert.isTrue( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Account.Id => newAccount.Id, + Account.Name => 'New Account' + } + } + ).matches(mockDML.recordsForInsert), + 'The new accoout record does not match' + ); + + // AND + System.Assert.isTrue( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => existingOpp.Id, + Opportunity.Name => 'Existing Opportunity', + Opportunity.StageName => 'Closed', + Opportunity.AccountId => newAccount.Id + } + } + ).matches(mockDML.recordsForUpdate), + 'The opportunity record should be related to the new Account' + ); + } + @IsTest private static void testRegisterUpsert() { Opportunity existingOpp = new Opportunity( From 32a23f4fffd77fc376db52ef099558bffd8111d7 Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 16:08:43 -0400 Subject: [PATCH 4/9] fix: remove all changes --- sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls | 1 - 1 file changed, 1 deletion(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index b6ae00d7ea1..537bf3ed78f 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -707,7 +707,6 @@ public virtual class fflib_SObjectUnitOfWork { for (Schema.SObjectType sObjectType : m_sObjectTypes) { - //m_relationships.get(sObjectType.getDescribe().getName()).resolve(); m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values()); } } From 937d4449c65de0328bf04c08cfa579fdcc2c73be Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 16:47:55 -0400 Subject: [PATCH 5/9] fix: add additional resolve logic --- .../main/classes/fflib_SObjectUnitOfWork.cls | 1 + .../classes/fflib_SObjectUnitOfWorkTest.cls | 46 +++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index 537bf3ed78f..edfcb0bc783 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -707,6 +707,7 @@ public virtual class fflib_SObjectUnitOfWork { for (Schema.SObjectType sObjectType : m_sObjectTypes) { + m_relationships.get(sObjectType.getDescribe().getName()).resolve(); m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values()); } } diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index 02c25677887..2ffadaf59cb 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -619,16 +619,15 @@ private with sharing class fflib_SObjectUnitOfWorkTest ); // AND an existing Account to which the existing opportunity will be related to Account newAccount = new Account( - Id = fflib_IDGenerator.generate(Schema.Account.SObjectType), Name = 'New Account' ); // WHEN Test.startTest(); - MockDML mockDML = new MockDML(); + MockDMLWithInsertIds mockDML = new MockDMLWithInsertIds(); List mySobjects = new List{ - Account.SObjectType, - Opportunity.SObjectType + Opportunity.SObjectType, + Account.SObjectType }; fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(mySobjects, mockDML); uow.registerNew(newAccount); @@ -647,7 +646,7 @@ private with sharing class fflib_SObjectUnitOfWorkTest } } ).matches(mockDML.recordsForInsert), - 'The new accoout record does not match' + 'The new account record does not match' ); // AND @@ -907,6 +906,43 @@ private with sharing class fflib_SObjectUnitOfWorkTest } } + private class MockDMLWithInsertIds implements fflib_SObjectUnitOfWork.IDML + { + public List recordsForInsert = new List(); + public List recordsForUpdate = new List(); + public List recordsForDelete = new List(); + public List recordsForRecycleBin = new List(); + public List recordsForEventPublish = new List(); + + public void dmlInsert(List objList) + { + for (SObject obj : objList) { + obj.Id = fflib_IDGenerator.generate(obj.getSObjectType()); + } + this.recordsForInsert.addAll(objList); + } + + public void dmlUpdate(List objList) + { + this.recordsForUpdate.addAll(objList); + } + + public void dmlDelete(List objList) + { + this.recordsForDelete.addAll(objList); + } + + public void eventPublish(List objList) + { + this.recordsForEventPublish.addAll(objList); + } + + public void emptyRecycleBin(List objList) + { + this.recordsForRecycleBin.addAll(objList); + } + } + public class DerivedUnitOfWorkException extends Exception {} public class FailDoingWorkException extends Exception {} } From c06bde8d4b40d7dd78daf97141a7c3b3e7d35850 Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 17:01:04 -0400 Subject: [PATCH 6/9] fix: remove null check --- .../apex-common/main/classes/fflib_SObjectUnitOfWork.cls | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index bc778ed4584..edfcb0bc783 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -903,9 +903,7 @@ public virtual class fflib_SObjectUnitOfWork public void resolve() { - if (this.Record.get(this.RelatedToField) == null){ - this.Record.put( this.RelatedToField, this.RelatedTo.Id); - } + this.Record.put( this.RelatedToField, this.RelatedTo.Id); } } From 2e95e529a57f78d0616407cbe55e4b91bfd53ecf Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 22:42:07 -0400 Subject: [PATCH 7/9] fix: remove resolved relationships --- .../main/classes/fflib_SObjectUnitOfWork.cls | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index edfcb0bc783..b3f15bf8dfe 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -810,12 +810,11 @@ public virtual class fflib_SObjectUnitOfWork public void resolve() { // Resolve relationships - for (IRelationship relationship : m_relationships) - { - //relationship.Record.put(relationship.RelatedToField, relationship.RelatedTo.Id); - relationship.resolve(); - } - + for (Integer i = (m_relationships.size() - 1); i >= 0; i--) { + if (m_relationships.get(i).resolve()) { + m_relationships.remove(i); + } + } } public void add(SObject record, Schema.SObjectField relatedToField, Schema.SObjectField externalIdField, Object externalId) @@ -875,7 +874,7 @@ public virtual class fflib_SObjectUnitOfWork private interface IRelationship { - void resolve(); + Boolean resolve(); } private class RelationshipByExternalId implements IRelationship @@ -887,11 +886,16 @@ public virtual class fflib_SObjectUnitOfWork public Schema.SObjectField ExternalIdField; public Object ExternalId; - public void resolve() + public Boolean resolve() { + if (ExternalId == null) { + return false; + } + SObject relationshipObject = this.RelatedTo.newSObject(); relationshipObject.put( ExternalIdField.getDescribe().getName(), this.ExternalId ); this.Record.putSObject( this.RelationshipName, relationshipObject ); + return true; } } @@ -901,9 +905,14 @@ public virtual class fflib_SObjectUnitOfWork public Schema.SObjectField RelatedToField; public SObject RelatedTo; - public void resolve() + public Boolean resolve() { + if (String.isBlank(this.relatedTo.Id)) { + return false; + } + this.Record.put( this.RelatedToField, this.RelatedTo.Id); + return true; } } @@ -912,9 +921,14 @@ public virtual class fflib_SObjectUnitOfWork public Messaging.SingleEmailMessage email; public SObject relatedTo; - public void resolve() + public Boolean resolve() { + if (String.isBlank(this.relatedTo.Id)) { + return false; + } + this.email.setWhatId( this.relatedTo.Id ); + return true; } } From 032777b0fe0ebd780d049065c1d665d680df9c13 Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 23:00:49 -0400 Subject: [PATCH 8/9] fix: revert to use previous IDML --- .../classes/fflib_SObjectUnitOfWorkTest.cls | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index 2ffadaf59cb..2f6c593931b 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -624,7 +624,7 @@ private with sharing class fflib_SObjectUnitOfWorkTest // WHEN Test.startTest(); - MockDMLWithInsertIds mockDML = new MockDMLWithInsertIds(); + MockDML mockDML = new MockDML(); List mySobjects = new List{ Opportunity.SObjectType, Account.SObjectType @@ -880,44 +880,10 @@ private with sharing class fflib_SObjectUnitOfWorkTest public List recordsForRecycleBin = new List(); public List recordsForEventPublish = new List(); - public void dmlInsert(List objList) - { - this.recordsForInsert.addAll(objList); - } - - public void dmlUpdate(List objList) - { - this.recordsForUpdate.addAll(objList); - } - - public void dmlDelete(List objList) - { - this.recordsForDelete.addAll(objList); - } - - public void eventPublish(List objList) - { - this.recordsForEventPublish.addAll(objList); - } - - public void emptyRecycleBin(List objList) - { - this.recordsForRecycleBin.addAll(objList); - } - } - - private class MockDMLWithInsertIds implements fflib_SObjectUnitOfWork.IDML - { - public List recordsForInsert = new List(); - public List recordsForUpdate = new List(); - public List recordsForDelete = new List(); - public List recordsForRecycleBin = new List(); - public List recordsForEventPublish = new List(); - public void dmlInsert(List objList) { for (SObject obj : objList) { - obj.Id = fflib_IDGenerator.generate(obj.getSObjectType()); + obj.Id = fflib_IDGenerator.generate(obj?.getSObjectType()); } this.recordsForInsert.addAll(objList); } From fb866b2b8b3d4c6b765335683e4d8ad783a74e4c Mon Sep 17 00:00:00 2001 From: Clay Chipps Date: Wed, 3 Apr 2024 23:45:00 -0400 Subject: [PATCH 9/9] fix: prevent null pointer on dmlInsert test --- .../apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index 2f6c593931b..f9f334bcdda 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -883,7 +883,10 @@ private with sharing class fflib_SObjectUnitOfWorkTest public void dmlInsert(List objList) { for (SObject obj : objList) { - obj.Id = fflib_IDGenerator.generate(obj?.getSObjectType()); + if (obj == null) { + continue; + } + obj.Id = fflib_IDGenerator.generate(obj.getSObjectType()); } this.recordsForInsert.addAll(objList); }