Merge pull request #44 from wimvelzeboer/feature/SObjects2_getGrandParentIdById
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
|
public virtual class fflib_SObjects2
|
||||||
extends fflib_SObjects
|
extends fflib_SObjects
|
||||||
implements fflib_ISObjects
|
implements fflib_ISObjects2
|
||||||
{
|
{
|
||||||
protected Schema.DescribeSObjectResult SObjectDescribe { get; private set; }
|
protected Schema.DescribeSObjectResult SObjectDescribe { get; private set; }
|
||||||
|
|
||||||
@@ -79,6 +79,58 @@ public virtual class fflib_SObjects2
|
|||||||
return (result.size() == 0) ? null : result.get(0);
|
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)
|
* @return Returns the contents of the Domain by their primary Key ('Id' field)
|
||||||
*/
|
*/
|
||||||
@@ -597,6 +649,16 @@ public virtual class fflib_SObjects2
|
|||||||
return result;
|
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
|
* Get a map with the record mapped to the given Id field value
|
||||||
* Key fields containing null values are omitted
|
* 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;
|
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
|
* Reverse the order of a list of objects
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -120,6 +120,51 @@ private class fflib_SObjects2Test
|
|||||||
// todo - add assertions
|
// 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
|
@IsTest
|
||||||
static void itShouldReturnStringById()
|
static void itShouldReturnStringById()
|
||||||
{
|
{
|
||||||
@@ -614,4 +659,36 @@ private class fflib_SObjects2Test
|
|||||||
clearField(Schema.Account.ShippingCountry);
|
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');
|
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
|
@IsTest
|
||||||
static void itShouldReturnAllValuesForTheGivenIds()
|
static void itShouldReturnAllValuesForTheGivenIds()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user