Add domain method to map the record Id to a grand-grand-...-parent record Id
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* File Name: fflib_ISObjects2
|
||||
*
|
||||
* @author architect ir. Wilhelmus G.J. Velzeboer
|
||||
*
|
||||
* Copyright (c), W.G.J. Velzeboer,
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above author notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the author nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
public interface fflib_ISObjects2 extends fflib_ISObjects
|
||||
{
|
||||
Map<Id, Id> getGrandParentIdById(List<fflib_ISObjects2> relatedDomains);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<apiVersion>53.0</apiVersion>
|
||||
<status>Active</status>
|
||||
</ApexClass>
|
||||
@@ -30,7 +30,7 @@
|
||||
*/
|
||||
public virtual class fflib_SObjects2
|
||||
extends fflib_SObjects
|
||||
implements fflib_ISObjects
|
||||
implements fflib_ISObjects2
|
||||
{
|
||||
protected Schema.DescribeSObjectResult SObjectDescribe { get; private set; }
|
||||
|
||||
@@ -79,6 +79,58 @@ public virtual class fflib_SObjects2
|
||||
return (result.size() == 0) ? null : result.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map between the record Ids of this domain to the last given grandparent domain.
|
||||
* This method is using getRelatedFieldByDomainType to determine which SObjectField
|
||||
* is related to the given parent domain
|
||||
*
|
||||
* @param relatedDomains A List of domains in hierarchical order from first parent to grandparent
|
||||
*
|
||||
* @return Returns a map with KEY: The Ids of the records in this domain,
|
||||
* to the VALUE: The Id of the last grandparent record listed in the relatedDomains,
|
||||
* null if no grandparent exists.
|
||||
*
|
||||
* @see getRelatedFieldByDomainType
|
||||
*
|
||||
* @example
|
||||
* IInvoiceLines invoiceLineDomain = InvoiceLines.newInstance(invoiceLineRecords);
|
||||
* IInvoices invoicesDomain = Invoices.newInstance(invoiceRecords);
|
||||
* IAccount accountDomain = Accounts.newInstance(accountRecords);
|
||||
*
|
||||
* // A map with key - InvoiceLine.Id to value - Account.Id
|
||||
* Map<Id, Id> accountIdByInvoiceLineId =
|
||||
* invoiceLineDomain.getGrandParentIdById(
|
||||
* new List<fflib_SObjects2>
|
||||
* {
|
||||
* invoicesDomain,
|
||||
* accountDomain
|
||||
* });
|
||||
*/
|
||||
public virtual Map<Id, Id> getGrandParentIdById(List<fflib_ISObjects2> relatedDomains)
|
||||
{
|
||||
fflib_ISObjects2 relatedDomain = relatedDomains.remove(0);
|
||||
SObjectType relatedDomainType = relatedDomain.getSObjectType();
|
||||
Map<SObjectType, SObjectField> relatedFieldByDomainType = getRelatedFieldByDomainType();
|
||||
|
||||
if (relatedFieldByDomainType.containsKey(relatedDomainType) == false)
|
||||
throw new DeveloperException(
|
||||
String.format(
|
||||
'RelatedDomain type {0} not present in method getRelatedFieldByDomainType',
|
||||
new List<String> {String.valueOf(relatedDomainType)}));
|
||||
|
||||
Map<Id, Id> result = getIdFieldByIdField(
|
||||
relatedFieldByDomainType.get(relatedDomainType),
|
||||
getSObjectType().getDescribe().fields.getMap().get('Id'));
|
||||
|
||||
// Check if we need to resolve another parent relation, if not return the results
|
||||
if (relatedDomains.isEmpty()) return result;
|
||||
|
||||
Map<Id, Id> grandChildIdByChildId = relatedDomain.getGrandParentIdById(relatedDomains);
|
||||
|
||||
// Replace the maps value (childId) with the grand child Id
|
||||
return fflib_ArrayUtils.replaceValue(result, grandChildIdByChildId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the contents of the Domain by their primary Key ('Id' field)
|
||||
*/
|
||||
@@ -597,6 +649,16 @@ public virtual class fflib_SObjects2
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Parent Domain SObjectType to SObjectField.relationship field on this domain SObjectType
|
||||
*
|
||||
* @return Returns a map of the Parent Domain SObjectType to SObjectField.relationship field on the SObjectType
|
||||
*/
|
||||
protected virtual Map<SObjectType, Schema.SObjectField> getRelatedFieldByDomainType()
|
||||
{
|
||||
return new Map<SObjectType, Schema.SObjectField>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map with the record mapped to the given Id field value
|
||||
* Key fields containing null values are omitted
|
||||
@@ -892,4 +954,6 @@ public virtual class fflib_SObjects2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DeveloperException extends Exception { }
|
||||
}
|
||||
@@ -310,6 +310,36 @@ public class fflib_ArrayUtils
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes two maps and replaces the value of the source with the value of the replacement,
|
||||
* where the source value matches the replacement key.
|
||||
* The value will become null if the source value does not exists as key in replacement map
|
||||
*
|
||||
* @param source The map to replace its value
|
||||
* @param replacement The map to use its value for replacement
|
||||
*
|
||||
* @return Returns a Map<{Source} Id, {Replacement} Id>
|
||||
*/
|
||||
public static Map<Id, Id> replaceValue(Map<Id, Id> source, Map<Id, Id> replacement)
|
||||
{
|
||||
Map<Id, Id> result = new Map<Id, Id>();
|
||||
for (Id keyId : source.keySet())
|
||||
{
|
||||
Id sourceValue = source.get(keyId);
|
||||
|
||||
if (replacement.containsKey(sourceValue))
|
||||
{
|
||||
result.put(keyId, replacement.get(sourceValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is no replacement value for this keyId
|
||||
result.put(keyId, null);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the order of a list of objects
|
||||
*
|
||||
|
||||
@@ -120,6 +120,51 @@ private class fflib_SObjects2Test
|
||||
// todo - add assertions
|
||||
}
|
||||
|
||||
@IsTest
|
||||
static void itShouldReturnTheRelatedParentDomainId()
|
||||
{
|
||||
// GIVEN a case related to contact related to Account ,
|
||||
// a case record related to contact but not to Account,
|
||||
// and a case record related to nothing.
|
||||
// All encapsulated in domains.
|
||||
Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType);
|
||||
Id caseIdB = fflib_IDGenerator.generate(Schema.Case.SObjectType);
|
||||
Id caseIdC = fflib_IDGenerator.generate(Schema.Case.SObjectType);
|
||||
Id contactIdA = fflib_IDGenerator.generate(Schema.Contact.SObjectType);
|
||||
Id contactIdB = fflib_IDGenerator.generate(Schema.Contact.SObjectType);
|
||||
Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType);
|
||||
Account accountRecord = new Account(Id = accountId);
|
||||
Contact contactRecordA = new Contact(Id = contactIdA, AccountId = accountId);
|
||||
Contact contactRecordB = new Contact(Id = contactIdB);
|
||||
Case caseRecordA = new Case(Id = caseIdA, ContactId = contactIdA);
|
||||
Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB);
|
||||
Case caseRecordC = new Case(Id = caseIdC);
|
||||
|
||||
DomainAccounts accountDomain = new DomainAccounts(new List<Account> {accountRecord});
|
||||
DomainContacts contactDomain = new DomainContacts(new List<Contact> {contactRecordA, contactRecordB});
|
||||
DomainCases caseDomain = new DomainCases(new List<Case> {caseRecordA, caseRecordB, caseRecordC});
|
||||
|
||||
// WHEN we request the relation between Case and Account
|
||||
System.Test.startTest();
|
||||
Map<Id, Id> result = caseDomain.getGrandParentIdById(
|
||||
new List<fflib_SObjects2>
|
||||
{
|
||||
contactDomain,
|
||||
accountDomain
|
||||
}
|
||||
);
|
||||
System.Test.stopTest();
|
||||
|
||||
// THEN it should return the AccountId By CaseId
|
||||
System.assertEquals(3, result.size(), 'Expected one related account to the case');
|
||||
System.assert(result.containsKey(caseIdA));
|
||||
System.assert(result.containsKey(caseIdB));
|
||||
System.assert(result.containsKey(caseIdC));
|
||||
System.assertEquals(accountId, result.get(caseIdA), 'Account Id should be related to the Case Id A');
|
||||
System.assertEquals(null, result.get(caseIdB), 'Case Id B should have no related Account Id');
|
||||
System.assertEquals(null, result.get(caseIdC), 'Case Id B should have no related Account Id');
|
||||
}
|
||||
|
||||
@IsTest
|
||||
static void itShouldReturnStringById()
|
||||
{
|
||||
@@ -614,4 +659,36 @@ private class fflib_SObjects2Test
|
||||
clearField(Schema.Account.ShippingCountry);
|
||||
}
|
||||
}
|
||||
|
||||
private class DomainContacts extends fflib_SObjects2
|
||||
{
|
||||
public DomainContacts(List<SObject> records)
|
||||
{
|
||||
super(records, Schema.Contact.SObjectType);
|
||||
}
|
||||
|
||||
protected override Map<SObjectType, Schema.SObjectField> getRelatedFieldByDomainType()
|
||||
{
|
||||
return new Map<SObjectType, Schema.SObjectField>
|
||||
{
|
||||
Schema.Account.SObjectType => Contact.AccountId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class DomainCases extends fflib_SObjects2
|
||||
{
|
||||
public DomainCases(List<SObject> records)
|
||||
{
|
||||
super(records, Schema.Case.SObjectType);
|
||||
}
|
||||
|
||||
protected override Map<SObjectType, Schema.SObjectField> getRelatedFieldByDomainType()
|
||||
{
|
||||
return new Map<SObjectType, Schema.SObjectField>
|
||||
{
|
||||
Schema.Contact.SObjectType => Case.ContactId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +238,34 @@ private class fflib_ArrayUtilsTest
|
||||
System.assertEquals(8, result, 'Did not return the last value in the list of objects');
|
||||
}
|
||||
|
||||
@IsTest
|
||||
static void itShouldReplaceValueInTheMap()
|
||||
{
|
||||
Id accountIdA = fflib_IDGenerator.generate(Schema.Account.SObjectType);
|
||||
Id accountIdB = fflib_IDGenerator.generate(Schema.Account.SObjectType);
|
||||
Id accountIdC = fflib_IDGenerator.generate(Schema.Account.SObjectType);
|
||||
Id contactIdA = fflib_IDGenerator.generate(Schema.Contact.SObjectType);
|
||||
Id contactIdB = fflib_IDGenerator.generate(Schema.Contact.SObjectType);
|
||||
Map<Id, Id> source = new Map<Id, Id>
|
||||
{
|
||||
accountIdA => contactIdA,
|
||||
accountIdB => contactIdB,
|
||||
accountIdC => fflib_IDGenerator.generate(Schema.Contact.SObjectType)
|
||||
};
|
||||
Map<Id, Id> replacement = new Map<Id, Id>
|
||||
{
|
||||
contactIdA => accountIdB,
|
||||
contactIdB => accountIdA
|
||||
};
|
||||
|
||||
Map<Id, Id> result = fflib_ArrayUtils.replaceValue(source, replacement);
|
||||
|
||||
System.assertEquals(3, result.size());
|
||||
System.assertEquals(accountIdB, result.get(accountIdA));
|
||||
System.assertEquals(accountIdA, result.get(accountIdB));
|
||||
System.assertEquals(null, result.get(accountIdC));
|
||||
}
|
||||
|
||||
@IsTest
|
||||
static void itShouldReturnAllValuesForTheGivenIds()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user