diff --git a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls index 9181259..6d2907d 100644 --- a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls +++ b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls @@ -28,1106 +28,1192 @@ * 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 virtual class fflib_SObjects2 - extends fflib_SObjects - implements fflib_ISObjects2 -{ - protected Schema.DescribeSObjectResult SObjectDescribe { get; private set; } - - /** - * Class constructors - * - * @param objects The Objects of type SObjectType, to contain inside the domain - */ - public fflib_SObjects2(List objects) - { - // todo - assert of objects of type SObject - this((List) objects); - } - - /** - * Class constructor - * - * @param records The records to contain inside the domain - */ - public fflib_SObjects2(List records) - { - this(records, records.getSObjectType()); - } - - /** - * Class constructor - * - * @param records The records to contain inside the domain - * @param sObjectType The SObjectType of the records - */ - public fflib_SObjects2(List records, Schema.SObjectType sObjectType) - { - super(records, sObjectType); - SObjectDescribe = sObjectType.getDescribe(); - } - - /** - * @param id Domain containing primary key (Id field) values - * - * @return Returns only the SObjects from the domain matching the given Ids. - */ - public virtual SObject getRecord(Id id) - { - List result = getRecords(new Set {id}); - - return (result.size() == 0) ? null : result.get(0); - } - - /** - * Creates a map between the record Ids of this domain to the last given grandparent domain. - * - * @param domainsByRelationshipField A Map of domains and the 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. - * - * @example - * ICases casesDomain = Cases.newInstance(caseRecords); - * IContacts contactsDomain = Contacts.newInstance(contactRecords); - * IAccount accountDomain = Accounts.newInstance(accountRecords); - * - * // A map with key - Case.Id to value - Account.Id - * Map accountIdByCaseId = - * casesDomain.getParentIdById( - * new Map - * { - * Schema.Case.ContactId => contactDomain, - * Schema.Contact.AccountId => accountDomain - * }); - */ - public virtual Map getParentIdById(Map domainsByRelationshipField) - { - // Takes the highest related domain from the domainsByRelationshipField - Schema.SObjectField relationshipField = - new List(domainsByRelationshipField.keySet()) - .get(0); - fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove(relationshipField); - - // Extract the Id values of the related SObjectType - Map result = getIdFieldByIdField( - relationshipField, - getSObjectType().getDescribe().fields.getMap().get('Id')); - - // Check if we need to resolve another parent relation, if not return the results - if (domainsByRelationshipField.isEmpty()) return result; - - // Fetch the Ids of the related SObjectType - Map parentIdByChildId = relatedDomain.getParentIdById(domainsByRelationshipField); - - // Replace the maps value (ParentId) with the grand parent Id - return fflib_ArrayUtils.replaceValue(result, parentIdByChildId); - } - - /** DEPRECATED **/ - public virtual Map getGrandParentIdById(List relatedDomains) - { - System.debug(LoggingLevel.WARN, - 'The \'getGrandParentIdById(List relatedDomains)\' method is deprecated,' + - 'please refactor to \'getParentIdById(Map relatedDomains)\''); - - fflib_ISObjects2 relatedDomain = relatedDomains.remove(0); - SObjectType relatedDomainType = relatedDomain.getSObjectType(); - Map relatedFieldByDomainType = getRelatedFieldByDomainType(); - - if (relatedFieldByDomainType.containsKey(relatedDomainType) == false) - throw new DeveloperException( - String.format( - 'RelatedDomain type {0} not present in method getRelatedFieldByDomainType', - new List {String.valueOf(relatedDomainType)})); - - Map 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 grandChildIdByChildId = relatedDomain.getGrandParentIdById(relatedDomains); - - // Replace the maps value (childId) with the grand child Id - return fflib_ArrayUtils.replaceValue(result, grandChildIdByChildId); - } - - /** - * Creates a map between the record Ids of this domain to the last given child domain. - * This method is using getRelatedFieldByDomainType to determine which SObjectField - * is related to the given parent domain - * - * @param domainsByRelationshipField A Map of domains in hierarchical order from first child to grandchild - * - * @return Returns a map with KEY: The Ids of the records in this domain, - * to the VALUE: The record Ids of the lowest child related record listed in the relatedDomains, - * null if no grandchild exists. - * - * @example - * ICases casesDomain = Cases.newInstance(caseRecords); - * IContacts contactsDomain = Contacts.newInstance(contactRecords); - * IAccount accountDomain = Accounts.newInstance(accountRecords); - * - * // A map with key - Case.Id to value - Account.Id - * Map> caseIdsByAccountId = - * accountsDomain.getChildIdsById( - * new Map - * { - * Schema.Contact.AccountId => contactsDomain - * Schema.Case.ContactId => casesDomain, - * }); - */ - public virtual Map> getChildIdsById(Map domainsByRelationshipField) - { - // Takes the highest related domain from the domainsByRelationshipField - Schema.SObjectField relationshipField = - new List(domainsByRelationshipField.keySet()) - .get(0); - fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove(relationshipField); - - Map> result = relatedDomain.getIdFieldsByIdField( - relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), - relationshipField); - - // Check if we need to resolve another child relation, if not return the results - if (domainsByRelationshipField.isEmpty()) return result; - - Map> childIdsById = relatedDomain.getChildIdsById(domainsByRelationshipField); - - // Replace the maps value (childIds) with the grand child Id - return fflib_ArrayUtils.replaceValues(result, childIdsById); - } - - /** DEPRECATED **/ - public virtual Map> getChildIdsById(List relatedDomains) - { - System.debug(LoggingLevel.WARN, - 'The \'getChildIdsById(List relatedDomains)\' method is deprecated,' + - 'please refactor to \'getChildIdsById(Map relatedDomains)\''); - - - SObjectType thisDomainType = getSObjectType(); - fflib_SObjects2 relatedDomain = relatedDomains.remove(0); - Map relatedFieldByDomainType = relatedDomain.getRelatedFieldByDomainType(); - - if (relatedFieldByDomainType.containsKey(thisDomainType) == false) - throw new DeveloperException( - String.format( - 'RelatedDomain type {0} not present in method getRelatedFieldByDomainType', - new List {String.valueOf(thisDomainType)})); - - SObjectField relatedLookupFieldOnChild = relatedFieldByDomainType.get(thisDomainType); - Map> result = relatedDomain.getIdFieldsByIdField( - relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), - relatedLookupFieldOnChild); - - // Check if we need to resolve another child relation, if not return the results - if (relatedDomains.isEmpty()) return result; - - Map> childIdsById = relatedDomain.getChildIdsById(relatedDomains); - - // Replace the maps value (childIds) with the grand child Id - return fflib_ArrayUtils.replaceValues(result, childIdsById); - } - - /** - * @return Returns the contents of the Domain by their primary Key ('Id' field) - */ - public virtual Map getSObjectById() - { - return new Map(getRecords()); - } - - /** - * @param ids Domain containing primary key (Id field) values - * - * @return Returns only the SObjects from the domain matching the given Ids. - */ - public virtual List getRecords(fflib_Ids ids) - { - return getRecords(ids.getIds()); - } - - /** - * @param ids A Set containing primary key (Id field) values - * - * @return Returns only the SObjects from the domain matching the given Ids. - */ - public virtual List getRecords(Set ids) - { - Map sObjectsByIds = getSObjectById(); - List result = new List(); - for (Id id : ids) - { - if (sObjectsByIds.containsKey(id) == false) continue; - - result.add(sObjectsByIds.get(id)); - } - return result; - } - - /** - * @param criteria The condition the returned record must meet - * - * @return Return the SObject records contained in the domain matching the criteria - */ - public virtual List getRecords(fflib_Criteria criteria) - { - List result = new List(); - for (SObject record : getRecords()) - { - if (!criteria.evaluate(record)) continue; - - result.add(record); - } - return result; - } - - /** - * @return Trip the records from fields that the user doesn't have access to - */ - public virtual List stripInaccessibleFields() - { - return stripInaccessibleFields(AccessType.READABLE); - } - - /** - * @param accessType - * - * @return Trip the records from fields that the user doesn't have access to - */ - public virtual List stripInaccessibleFields( - System.AccessType accessType - ) - { - return Security.stripInaccessible(accessType, getRecords()).getRecords(); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * - * @return The Date values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Dates getDateFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - result.add((Date) fieldValue); - } - return new fflib_Dates(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Date values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Dates getDateFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getDateFieldValues(sObjectField); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * - * @return The DateTime values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_DateTimes getDateTimeFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - result.add((Datetime) fieldValue); - } - return new fflib_DateTimes(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The DateTime values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_DateTimes getDateTimeFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getDateTimeFieldValues(sObjectField); - } - - /** - * @param decimalField The field to use as the Value of the returned Map - * @param idField The field to use as the Key of the returned map - * - * @return Returns a map with the values of two fields - */ - @TestVisible - protected virtual Map getDecimalById(Schema.SObjectField decimalField, Schema.SObjectField idField) { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(idField) == null) continue; - - result.put( - (Id) record.get(idField), - (Decimal) record.get(decimalField) - ); - } - return result; - } - - /** - * @param sObjectField The Schema.SObjectField to get the SUM of - * - * @return The total SUM of the given field - */ - @TestVisible - protected virtual Decimal getDecimalFieldSum(Schema.SObjectField sObjectField) - { - Decimal result = 0; - for (SObject record : getRecords()) - { - result += record.get(sObjectField) == null ? 0 : (Decimal) record.get(sObjectField); - } - return result; - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * - * @return The Decimal values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Decimals getDecimalFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - result.add((Decimal) fieldValue); - } - return new fflib_Decimals(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Decimal values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Decimals getDecimalFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getDecimalFieldValues(sObjectField); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * - * @return The Double values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Doubles getDoubleFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - result.add((Double) fieldValue); - } - return new fflib_Doubles(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Double values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Doubles getDoubleFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getDoubleFieldValues(sObjectField); - } - - /** - * Get a map with the values of two fields. - * Key fields containing null values are omitted - * - * @param valueField The field to use as the Value of the Map - * @param keyField The field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Accounts accounts = Accounts.newInstance(records); - * Map accountNameById = accounts.getFieldByField(Account.Name, Account.Id); - */ - @TestVisible - protected virtual Map getFieldByField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(keyField) == null) continue; - - result.put( - record.get(keyField), - record.get(valueField) - ); - } - return result; - } - - /** - * Get a map with the values of two fields - * Key fields containing null values are omitted - * - * @param valueField The field to use as the Value of the Map - * @param keyField The field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Contacts contacts = Contacts.newInstance(records); - * Map> contactIdByAccountId = contacts.getFieldsByField(Contact.Id, Contact.AccountId); - */ - @TestVisible - protected virtual Map> getFieldsByField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map> result = new Map>(); - for (SObject record : getRecords()) - { - Object keyFieldValue = record.get(keyField); - if (keyFieldValue == null) continue; - - if (!result.containsKey(keyFieldValue)) - { - result.put(keyFieldValue, new Set()); - } - result.get(keyFieldValue).add(record.get(valueField)); - } - return result; - } - - /** - * @return Returns only the first SObject contained in the domain. - */ - public SObject getFirstSObject() - { - if (isEmpty()) return null; - - return getRecords().get(0); - } - - /** - * Get a map with the values of a String field as key and Id field - * Key fields containing null values are omitted - * - * @param valueField The Id field to use as the Value of the Map - * @param keyField The String field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Contacts contacts = Contacts.newInstance(records); - * Map accountIdByContactId = contacts.getIdFieldByStringField(Contact.Id, Contact.Name); - */ - @TestVisible - protected virtual Map getIdFieldByStringField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(keyField) == null) continue; - - result.put( - (String) record.get(keyField), - (Id) record.get(valueField) - ); - } - return result; - } - - /** - * Get a map with the values of two Id fields - * Key fields containing null values are omitted - * - * @param valueField The Id field to use as the Value of the Map - * @param keyField The Id field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Contacts contacts = Contacts.newInstance(records); - * Map accountIdByContactId = contacts.getIdFieldByIdField(Contact.AccountId, Contact.Id); - */ - @TestVisible - protected virtual Map getIdFieldByIdField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(keyField) == null) continue; - - result.put( - (Id) record.get(keyField), - (Id) record.get(valueField) - ); - } - return result; - } - - /** - * Get a map with the values of two Id fields with a one to many relation - * Key fields containing null values are omitted - * - * @param valueField The Id field to use as the Value of the Map - * @param keyField The Id field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Contacts contacts = Contacts.newInstance(records); - * Map> contactIdByAccountId = contacts.getIdFieldsByIdField(Contact.Id, Contact.AccountId); - */ - @TestVisible - protected virtual Map> getIdFieldsByIdField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map> result = new Map>(); - for (SObject record : getRecords()) - { - Id keyFieldId = (Id) record.get(keyField); - if (keyFieldId == null) continue; - - if (!result.containsKey(keyFieldId)) - { - result.put(keyFieldId, new Set()); - } - result.get(keyFieldId).add((Id) record.get(valueField)); - } - return result; - } - - /** - * Get a map with the values of an Id field by a String field with a one to many relation - * Key fields containing null values are omitted - * - * @param valueField The Id field to use as the Value of the Map - * @param keyField The String field to use as the Key of the map - * - * @return Returns a map with the values of two fields - * - * @example - * Accounts accounts = Accounts.newInstance(records); - * Map> accountIdsByCountry = accounts.getIdFieldsByStringField(Account.BillingCountry, Account.Id); - */ - @TestVisible - protected virtual Map> getIdFieldsByStringField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map> result = new Map>(); - for (SObject record : getRecords()) - { - String keyFieldString = String.valueOf(record.get(keyField)); - if (keyFieldString == null) continue; - - if (!result.containsKey(keyFieldString)) - { - result.put(keyFieldString, new Set()); - } - result.get(keyFieldString).add((Id) record.get(valueField)); - } - return result; - } - - /** - * @param sObjectField The Schema.SObjectField to get their Id values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Id values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Ids getIdFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_Ids(new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getIdFieldValues(sObjectField)); - } - - /** - * @param sObjectField The Schema.SObjectField to get their values - * - * @return The Integer values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Integers getIntegerFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - - result.add(Integer.valueOf(fieldValue)); - } - return new fflib_Integers(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Integer values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Integers getIntegerFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getIntegerFieldValues(sObjectField); - } - - /** - * @param sObjectField The Schema.SObjectField to get their values - * - * @return The Long values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Longs getLongFieldValues(Schema.SObjectField sObjectField) - { - Set result = new Set(); - for (SObject record : getRecords()) - { - Object fieldValue = record.get(sObjectField); - if (fieldValue == null) continue; - result.add((Long) fieldValue); - } - return new fflib_Longs(result); - } - - /** - * @param sObjectField The Schema.SObjectField to get their values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The Long values of the given sObjectField, null values are omitted - */ - @TestVisible - protected virtual fflib_Longs getLongFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getLongFieldValues(sObjectField); - } - - @TestVisible - protected virtual List getRecords(Schema.SObjectField sObjectField, Id idValue) - { - return getRecords(sObjectField, new Set{ idValue }); - } - - @TestVisible - protected virtual List getRecords(Schema.SObjectField sObjectField, Set values) - { - List result = new List(); - for (SObject record : getRecords()) - { - if (values?.contains((Id) record.get(sObjectField))) - { - result.add(record); - } - } - return result; - } - - @TestVisible - protected virtual List getRecords(Schema.SObjectField sObjectField, String value) - { - return getRecords(sObjectField, new Set{ value }); - } - - @TestVisible - protected virtual List getRecords(Schema.SObjectField sObjectField, Set values) - { - List result = new List(); - for (SObject record : getRecords()) - { - if (values?.contains((String) record.get(sObjectField))) - { - result.add(record); - } - } - return result; - } - - @TestVisible - protected virtual List getRecordsIsNot(Schema.SObjectField sObjectField, String value) - { - return getRecordsNotIn(sObjectField, new Set{ value }); - } - - @TestVisible - protected virtual List getRecordsNotIn(Schema.SObjectField sObjectField, Set values) - { - List result = new List(); - for (SObject record : getRecords()) - { - if (values?.contains((String) record.get(sObjectField))) continue; - result.add(record); - } - return result; - } - - protected virtual List getRecordsIsNot(Schema.SObjectField sObjectField, Id idValue) - { - return getRecordsNotIn(sObjectField, new Set{ idValue }); - } - - protected virtual List getRecordsNotIn(Schema.SObjectField sObjectField, Set idValues) - { - List result = new List(); - for (SObject record : getRecords()) - { - if (idValues?.contains((Id) record.get(sObjectField)) == false) - { - result.add(record); - } - } - return result; - } - - @TestVisible - protected virtual List getRecords(Schema.SObjectField sObjectField, Boolean value) - { - List result = new List(); - for (SObject record : getRecords()) - { - if ((Boolean) record.get(sObjectField) == value) - { - result.add(record); - } - } - 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 getRelatedFieldByDomainType() - { - return new Map(); - } - - /** - * Get a map with the record mapped to the given Id field value - * Key fields containing null values are omitted - * - * @param sObjectField The field to use as key for the map - * - * @return Returns a map with the record mapped to the given Id field value - * - * @example - * Account account = Account.newInstance(records); - * Map accountById = account.getSObjectByIdField(Account.Id); - */ - @TestVisible - protected virtual Map getSObjectByIdField(Schema.SObjectField sObjectField) - { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(sObjectField) == null) continue; - - result.put((Id) record.get(sObjectField), record); - } - return result; - } - - /** - * Get a map with the records mapped to the given Id field value - * Key fields containing null values are omitted - * - * @param sObjectField The field to use as key for the map - * - * @return Returns a map with the records mapped to the given Id field value - * - * @example - * Contacts contacts = Contacts.newInstance(records); - * Map> contactsByAccountId = contacts.getSObjectsByIdField(Contact.AccountId); - */ - @TestVisible - protected virtual Map> getSObjectsByIdField(Schema.SObjectField sObjectField) - { - Map> result = new Map>(); - for (SObject record : getRecords()) - { - Id fieldId = (Id) record.get(sObjectField); - if (fieldId == null) continue; - - if (!result.containsKey(fieldId)) - { - result.put(fieldId, new List()); - } - result.get(fieldId).add(record); - } - return result; - } - - /** - * Get a map with the record mapped to the given String field value - * Key fields containing null values are omitted - * - * @param sObjectField The field to use as key for the map - * - * @return Returns a map with the record mapped to the given String field value - * - * @example - * Account account = Account.newInstance(records); - * Map accountByNumber = account.getSObjectByStringField(Account.AccountNumber); - */ - @TestVisible - protected virtual Map getSObjectByStringField(Schema.SObjectField sObjectField) - { - Map result = new Map(); - for (SObject record : getRecords()) - { - if (record.get(sObjectField) == null) continue; - - String fieldValue = (String) record.get(sObjectField); - result.put(fieldValue, record); - } - return result; - } - - /** - * Get a map with the records mapped to the given String field value - * Key fields containing null values are omitted - * - * @param sObjectField The field to use as key for the map - * - * @return Returns a map with the records mapped to the given String field value - * - * @example - * Accounts accounts = Account.newInstance(records); - * Map> accountByName = accounts.getSObjectsByStringField(Account.AccountName); - */ - @TestVisible - protected virtual Map> getSObjectsByStringField(Schema.SObjectField sObjectField) - { - Map> result = new Map>(); - for (SObject record : getRecords()) - { - String fieldValue = (String) record.get(sObjectField); - if (fieldValue == null) continue; - - if (!result.containsKey(fieldValue)) - { - result.put(fieldValue, new List()); - } - result.get(fieldValue).add(record); - } - return result; - } - - /** - * Get a map with the given String field value mapped to the given Id field - * Key fields containing null values are omitted - * - * @param valueField The String field to use as value for the map - * @param keyField The Id field to use as key for the map - * - * @return a map with the given String field value mapped to the given Id field - * - * @example - * Account account = Account.newInstance(records); - * Map accountNameById = account.getStringFieldByIdField(Account.AccountName, Account.Id); - */ - @TestVisible - protected virtual Map getStringFieldByIdField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - Map fieldByField = getFieldByField(valueField, keyField); - for (Object obj : fieldByField.keySet()) - { - result.put((Id) obj, (String) fieldByField.get(obj)); - } - return result; - } - - /** - * Get a map with the given Date field value mapped to the given Id field - * Key fields containing null values are omitted - * - * @param valueField The Date field to use as value for the map - * @param keyField The Id field to use as key for the map - * - * @return a map with the given Date field value mapped to the given Id field - * - * @example - * Account account = Account.newInstance(records); - * Map createdDateById = account.getDateFieldByIdField(Account.CreatedDate, Account.Id); - */ - @TestVisible - protected virtual Map getDateFieldByIdField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - Map fieldByField = getFieldByField(valueField, keyField); - for (Object obj : fieldByField.keySet()) - { - Object value = fieldByField.get(obj); - Date dateValue; - if (value instanceof Date) - { - dateValue = (Date) value; - } - else if (value != null) - { - dateValue = ((Datetime) value).date(); - } - result.put((Id) obj, dateValue); - } - return result; - } - - /** - * Get a map with the given Date field value mapped to the given Id field - * Key fields containing null values are omitted - * - * @param valueField The DateTime field to use as value for the map - * @param keyField The Id field to use as key for the map - * - * @return a map with the given Date field value mapped to the given Id field - * - * @example - * Account account = Account.newInstance(records); - * Map createdDateById = account.getDateFieldByIdField(Account.CreatedDate, Account.Id); - */ - @TestVisible - protected virtual Map getDateTimeFieldByIdField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - Map fieldByField = getFieldByField(valueField, keyField); - for (Object obj : fieldByField.keySet()) - { - result.put((Id) obj, (Datetime) fieldByField.get(obj)); - } - return result; - } - - /** - * Get a map with the records mapped to the given String field value - * Key fields containing null values are omitted - * - * @param valueField The field to use as value for the map - * @param keyField The field to use as key for the map - * - * @return Returns a map with the record mapped to the given Id field value - * - * @example - * Account account = Account.newInstance(records); - * Map accountNameById = account.getStringFieldByStringField(Account.AccountName, Account.Id); - */ - @TestVisible - protected virtual Map getStringFieldByStringField(Schema.SObjectField valueField, Schema.SObjectField keyField) - { - Map result = new Map(); - Map fieldByField = getFieldByField(keyField, valueField); - for (Object obj : fieldByField.keySet()) - { - result.put((String) obj, (String) fieldByField.get(obj)); - } - return result; - } - - - /** - * @param sObjectField The Schema.SObjectField to get their values - * @param criteria The criteria condition that must be met to include the value in the return - * - * @return The String values of the given sObjectField - */ - @TestVisible - protected virtual fflib_Strings getStringFieldValues(Schema.SObjectField sObjectField, fflib_Criteria criteria) - { - return new fflib_Strings(new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .getStringFieldValues(sObjectField)); - } - - /** - * Sets the given value to the given field only when the criteria is met for the record - * - * @param sObjectField The Schema.SObjectField to change its value - * @param value The new value for the given sObjectField - * @param criteria The criteria condition that must be met to include the value in the return - */ - @TestVisible - protected virtual void setFieldValue(Schema.SObjectField sObjectField, Object value, fflib_Criteria criteria) - { - new fflib_SObjects2(getRecords(criteria), getSObjectType()) - .setFieldValue(sObjectField, value); - } - - /** - * Sets a value to the given field only when key field Id value is provided in the given map - * - * @param sObjectIdFieldToCheck The SObject Id Field to match the key against in the provided map - * @param sObjectFieldToUpdate The SObjectField to store the mapped value when the key matches the value in the sObjectFieldToUpdate field - * @param values Map of values to store by the sObjectIdFieldToCheck fields value - */ - @TestVisible - protected virtual void setFieldValue( - Schema.SObjectField sObjectIdFieldToCheck, - Schema.SObjectField sObjectFieldToUpdate, - Map values) - { - for (SObject record : getRecords()) - { - Id keyValue = (Id) record.get(sObjectIdFieldToCheck); - if (values?.containsKey(keyValue)) - { - record.put(sObjectFieldToUpdate, values.get(keyValue)); - } - } - } - - /** - * Sets a value to the given field only when key field String value is provided in the given map - * - * @param sObjectStringFieldToCheck The SObject String Field to match the key against in the provided map - * @param sObjectFieldToUpdate The SObjectField to store the mapped value when the key matches the value in the sObjectFieldToUpdate field - * @param values Map of values to store by the sObjectIdFieldToCheck fields value - */ - @TestVisible - protected virtual void setFieldValue( - Schema.SObjectField sObjectStringFieldToCheck, - Schema.SObjectField sObjectFieldToUpdate, - Map values) - { - for (SObject record : getRecords()) - { - String keyValue = (String) record.get(sObjectStringFieldToCheck); - if (values?.containsKey(keyValue)) - { - record.put(sObjectFieldToUpdate, values.get(keyValue)); - } - } - - } - - public class DeveloperException extends Exception { } -} \ No newline at end of file +public virtual class fflib_SObjects2 extends fflib_SObjects implements fflib_ISObjects2 { + protected Schema.DescribeSObjectResult SObjectDescribe { get; private set; } + + /** + * Class constructors + * + * @param objects The Objects of type SObjectType, to contain inside the domain + */ + public fflib_SObjects2(List objects) { + // todo - assert of objects of type SObject + this((List) objects); + } + + /** + * Class constructor + * + * @param records The records to contain inside the domain + */ + public fflib_SObjects2(List records) { + this(records, records.getSObjectType()); + } + + /** + * Class constructor + * + * @param records The records to contain inside the domain + * @param sObjectType The SObjectType of the records + */ + public fflib_SObjects2( + List records, + Schema.SObjectType sObjectType + ) { + super(records, sObjectType); + SObjectDescribe = sObjectType.getDescribe(); + } + + /** + * @param id Domain containing primary key (Id field) values + * + * @return Returns only the SObjects from the domain matching the given Ids. + */ + public virtual SObject getRecord(Id id) { + List result = getRecords(new Set{ id }); + + return (result.size() == 0) ? null : result.get(0); + } + + /** + * Creates a map between the record Ids of this domain to the last given grandparent domain. + * + * @param domainsByRelationshipField A Map of domains and the 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. + * + * @example + * ICases casesDomain = Cases.newInstance(caseRecords); + * IContacts contactsDomain = Contacts.newInstance(contactRecords); + * IAccount accountDomain = Accounts.newInstance(accountRecords); + * + * // A map with key - Case.Id to value - Account.Id + * Map accountIdByCaseId = + * casesDomain.getParentIdById( + * new Map + * { + * Schema.Case.ContactId => contactDomain, + * Schema.Contact.AccountId => accountDomain + * }); + */ + public virtual Map getParentIdById( + Map domainsByRelationshipField + ) { + // Takes the highest related domain from the domainsByRelationshipField + Schema.SObjectField relationshipField = new List( + domainsByRelationshipField.keySet() + ) + .get(0); + fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove( + relationshipField + ); + + // Extract the Id values of the related SObjectType + Map result = getIdFieldByIdField( + relationshipField, + getSObjectType().getDescribe().fields.getMap().get('Id') + ); + + // Check if we need to resolve another parent relation, if not return the results + if (domainsByRelationshipField.isEmpty()) + return result; + + // Fetch the Ids of the related SObjectType + Map parentIdByChildId = relatedDomain.getParentIdById( + domainsByRelationshipField + ); + + // Replace the maps value (ParentId) with the grand parent Id + return fflib_ArrayUtils.replaceValue(result, parentIdByChildId); + } + + /** DEPRECATED **/ + public virtual Map getGrandParentIdById( + List relatedDomains + ) { + System.debug( + LoggingLevel.WARN, + 'The \'getGrandParentIdById(List relatedDomains)\' method is deprecated,' + + 'please refactor to \'getParentIdById(Map relatedDomains)\'' + ); + + fflib_ISObjects2 relatedDomain = relatedDomains.remove(0); + SObjectType relatedDomainType = relatedDomain.getSObjectType(); + Map relatedFieldByDomainType = getRelatedFieldByDomainType(); + + if (relatedFieldByDomainType.containsKey(relatedDomainType) == false) + throw new DeveloperException( + String.format( + 'RelatedDomain type {0} not present in method getRelatedFieldByDomainType', + new List{ String.valueOf(relatedDomainType) } + ) + ); + + Map 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 grandChildIdByChildId = relatedDomain.getGrandParentIdById( + relatedDomains + ); + + // Replace the maps value (childId) with the grand child Id + return fflib_ArrayUtils.replaceValue(result, grandChildIdByChildId); + } + + /** + * Creates a map between the record Ids of this domain to the last given child domain. + * This method is using getRelatedFieldByDomainType to determine which SObjectField + * is related to the given parent domain + * + * @param domainsByRelationshipField A Map of domains in hierarchical order from first child to grandchild + * + * @return Returns a map with KEY: The Ids of the records in this domain, + * to the VALUE: The record Ids of the lowest child related record listed in the relatedDomains, + * null if no grandchild exists. + * + * @example + * ICases casesDomain = Cases.newInstance(caseRecords); + * IContacts contactsDomain = Contacts.newInstance(contactRecords); + * IAccount accountDomain = Accounts.newInstance(accountRecords); + * + * // A map with key - Case.Id to value - Account.Id + * Map> caseIdsByAccountId = + * accountsDomain.getChildIdsById( + * new Map + * { + * Schema.Contact.AccountId => contactsDomain + * Schema.Case.ContactId => casesDomain, + * }); + */ + public virtual Map> getChildIdsById( + Map domainsByRelationshipField + ) { + // Takes the highest related domain from the domainsByRelationshipField + Schema.SObjectField relationshipField = new List( + domainsByRelationshipField.keySet() + ) + .get(0); + fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove( + relationshipField + ); + + Map> result = relatedDomain.getIdFieldsByIdField( + relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), + relationshipField + ); + + // Check if we need to resolve another child relation, if not return the results + if (domainsByRelationshipField.isEmpty()) + return result; + + Map> childIdsById = relatedDomain.getChildIdsById( + domainsByRelationshipField + ); + + // Replace the maps value (childIds) with the grand child Id + return fflib_ArrayUtils.replaceValues(result, childIdsById); + } + + /** DEPRECATED **/ + public virtual Map> getChildIdsById( + List relatedDomains + ) { + System.debug( + LoggingLevel.WARN, + 'The \'getChildIdsById(List relatedDomains)\' method is deprecated,' + + 'please refactor to \'getChildIdsById(Map relatedDomains)\'' + ); + + SObjectType thisDomainType = getSObjectType(); + fflib_SObjects2 relatedDomain = relatedDomains.remove(0); + Map relatedFieldByDomainType = relatedDomain.getRelatedFieldByDomainType(); + + if (relatedFieldByDomainType.containsKey(thisDomainType) == false) + throw new DeveloperException( + String.format( + 'RelatedDomain type {0} not present in method getRelatedFieldByDomainType', + new List{ String.valueOf(thisDomainType) } + ) + ); + + SObjectField relatedLookupFieldOnChild = relatedFieldByDomainType.get( + thisDomainType + ); + Map> result = relatedDomain.getIdFieldsByIdField( + relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), + relatedLookupFieldOnChild + ); + + // Check if we need to resolve another child relation, if not return the results + if (relatedDomains.isEmpty()) + return result; + + Map> childIdsById = relatedDomain.getChildIdsById( + relatedDomains + ); + + // Replace the maps value (childIds) with the grand child Id + return fflib_ArrayUtils.replaceValues(result, childIdsById); + } + + /** + * @return Returns the contents of the Domain by their primary Key ('Id' field) + */ + public virtual Map getSObjectById() { + return new Map(getRecords()); + } + + /** + * @param ids Domain containing primary key (Id field) values + * + * @return Returns only the SObjects from the domain matching the given Ids. + */ + public virtual List getRecords(fflib_Ids ids) { + return getRecords(ids.getIds()); + } + + /** + * @param ids A Set containing primary key (Id field) values + * + * @return Returns only the SObjects from the domain matching the given Ids. + */ + public virtual List getRecords(Set ids) { + Map sObjectsByIds = getSObjectById(); + List result = new List(); + for (Id id : ids) { + if (sObjectsByIds.containsKey(id) == false) + continue; + + result.add(sObjectsByIds.get(id)); + } + return result; + } + + /** + * @param criteria The condition the returned record must meet + * + * @return Return the SObject records contained in the domain matching the criteria + */ + public virtual List getRecords(fflib_Criteria criteria) { + List result = new List(); + for (SObject record : getRecords()) { + if (!criteria.evaluate(record)) + continue; + + result.add(record); + } + return result; + } + + /** + * @return Trip the records from fields that the user doesn't have access to + */ + public virtual List stripInaccessibleFields() { + return stripInaccessibleFields(AccessType.READABLE); + } + + /** + * @param accessType + * + * @return Trip the records from fields that the user doesn't have access to + */ + public virtual List stripInaccessibleFields( + System.AccessType accessType + ) { + return Security.stripInaccessible(accessType, getRecords()).getRecords(); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * + * @return The Date values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Dates getDateFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + result.add((Date) fieldValue); + } + return new fflib_Dates(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Date values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Dates getDateFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getDateFieldValues(sObjectField); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * + * @return The DateTime values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_DateTimes getDateTimeFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + result.add((Datetime) fieldValue); + } + return new fflib_DateTimes(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The DateTime values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_DateTimes getDateTimeFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getDateTimeFieldValues(sObjectField); + } + + /** + * @param decimalField The field to use as the Value of the returned Map + * @param idField The field to use as the Key of the returned map + * + * @return Returns a map with the values of two fields + */ + @TestVisible + protected virtual Map getDecimalById( + Schema.SObjectField decimalField, + Schema.SObjectField idField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(idField) == null) + continue; + + result.put((Id) record.get(idField), (Decimal) record.get(decimalField)); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to get the SUM of + * + * @return The total SUM of the given field + */ + @TestVisible + protected virtual Decimal getDecimalFieldSum( + Schema.SObjectField sObjectField + ) { + Decimal result = 0; + for (SObject record : getRecords()) { + result += record.get(sObjectField) == null + ? 0 + : (Decimal) record.get(sObjectField); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * + * @return The Decimal values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Decimals getDecimalFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + result.add((Decimal) fieldValue); + } + return new fflib_Decimals(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Decimal values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Decimals getDecimalFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getDecimalFieldValues(sObjectField); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * + * @return The Double values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Doubles getDoubleFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + result.add((Double) fieldValue); + } + return new fflib_Doubles(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Double values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Doubles getDoubleFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getDoubleFieldValues(sObjectField); + } + + /** + * Get a map with the values of two fields. + * Key fields containing null values are omitted + * + * @param valueField The field to use as the Value of the Map + * @param keyField The field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Accounts accounts = Accounts.newInstance(records); + * Map accountNameById = accounts.getFieldByField(Account.Name, Account.Id); + */ + @TestVisible + protected virtual Map getFieldByField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(keyField) == null) + continue; + + result.put(record.get(keyField), record.get(valueField)); + } + return result; + } + + /** + * Get a map with the values of two fields + * Key fields containing null values are omitted + * + * @param valueField The field to use as the Value of the Map + * @param keyField The field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Contacts contacts = Contacts.newInstance(records); + * Map> contactIdByAccountId = contacts.getFieldsByField(Contact.Id, Contact.AccountId); + */ + @TestVisible + protected virtual Map> getFieldsByField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map> result = new Map>(); + for (SObject record : getRecords()) { + Object keyFieldValue = record.get(keyField); + if (keyFieldValue == null) + continue; + + if (!result.containsKey(keyFieldValue)) { + result.put(keyFieldValue, new Set()); + } + result.get(keyFieldValue).add(record.get(valueField)); + } + return result; + } + + /** + * @return Returns only the first SObject contained in the domain. + */ + public SObject getFirstSObject() { + if (isEmpty()) + return null; + + return getRecords().get(0); + } + + /** + * Get a map with the values of a String field as key and Id field + * Key fields containing null values are omitted + * + * @param valueField The Id field to use as the Value of the Map + * @param keyField The String field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Contacts contacts = Contacts.newInstance(records); + * Map accountIdByContactId = contacts.getIdFieldByStringField(Contact.Id, Contact.Name); + */ + @TestVisible + protected virtual Map getIdFieldByStringField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(keyField) == null) + continue; + + result.put((String) record.get(keyField), (Id) record.get(valueField)); + } + return result; + } + + /** + * Get a map with the values of two Id fields + * Key fields containing null values are omitted + * + * @param valueField The Id field to use as the Value of the Map + * @param keyField The Id field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Contacts contacts = Contacts.newInstance(records); + * Map accountIdByContactId = contacts.getIdFieldByIdField(Contact.AccountId, Contact.Id); + */ + @TestVisible + protected virtual Map getIdFieldByIdField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(keyField) == null) + continue; + + result.put((Id) record.get(keyField), (Id) record.get(valueField)); + } + return result; + } + + /** + * Get a map with the values of two Id fields with a one to many relation + * Key fields containing null values are omitted + * + * @param valueField The Id field to use as the Value of the Map + * @param keyField The Id field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Contacts contacts = Contacts.newInstance(records); + * Map> contactIdByAccountId = contacts.getIdFieldsByIdField(Contact.Id, Contact.AccountId); + */ + @TestVisible + protected virtual Map> getIdFieldsByIdField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map> result = new Map>(); + for (SObject record : getRecords()) { + Id keyFieldId = (Id) record.get(keyField); + if (keyFieldId == null) + continue; + + if (!result.containsKey(keyFieldId)) { + result.put(keyFieldId, new Set()); + } + result.get(keyFieldId).add((Id) record.get(valueField)); + } + return result; + } + + /** + * Get a map with the values of an Id field by a String field with a one to many relation + * Key fields containing null values are omitted + * + * @param valueField The Id field to use as the Value of the Map + * @param keyField The String field to use as the Key of the map + * + * @return Returns a map with the values of two fields + * + * @example + * Accounts accounts = Accounts.newInstance(records); + * Map> accountIdsByCountry = accounts.getIdFieldsByStringField(Account.BillingCountry, Account.Id); + */ + @TestVisible + protected virtual Map> getIdFieldsByStringField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map> result = new Map>(); + for (SObject record : getRecords()) { + String keyFieldString = String.valueOf(record.get(keyField)); + if (keyFieldString == null) + continue; + + if (!result.containsKey(keyFieldString)) { + result.put(keyFieldString, new Set()); + } + result.get(keyFieldString).add((Id) record.get(valueField)); + } + return result; + } + + /** + * Gets the Id values, but with the option to omit null values + * + * @param field The Schema.SObjectField to get their Id values + * @param omitNulls If set to TRUE, null values will be removed from the result + * + * @return The Id values of the given sObjectField, null values are omitted + */ + @TestVisible + protected Set getIdFieldValues( + Schema.SObjectField field, + Boolean omitNulls + ) { + Set result = getIdFieldValues(field); + + if (omitNulls) { + result.remove(null); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to get their Id values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Id values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Ids getIdFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_Ids( + new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getIdFieldValues(sObjectField) + ); + } + + /** + * @param sObjectField The Schema.SObjectField to get their values + * + * @return The Integer values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Integers getIntegerFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + + result.add(Integer.valueOf(fieldValue)); + } + return new fflib_Integers(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Integer values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Integers getIntegerFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getIntegerFieldValues(sObjectField); + } + + /** + * @param sObjectField The Schema.SObjectField to get their values + * + * @return The Long values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Longs getLongFieldValues( + Schema.SObjectField sObjectField + ) { + Set result = new Set(); + for (SObject record : getRecords()) { + Object fieldValue = record.get(sObjectField); + if (fieldValue == null) + continue; + result.add((Long) fieldValue); + } + return new fflib_Longs(result); + } + + /** + * @param sObjectField The Schema.SObjectField to get their values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The Long values of the given sObjectField, null values are omitted + */ + @TestVisible + protected virtual fflib_Longs getLongFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getLongFieldValues(sObjectField); + } + + @TestVisible + protected virtual List getRecords( + Schema.SObjectField sObjectField, + Id idValue + ) { + return getRecords(sObjectField, new Set{ idValue }); + } + + @TestVisible + protected virtual List getRecords( + Schema.SObjectField sObjectField, + Set values + ) { + List result = new List(); + for (SObject record : getRecords()) { + if (values?.contains((Id) record.get(sObjectField))) { + result.add(record); + } + } + return result; + } + + @TestVisible + protected virtual List getRecords( + Schema.SObjectField sObjectField, + String value + ) { + return getRecords(sObjectField, new Set{ value }); + } + + @TestVisible + protected virtual List getRecords( + Schema.SObjectField sObjectField, + Set values + ) { + List result = new List(); + for (SObject record : getRecords()) { + if (values?.contains((String) record.get(sObjectField))) { + result.add(record); + } + } + return result; + } + + @TestVisible + protected virtual List getRecordsIsNot( + Schema.SObjectField sObjectField, + String value + ) { + return getRecordsNotIn(sObjectField, new Set{ value }); + } + + @TestVisible + protected virtual List getRecordsNotIn( + Schema.SObjectField sObjectField, + Set values + ) { + List result = new List(); + for (SObject record : getRecords()) { + if (values?.contains((String) record.get(sObjectField))) + continue; + result.add(record); + } + return result; + } + + @TestVisible + protected virtual List getRecordsIsNot( + Schema.SObjectField sObjectField, + Id idValue + ) { + return getRecordsNotIn(sObjectField, new Set{ idValue }); + } + + @TestVisible + protected virtual List getRecordsNotIn( + Schema.SObjectField sObjectField, + Set idValues + ) { + List result = new List(); + for (SObject record : getRecords()) { + if (idValues?.contains((Id) record.get(sObjectField)) == false) { + result.add(record); + } + } + return result; + } + + @TestVisible + protected virtual List getRecords( + Schema.SObjectField sObjectField, + Boolean value + ) { + List result = new List(); + for (SObject record : getRecords()) { + if ((Boolean) record.get(sObjectField) == value) { + result.add(record); + } + } + 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 getRelatedFieldByDomainType() { + return new Map(); + } + + /** + * Get a map with the record mapped to the given Id field value + * Key fields containing null values are omitted + * + * @param sObjectField The field to use as key for the map + * + * @return Returns a map with the record mapped to the given Id field value + * + * @example + * Account account = Account.newInstance(records); + * Map accountById = account.getSObjectByIdField(Account.Id); + */ + @TestVisible + protected virtual Map getSObjectByIdField( + Schema.SObjectField sObjectField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(sObjectField) == null) + continue; + + result.put((Id) record.get(sObjectField), record); + } + return result; + } + + /** + * Get a map with the records mapped to the given Id field value + * Key fields containing null values are omitted + * + * @param sObjectField The field to use as key for the map + * + * @return Returns a map with the records mapped to the given Id field value + * + * @example + * Contacts contacts = Contacts.newInstance(records); + * Map> contactsByAccountId = contacts.getSObjectsByIdField(Contact.AccountId); + */ + @TestVisible + protected virtual Map> getSObjectsByIdField( + Schema.SObjectField sObjectField + ) { + Map> result = new Map>(); + for (SObject record : getRecords()) { + Id fieldId = (Id) record.get(sObjectField); + if (fieldId == null) + continue; + + if (!result.containsKey(fieldId)) { + result.put(fieldId, new List()); + } + result.get(fieldId).add(record); + } + return result; + } + + /** + * Get a map with the record mapped to the given String field value + * Key fields containing null values are omitted + * + * @param sObjectField The field to use as key for the map + * + * @return Returns a map with the record mapped to the given String field value + * + * @example + * Account account = Account.newInstance(records); + * Map accountByNumber = account.getSObjectByStringField(Account.AccountNumber); + */ + @TestVisible + protected virtual Map getSObjectByStringField( + Schema.SObjectField sObjectField + ) { + Map result = new Map(); + for (SObject record : getRecords()) { + if (record.get(sObjectField) == null) + continue; + + String fieldValue = (String) record.get(sObjectField); + result.put(fieldValue, record); + } + return result; + } + + /** + * Get a map with the records mapped to the given String field value + * Key fields containing null values are omitted + * + * @param sObjectField The field to use as key for the map + * + * @return Returns a map with the records mapped to the given String field value + * + * @example + * Accounts accounts = Account.newInstance(records); + * Map> accountByName = accounts.getSObjectsByStringField(Account.AccountName); + */ + @TestVisible + protected virtual Map> getSObjectsByStringField( + Schema.SObjectField sObjectField + ) { + Map> result = new Map>(); + for (SObject record : getRecords()) { + String fieldValue = (String) record.get(sObjectField); + if (fieldValue == null) + continue; + + if (!result.containsKey(fieldValue)) { + result.put(fieldValue, new List()); + } + result.get(fieldValue).add(record); + } + return result; + } + + /** + * Get a map with the given String field value mapped to the given Id field + * Key fields containing null values are omitted + * + * @param valueField The String field to use as value for the map + * @param keyField The Id field to use as key for the map + * + * @return a map with the given String field value mapped to the given Id field + * + * @example + * Account account = Account.newInstance(records); + * Map accountNameById = account.getStringFieldByIdField(Account.AccountName, Account.Id); + */ + @TestVisible + protected virtual Map getStringFieldByIdField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + Map fieldByField = getFieldByField(valueField, keyField); + for (Object obj : fieldByField.keySet()) { + result.put((Id) obj, (String) fieldByField.get(obj)); + } + return result; + } + + /** + * Get a map with the given Date field value mapped to the given Id field + * Key fields containing null values are omitted + * + * @param valueField The Date field to use as value for the map + * @param keyField The Id field to use as key for the map + * + * @return a map with the given Date field value mapped to the given Id field + * + * @example + * Account account = Account.newInstance(records); + * Map createdDateById = account.getDateFieldByIdField(Account.CreatedDate, Account.Id); + */ + @TestVisible + protected virtual Map getDateFieldByIdField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + Map fieldByField = getFieldByField(valueField, keyField); + for (Object obj : fieldByField.keySet()) { + Object value = fieldByField.get(obj); + Date dateValue; + if (value instanceof Date) { + dateValue = (Date) value; + } else if (value != null) { + dateValue = ((Datetime) value).date(); + } + result.put((Id) obj, dateValue); + } + return result; + } + + /** + * Get a map with the given Date field value mapped to the given Id field + * Key fields containing null values are omitted + * + * @param valueField The DateTime field to use as value for the map + * @param keyField The Id field to use as key for the map + * + * @return a map with the given Date field value mapped to the given Id field + * + * @example + * Account account = Account.newInstance(records); + * Map createdDateById = account.getDateFieldByIdField(Account.CreatedDate, Account.Id); + */ + @TestVisible + protected virtual Map getDateTimeFieldByIdField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + Map fieldByField = getFieldByField(valueField, keyField); + for (Object obj : fieldByField.keySet()) { + result.put((Id) obj, (Datetime) fieldByField.get(obj)); + } + return result; + } + + /** + * Get a map with the records mapped to the given String field value + * Key fields containing null values are omitted + * + * @param valueField The field to use as value for the map + * @param keyField The field to use as key for the map + * + * @return Returns a map with the record mapped to the given Id field value + * + * @example + * Account account = Account.newInstance(records); + * Map accountNameById = account.getStringFieldByStringField(Account.AccountName, Account.Id); + */ + @TestVisible + protected virtual Map getStringFieldByStringField( + Schema.SObjectField valueField, + Schema.SObjectField keyField + ) { + Map result = new Map(); + Map fieldByField = getFieldByField(keyField, valueField); + for (Object obj : fieldByField.keySet()) { + result.put((String) obj, (String) fieldByField.get(obj)); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to get their values + * @param criteria The criteria condition that must be met to include the value in the return + * + * @return The String values of the given sObjectField + */ + @TestVisible + protected virtual fflib_Strings getStringFieldValues( + Schema.SObjectField sObjectField, + fflib_Criteria criteria + ) { + return new fflib_Strings( + new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .getStringFieldValues(sObjectField) + ); + } + + /** + * Sets the given value to the given field only when the criteria is met for the record + * + * @param sObjectField The Schema.SObjectField to change its value + * @param value The new value for the given sObjectField + * @param criteria The criteria condition that must be met to include the value in the return + */ + @TestVisible + protected virtual void setFieldValue( + Schema.SObjectField sObjectField, + Object value, + fflib_Criteria criteria + ) { + new fflib_SObjects2(getRecords(criteria), getSObjectType()) + .setFieldValue(sObjectField, value); + } + + /** + * Sets a value to the given field only when key field Id value is provided in the given map + * + * @param sObjectIdFieldToCheck The SObject Id Field to match the key against in the provided map + * @param sObjectFieldToUpdate The SObjectField to store the mapped value when the key matches the value in the sObjectFieldToUpdate field + * @param values Map of values to store by the sObjectIdFieldToCheck fields value + */ + @TestVisible + protected virtual void setFieldValue( + Schema.SObjectField sObjectIdFieldToCheck, + Schema.SObjectField sObjectFieldToUpdate, + Map values + ) { + for (SObject record : getRecords()) { + Id keyValue = (Id) record.get(sObjectIdFieldToCheck); + if (values?.containsKey(keyValue)) { + record.put(sObjectFieldToUpdate, values.get(keyValue)); + } + } + } + + /** + * Sets a value to the given field only when key field String value is provided in the given map + * + * @param sObjectStringFieldToCheck The SObject String Field to match the key against in the provided map + * @param sObjectFieldToUpdate The SObjectField to store the mapped value when the key matches the value in the sObjectFieldToUpdate field + * @param values Map of values to store by the sObjectIdFieldToCheck fields value + */ + @TestVisible + protected virtual void setFieldValue( + Schema.SObjectField sObjectStringFieldToCheck, + Schema.SObjectField sObjectFieldToUpdate, + Map values + ) { + for (SObject record : getRecords()) { + String keyValue = (String) record.get(sObjectStringFieldToCheck); + if (values?.containsKey(keyValue)) { + record.put(sObjectFieldToUpdate, values.get(keyValue)); + } + } + } + + public class DeveloperException extends Exception { + } +} diff --git a/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls b/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls index 506b241..e0bb4d6 100644 --- a/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls +++ b/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls @@ -29,814 +29,931 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @IsTest(IsParallel=true) -private class fflib_SObjects2Test -{ - @IsTest - static void itShouldAddErrorMessage() - { - SObjectType accountSObjectType = Schema.Account.SObjectType; - final Id idA = fflib_IDGenerator.generate(accountSObjectType); - final Id idB = fflib_IDGenerator.generate(accountSObjectType); - - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Id = idA), - new Account(Id = idB) - } - ); - - domain.addError(Schema.Account.Name,'Oops'); - - System.Assert.areEqual( - 2, - fflib_SObjects.Errors.getAll().size() - ); - } - - @IsTest - static void itShouldClearTheField() - { - Account record = new Account(ShippingCountry = 'Holland'); - DomainAccounts domain = new DomainAccounts(new List {record}); - System.Assert.isFalse(domain.selectByShippingCountry('Holland').isEmpty(), 'Incorrect test data'); - - domain.clearShippingCountry(); - - System.Assert.isTrue(domain.selectByShippingCountry('Holland').isEmpty(), 'Field should have been nullified'); - } - - @IsTest - static void itShouldReturnTheCreatedData() - { - System.Assert.areEqual( - (Object) Schema.Account.SObjectType, - new DomainAccounts(new List()) - .getType(), - 'Unexpected Domain Type' - ); - System.Assert.areEqual( - Schema.Account.SObjectType, - new DomainAccounts(new List()) - .getSObjectType(), - 'Unexpected Domain SObjectType' - ); - } - - @IsTest - static void itShouldReturnTheDomainsType() - { - fflib_Criteria criteria = new fflib_Criteria().equalTo(Account.Name, 'A'); - - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), Name = 'A', ShippingCountry = 'USA', AnnualRevenue = 5, BillingLatitude = 121.0), - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), Name = 'B', ShippingCountry = 'Ireland', AnnualRevenue = 3), - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), Name = 'C', ShippingCountry = 'UK', AnnualRevenue = 2), - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), Name = 'D', ShippingCountry = '', AnnualRevenue = 1), - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), Name = 'E'), - new Account(Id = fflib_IDGenerator.generate(Account.SObjectType), BillingLatitude = 121.0 ) - } - ); - domain.getDateTimeFieldValues(Account.LastActivityDate); - domain.getDateTimeFieldValues(Account.CreatedDate, criteria); - domain.getFieldByField(Account.Name, Account.AnnualRevenue); - domain.getFieldsByField(Account.Id, Account.AnnualRevenue); - domain.getFieldByField(Account.Id, Account.Id); - domain.getIdFieldsByIdField(Account.Id, Account.Id); - domain.getIdFieldsByStringField(Account.Name, Account.Id); - domain.getIdFieldValues(Account.Id); - domain.getIdFieldValues(Account.Id, criteria); - domain.getDecimalFieldValues(Account.BillingLatitude, criteria); - domain.getDoubleFieldValues(Account.BillingLatitude); - domain.getDoubleFieldValues(Account.BillingLatitude, criteria); - domain.getSObjectByIdField(Account.Id); - domain.getSObjectsByIdField(Account.Id); - domain.getSObjectByStringField(Account.Name); - domain.getSObjectsByStringField(Account.Name); - domain.getStringFieldByStringField(Account.Name, Account.AccountNumber); - domain.getStringFieldValues(Account.Name, criteria); - domain.stri - - // todo - add assertions - } - - @IsTest - static void itShouldReturnTheRelatedChildDomainId_deprecated() - { - // GIVEN a case A & Z related to contact A related to Account, - Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); - Id caseIdZ = 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 caseRecordZ = new Case(Id = caseIdZ, ContactId = contactIdA); - Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); - Case caseRecordC = new Case(Id = caseIdC); - - DomainAccounts accountDomain = new DomainAccounts(new List {accountRecord}); - DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); - DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC, caseRecordZ}); - - // WHEN we request the relation between Case and Account - System.Test.startTest(); - Map> result = accountDomain.getChildIdsById( - new List - { - contactDomain, - caseDomain - } - ); - System.Test.stopTest(); - - // THEN it should return the AccountId By CaseId - System.Assert.areEqual(1, result.size(), 'Expected one account'); - System.Assert.isTrue(result.containsKey(accountId), 'Account Id should be the main key'); - System.Assert.areEqual(2, result.get(accountId).size(), 'Incorrect amount of related cases to the account'); - System.Assert.isTrue(result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ })); - } - @IsTest - static void itShouldReturnTheRelatedChildDomainId() - { - // GIVEN a case A & Z related to contact A related to Account, - Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); - Id caseIdZ = 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 caseRecordZ = new Case(Id = caseIdZ, ContactId = contactIdA); - Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); - Case caseRecordC = new Case(Id = caseIdC); - - DomainAccounts accountDomain = new DomainAccounts(new List {accountRecord}); - DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); - DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC, caseRecordZ}); - - // WHEN we request the relation between Case and Account - System.Test.startTest(); - Map> result = accountDomain.getChildIdsById( - new Map - { - Contact.AccountId => contactDomain, - Case.ContactId => caseDomain - } - ); - System.Test.stopTest(); - - // THEN it should return the AccountId By CaseId - System.Assert.areEqual(1, result.size(), 'Expected one account'); - System.Assert.isTrue(result.containsKey(accountId), 'Account Id should be the main key'); - System.Assert.areEqual(2, result.get(accountId).size(), 'Incorrect amount of related cases to the account'); - System.Assert.isTrue(result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ })); - } - - @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 {accountRecord}); - DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); - DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC}); - - // WHEN we request the relation between Case and Account - System.Test.startTest(); - Map result = caseDomain.getParentIdById( - new Map - { - Schema.Case.ContactId => contactDomain, - Schema.Contact.AccountId => accountDomain - } - ); - System.Test.stopTest(); - - // THEN it should return the AccountId By CaseId - System.Assert.areEqual(3, result.size(), 'Expected one related account to the case'); - System.Assert.isTrue(result.containsKey(caseIdA)); - System.Assert.isTrue(result.containsKey(caseIdB)); - System.Assert.isTrue(result.containsKey(caseIdC)); - System.Assert.areEqual(accountId, result.get(caseIdA), 'Account Id should be related to the Case Id A'); - System.Assert.isNull(result.get(caseIdB), 'Case Id B should have no related Account Id'); - System.Assert.isNull(result.get(caseIdC), 'Case Id B should have no related Account Id'); - } - - /** Unit test for the deprecated method **/ - @IsTest - static void itShouldReturnTheRelatedParentDomainId_deprecated() - { - // 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 {accountRecord}); - DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); - DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC}); - - // WHEN we request the relation between Case and Account - System.Test.startTest(); - Map result = caseDomain.getGrandParentIdById( - new List - { - contactDomain, - accountDomain - } - ); - System.Test.stopTest(); - - // THEN it should return the AccountId By CaseId - System.Assert.areEqual(3, result.size(), 'Expected one related account to the case'); - System.Assert.isTrue(result.containsKey(caseIdA)); - System.Assert.isTrue(result.containsKey(caseIdB)); - System.Assert.isTrue(result.containsKey(caseIdC)); - System.Assert.areEqual(accountId, result.get(caseIdA), 'Account Id should be related to the Case Id A'); - System.Assert.isNull(result.get(caseIdB), 'Case Id B should have no related Account Id'); - System.Assert.isNull(result.get(caseIdC), 'Case Id B should have no related Account Id'); - } - - @IsTest - static void itShouldReturnDecimalById() { - final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); - final Account record = new Account(Id = accountId, AnnualRevenue = 123.45); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - System.Test.startTest(); - Map result = domain.getDecimalById(Schema.Account.AnnualRevenue, Schema.Account.Id); - System.Test.stopTest(); - } - - @IsTest - static void itShouldReturnStringById() - { - final String accountName = 'My Account'; - final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); - final Account record = new Account(Id = accountId, Name = accountName); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - System.Test.startTest(); - Map nameById = domain.getIdFieldByStringField(Schema.Account.Id, Schema.Account.Name); - System.Test.stopTest(); - System.Assert.isTrue(nameById.containsKey(accountName)); - System.Assert.areEqual(accountId, nameById.get(accountName)); - } - - @IsTest - static void itShouldReturnStringFieldByIdField() - { - final String accountName = 'My Account'; - final Id accountId = fflib_IDGenerator.generate(Account.SObjectType); - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Id = accountId, Name = accountName), - new Account(Name = accountName + ' 2') // This one should be ignored - }); - - System.Test.startTest(); - Map result = domain.getStringFieldByIdField(Account.Name, Account.Id); - System.Test.stopTest(); - - System.Assert.areEqual(1, result.size(), 'Incorrect returned amount of results'); - System.Assert.isTrue(result.keySet().contains(accountId), 'The accountId is missing from the results'); - System.Assert.areEqual(accountName, result.get(accountId), 'Incorrect returned account name'); - } - - @IsTest - static void itShouldReturnDateFieldByIdField() - { - final Date today = Date.today(); - final Datetime todayDatetime = Datetime.newInstance(today.year(), today.month(), today.day()); - final String accountName = 'My Account'; - final Id accountId = fflib_IDGenerator.generate(Account.SObjectType); - DomainAccounts domain = new DomainAccounts( - new List - { - (Account) fflib_MockSObjectUtil.addFieldValue(new Account(Id = accountId), Account.CreatedDate, todayDatetime), - new Account(Name = accountName + ' 2') // This one should be ignored - }); - - System.Test.startTest(); - Map result = domain.getDateFieldByIdField(Account.CreatedDate, Account.Id); - System.Test.stopTest(); - - System.Assert.areEqual(1, result.size(), 'Incorrect returned amount of results'); - System.Assert.istrue(result.keySet().contains(accountId), 'The accountId is missing from the results'); - System.Assert.areEqual(today, result.get(accountId), 'Incorrect returned date'); - } - - @IsTest - static void itShouldReturnIntegerFieldValues() - { - final Integer numberOfEmployees = 10; - final String accountName = 'Test Account'; - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(NumberOfEmployees = numberOfEmployees), - new Account(Name = accountName, NumberOfEmployees = numberOfEmployees * 2), - new Account() // This one should be ignored - }); - - System.Test.startTest(); - Set result = domain.getIntegerFieldValues(Account.NumberOfEmployees).getIntegerSet(); - Set result2 = - domain.getIntegerFieldValues( - Account.NumberOfEmployees, - new fflib_Criteria().equalTo(Schema.Account.Name, accountName) - ) - .getIntegerSet(); - System.Test.stopTest(); - - System.Assert.areEqual(2, result.size(), 'Incorrect amount of results'); - System.Assert.istrue(result.contains(numberOfEmployees), 'The expected numberOfEmployees was not returned'); - System.Assert.istrue(result.contains(numberOfEmployees * 2), 'The expected numberOfEmployees was not returned'); - - System.Assert.areEqual(1, result2.size(), 'Incorrect amount of results'); - System.Assert.istrue(result2.contains(numberOfEmployees * 2), 'The expected numberOfEmployees was not returned'); - } - - @IsTest - static void itShouldReturnLongFieldValues() - { - final Integer numberOfEmployees = 10; - final String accountName = 'Test Account'; - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(NumberOfEmployees = numberOfEmployees), - new Account(Name = accountName, NumberOfEmployees = numberOfEmployees * 2), - new Account() // This one should be ignored - }); - - System.Test.startTest(); - Set result = domain.getLongFieldValues(Account.NumberOfEmployees).getLongSet(); - Set result2 = - domain.getLongFieldValues( - Account.NumberOfEmployees, - new fflib_Criteria().equalTo(Schema.Account.Name, accountName) - ) - .getLongSet(); - System.Test.stopTest(); - - System.Assert.areEqual(2, result.size(), 'Incorrect amount of results'); - System.Assert.istrue(result.contains(numberOfEmployees), 'The expected numberOfEmployees was not returned'); - System.Assert.istrue(result.contains(numberOfEmployees * 2), 'The expected numberOfEmployees was not returned'); - - System.Assert.areEqual(1, result2.size(), 'Incorrect amount of results'); - System.Assert.istrue(result2.contains(numberOfEmployees * 2), 'The expected numberOfEmployees was not returned'); - } - - @IsTest - static void itShouldReturnRecordIdById() - { - final Id contactId = fflib_IDGenerator.generate(Schema.Contact.SObjectType); - final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); - final Contact record = new Contact(Id = contactId, AccountId = accountId); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - System.Test.startTest(); - Map accountIdByContactId = domain.getIdFieldByIdField(Schema.Contact.AccountId, Schema.Contact.Id); - System.Test.stopTest(); - System.Assert.istrue(accountIdByContactId.containsKey(contactId)); - System.Assert.areEqual(accountId, accountIdByContactId.get(contactId)); - } - - @IsTest - static void itShouldReturnRecordByIds() - { - SObjectType accountSObjectType = Schema.Account.SObjectType; - final Id idA = fflib_IDGenerator.generate(accountSObjectType); - final Id idB = fflib_IDGenerator.generate(accountSObjectType); - final Id idC = fflib_IDGenerator.generate(accountSObjectType); - - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Id = idA), - new Account(Id = idB), - new Account(Id = idC) - } - ); - - Set recordIds = new Set {idA, idB, idC}; - System.Assert.istrue( - domain.getSObjectById().keySet().containsAll(recordIds), - 'The domain should return all the record Ids' - ); - } - - - @IsTest - static void itShouldReturnRecordsIds() - { - SObjectType accountSObjectType = Schema.Account.SObjectType; - Id idA = fflib_IDGenerator.generate(accountSObjectType); - Id idB = fflib_IDGenerator.generate(accountSObjectType); - Id idC = fflib_IDGenerator.generate(accountSObjectType); - - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Id = idA), - new Account(Id = idB), - new Account(Id = idC) - } - ); - - Set recordIds = new Set {idA, idB, idC}; - System.Assert.istrue( - domain.getRecordIds().containsAll(recordIds), - 'The domain should return all the record Ids' - ); - } - - @IsTest - static void itShouldReturnRecords() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(6, domain.getRecords().size()); - } - - @IsTest - static void itShouldReturnRecordByCriteria() - { - final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Name = 'A', Id = idA), - new Account(Name = 'B', Id = fflib_IDGenerator.generate(Schema.Account.SObjectType)) - }); - - System.Assert.areEqual( - 'A', - domain.getRecords( - new fflib_Criteria() - .equalTo(Schema.Account.Id, idA) - ) - .get(0) - .get('Name') - ); - } - - @IsTest - static void itShouldReturnRecordById() - { - final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Name = 'A', Id = idA), - new Account(Name = 'B', Id = fflib_IDGenerator.generate(Schema.Account.SObjectType)) - }); - - System.Assert.areEqual('A', domain.getRecord(idA).get('Name')); - } - - @IsTest - static void itShouldReturnRecordsById() - { - final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); - final fflib_Ids ids = new fflib_Ids(new Set {idA}); - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Name = 'A', Id = idA), - new Account(Name = 'B', Id = fflib_IDGenerator.generate(Schema.Account.SObjectType)) - }); - - System.Assert.areEqual('A', domain.getRecords(ids).get(0).get('Name')); - } - - @IsTest - static void itShouldReturnRecordsByIds() - { - final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Name = 'A', Id = idA), - new Account(Name = 'B', Id = fflib_IDGenerator.generate(Schema.Account.SObjectType)) - }); - - System.Assert.areEqual('A', domain.getRecords(new Set {idA}).get(0).get('Name')); - } - - @IsTest - static void itShouldReturnRecordsWithFieldValues() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(1, domain.selectByShippingCountry('USA').size()); - } - - @IsTest - static void itShouldReturnRecordsWithoutFieldValues() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(3, domain.selectWithoutShippingCountry().size()); - } - - @IsTest - static void itShouldReturnRecordsWithoutAllFieldValues() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(1, domain.selectWithEmptyRecord().size()); - } - - @IsTest - static void itShouldReturnRecordsWithShippingCountry() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(3, domain.selectWithShippingCountry().size()); - } - - @IsTest - static void itShouldReturnRecordsWithAllFieldValues() - { - DomainAccounts domain = generateDomain(); - - System.Assert.areEqual(3, domain.selectPopulatedRecords().size()); - } - - @IsTest - static void itShouldReturnFieldValues() - { - DomainAccounts domain = generateDomain(); - - System.Assert.istrue( - domain.getStringFieldValues(Schema.Account.ShippingCountry) - .containsAll( - new Set - { - 'USA', - 'Ireland', - 'UK', - '' - } - ) - ); - } - - @IsTest - static void itShouldSetFieldValue() - { - DomainAccounts domain = generateDomain(); - String country = 'Holland'; - domain.setShippingCountry(country); - - System.Assert.areEqual(6, domain.selectByShippingCountry(country).size()); - } - - @IsTest - static void itShouldSetFieldValueByCondition() - { - DomainAccounts domain = generateDomain(); - domain.setRatingByShippingCountry( - new Map - { - 'USA' => 'Hot' - } - ); - - System.Assert.areEqual(1, domain.selectByRating('Hot').size()); - } - - @IsTest - static void itShouldGetTheSumOfAnnualRevenue() - { - DomainAccounts domain = generateDomain(); - System.Assert.areEqual(11, domain.getDecimalFieldSum(Account.AnnualRevenue)); - System.Assert.isTrue(domain.getDecimalFieldValues(Account.AnnualRevenue).contains((Decimal) 5)); - } - - @IsTest - static void itShouldSetFieldByIdField() - { - Id accountId = fflib_IDGenerator.generate(Account.SObjectType); - Account record = new Account(Id = accountId, Name = ''); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - - domain.setFieldValue( - Schema.Account.Id, - Schema.Account.Name, - new Map - { - accountId => 'Hello' - } - ); - - System.Assert.areEqual('Hello', domain.getFirstSObject().get('Name')); - } - - @IsTest - static void itShouldSetFieldByStringField() - { - Account record = new Account(Name = 'Hello', Rating = 'Cold'); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - - domain.setFieldValue( - Schema.Account.Name, - Schema.Account.Rating, - new Map - { - 'Hello' => 'Warm' - } - ); - - System.Assert.areEqual('Warm', domain.getFirstSObject().get('Rating')); - } - - @IsTest - static void itShouldSetField() - { - Account record = new Account(Name = 'Hello', Rating = 'Cold'); - fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); - - domain.setFieldValue(Account.Name, 'Hello World', new fflib_Criteria().equalTo(Account.Rating, 'Cold')); - - System.Assert.areEqual('Hello World', domain.getFirstSObject().get('Name')); - } - - private static DomainAccounts generateDomain() - { - DomainAccounts domain = new DomainAccounts( - new List - { - new Account(Name = 'A', ShippingCountry = 'USA', AnnualRevenue = 5), - new Account(Name = 'B', ShippingCountry = 'Ireland', AnnualRevenue = 3), - new Account(Name = 'C', ShippingCountry = 'UK', AnnualRevenue = 2), - new Account(Name = 'D', ShippingCountry = '', AnnualRevenue = 1), - new Account(Name = 'E'), - new Account() - } - ); - return domain; - } - - - private class DomainAccounts extends fflib_SObjects2 - { - public DomainAccounts(List records) - { - super(records, Schema.Account.SObjectType); - } - - public List selectByShippingCountry(String country) - { - return (List) getRecordsByFieldValues( - Schema.Account.ShippingCountry, - new Set {country} - ); - } - - public List selectByRating(String rating) - { - return (List) getRecordsByFieldValue( - Schema.Account.Rating, - rating - ); - } - - public List selectWithoutShippingCountry() - { - return (List) getRecordsWithBlankFieldValues( - Schema.Account.ShippingCountry - ); - } - - public List selectWithShippingCountry() - { - return (List) getRecordsWithNotBlankFieldValues( - Schema.Account.ShippingCountry - ); - } - - public List selectWithEmptyRecord() - { - return (List) getRecordsWithAllBlankFieldValues( - new Set - { - Schema.Account.Name, - Schema.Account.ShippingCountry - } - ); - } - - public List selectPopulatedRecords() - { - return (List) getRecordsWithAllNotBlankFieldValues( - new Set - { - Schema.Account.Name, - Schema.Account.ShippingCountry - } - ); - } - - public void setShippingCountry(String country) - { - setFieldValue(Schema.Account.ShippingCountry, country); - } - - public void setRatingByShippingCountry(Map ratingByCountry) - { - setFieldValueByMap( - Schema.Account.ShippingCountry, - Schema.Account.Rating, - ratingByCountry); - } - - public void addNameError(String message) - { - addError(Schema.Account.Name, message); - } - - public void clearShippingCountry() - { - clearField(Schema.Account.ShippingCountry); - } - - protected override Map getRelatedFieldByDomainType() - { - return new Map - { - Schema.Contact.SObjectType => Contact.AccountId - }; - } - } - - private class DomainContacts extends fflib_SObjects2 - { - public DomainContacts(List records) - { - super(records, Schema.Contact.SObjectType); - } - - protected override Map getRelatedFieldByDomainType() - { - return new Map - { - Schema.Account.SObjectType => Contact.AccountId, - Schema.Case.SObjectType => Case.ContactId - }; - } - } - - private class DomainCases extends fflib_SObjects2 - { - public DomainCases(List records) - { - super(records, Schema.Case.SObjectType); - } - - protected override Map getRelatedFieldByDomainType() - { - return new Map - { - Schema.Contact.SObjectType => Case.ContactId - }; - } - } +private class fflib_SObjects2Test { + @IsTest + static void itShouldAddErrorMessage() { + SObjectType accountSObjectType = Schema.Account.SObjectType; + final Id idA = fflib_IDGenerator.generate(accountSObjectType); + final Id idB = fflib_IDGenerator.generate(accountSObjectType); + + DomainAccounts domain = new DomainAccounts( + new List{ new Account(Id = idA), new Account(Id = idB) } + ); + + domain.addError(Schema.Account.Name, 'Oops'); + + System.Assert.areEqual(2, fflib_SObjects.Errors.getAll().size()); + } + + @IsTest + static void itShouldClearTheField() { + Account record = new Account(ShippingCountry = 'Holland'); + DomainAccounts domain = new DomainAccounts(new List{ record }); + System.Assert.isFalse( + domain.selectByShippingCountry('Holland').isEmpty(), + 'Incorrect test data' + ); + + domain.clearShippingCountry(); + + System.Assert.isTrue( + domain.selectByShippingCountry('Holland').isEmpty(), + 'Field should have been nullified' + ); + } + + @IsTest + static void itShouldReturnTheCreatedData() { + System.Assert.areEqual( + (Object) Schema.Account.SObjectType, + new DomainAccounts(new List()).getType(), + 'Unexpected Domain Type' + ); + System.Assert.areEqual( + Schema.Account.SObjectType, + new DomainAccounts(new List()).getSObjectType(), + 'Unexpected Domain SObjectType' + ); + } + + @IsTest + static void itShouldReturnTheDomainsType() { + fflib_Criteria criteria = new fflib_Criteria().equalTo(Account.Name, 'A'); + + DomainAccounts domain = new DomainAccounts( + new List{ + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'A', + ShippingCountry = 'USA', + AnnualRevenue = 5, + BillingLatitude = 121.0 + ), + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'B', + ShippingCountry = 'Ireland', + AnnualRevenue = 3 + ), + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'C', + ShippingCountry = 'UK', + AnnualRevenue = 2 + ), + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'D', + ShippingCountry = '', + AnnualRevenue = 1 + ), + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + Name = 'E' + ), + new Account( + Id = fflib_IDGenerator.generate(Account.SObjectType), + BillingLatitude = 121.0 + ) + } + ); + domain.getDateTimeFieldValues(Account.LastActivityDate); + domain.getDateTimeFieldValues(Account.CreatedDate, criteria); + domain.getFieldByField(Account.Name, Account.AnnualRevenue); + domain.getFieldsByField(Account.Id, Account.AnnualRevenue); + domain.getFieldByField(Account.Id, Account.Id); + domain.getIdFieldsByIdField(Account.Id, Account.Id); + domain.getIdFieldsByStringField(Account.Id, Account.Name); + domain.getIdFieldValues(Account.Id); + domain.getIdFieldValues(Account.Id, criteria); + domain.getDecimalFieldValues(Account.BillingLatitude, criteria); + domain.getDoubleFieldValues(Account.BillingLatitude); + domain.getDoubleFieldValues(Account.BillingLatitude, criteria); + domain.getSObjectByIdField(Account.Id); + domain.getSObjectsByIdField(Account.Id); + domain.getSObjectByStringField(Account.Name); + domain.getSObjectsByStringField(Account.Name); + domain.getStringFieldByStringField(Account.Name, Account.AccountNumber); + domain.getStringFieldValues(Account.Name, criteria); + domain.stripInaccessibleFields(); + domain.getDateFieldValues(Account.CreatedDate); + domain.getDateFieldValues(Account.CreatedDate, criteria); + domain.getIdFieldValues(Account.Id, criteria); + domain.getRecords(Account.Id, domain.getFirstSObject().Id); + domain.getRecords(Account.Name, 'A'); + domain.getRecordsIsNot(Account.Name, 'A'); + domain.getRecordsIsNot(Account.Id, domain.getFirstSObject().Id); + domain.getSObjectByIdField(Account.Id); + domain.getIdFieldValues(Account.Id, true); + // todo - add assertions + } + + @IsTest + static void itShouldReturnTheRelatedChildDomainId_deprecated() { + // GIVEN a case A & Z related to contact A related to Account, + Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdZ = 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 caseRecordZ = new Case(Id = caseIdZ, ContactId = contactIdA); + Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); + Case caseRecordC = new Case(Id = caseIdC); + + DomainAccounts accountDomain = new DomainAccounts( + new List{ accountRecord } + ); + DomainContacts contactDomain = new DomainContacts( + new List{ contactRecordA, contactRecordB } + ); + DomainCases caseDomain = new DomainCases( + new List{ caseRecordA, caseRecordB, caseRecordC, caseRecordZ } + ); + + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map> result = accountDomain.getChildIdsById( + new List{ contactDomain, caseDomain } + ); + System.Test.stopTest(); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual(1, result.size(), 'Expected one account'); + System.Assert.isTrue( + result.containsKey(accountId), + 'Account Id should be the main key' + ); + System.Assert.areEqual( + 2, + result.get(accountId).size(), + 'Incorrect amount of related cases to the account' + ); + System.Assert.isTrue( + result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ }) + ); + } + @IsTest + static void itShouldReturnTheRelatedChildDomainId() { + // GIVEN a case A & Z related to contact A related to Account, + Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdZ = 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 caseRecordZ = new Case(Id = caseIdZ, ContactId = contactIdA); + Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); + Case caseRecordC = new Case(Id = caseIdC); + + DomainAccounts accountDomain = new DomainAccounts( + new List{ accountRecord } + ); + DomainContacts contactDomain = new DomainContacts( + new List{ contactRecordA, contactRecordB } + ); + DomainCases caseDomain = new DomainCases( + new List{ caseRecordA, caseRecordB, caseRecordC, caseRecordZ } + ); + + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map> result = accountDomain.getChildIdsById( + new Map{ + Contact.AccountId => contactDomain, + Case.ContactId => caseDomain + } + ); + System.Test.stopTest(); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual(1, result.size(), 'Expected one account'); + System.Assert.isTrue( + result.containsKey(accountId), + 'Account Id should be the main key' + ); + System.Assert.areEqual( + 2, + result.get(accountId).size(), + 'Incorrect amount of related cases to the account' + ); + System.Assert.isTrue( + result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ }) + ); + } + + @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{ accountRecord } + ); + DomainContacts contactDomain = new DomainContacts( + new List{ contactRecordA, contactRecordB } + ); + DomainCases caseDomain = new DomainCases( + new List{ caseRecordA, caseRecordB, caseRecordC } + ); + + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map result = caseDomain.getParentIdById( + new Map{ + Schema.Case.ContactId => contactDomain, + Schema.Contact.AccountId => accountDomain + } + ); + System.Test.stopTest(); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual( + 3, + result.size(), + 'Expected one related account to the case' + ); + System.Assert.isTrue(result.containsKey(caseIdA)); + System.Assert.isTrue(result.containsKey(caseIdB)); + System.Assert.isTrue(result.containsKey(caseIdC)); + System.Assert.areEqual( + accountId, + result.get(caseIdA), + 'Account Id should be related to the Case Id A' + ); + System.Assert.isNull( + result.get(caseIdB), + 'Case Id B should have no related Account Id' + ); + System.Assert.isNull( + result.get(caseIdC), + 'Case Id B should have no related Account Id' + ); + } + + /** Unit test for the deprecated method **/ + @IsTest + static void itShouldReturnTheRelatedParentDomainId_deprecated() { + // 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{ accountRecord } + ); + DomainContacts contactDomain = new DomainContacts( + new List{ contactRecordA, contactRecordB } + ); + DomainCases caseDomain = new DomainCases( + new List{ caseRecordA, caseRecordB, caseRecordC } + ); + + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map result = caseDomain.getGrandParentIdById( + new List{ contactDomain, accountDomain } + ); + System.Test.stopTest(); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual( + 3, + result.size(), + 'Expected one related account to the case' + ); + System.Assert.isTrue(result.containsKey(caseIdA)); + System.Assert.isTrue(result.containsKey(caseIdB)); + System.Assert.isTrue(result.containsKey(caseIdC)); + System.Assert.areEqual( + accountId, + result.get(caseIdA), + 'Account Id should be related to the Case Id A' + ); + System.Assert.isNull( + result.get(caseIdB), + 'Case Id B should have no related Account Id' + ); + System.Assert.isNull( + result.get(caseIdC), + 'Case Id B should have no related Account Id' + ); + } + + @IsTest + static void itShouldReturnDecimalById() { + final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); + final Account record = new Account(Id = accountId, AnnualRevenue = 123.45); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + System.Test.startTest(); + Map result = domain.getDecimalById( + Schema.Account.AnnualRevenue, + Schema.Account.Id + ); + System.Test.stopTest(); + } + + @IsTest + static void itShouldReturnStringById() { + final String accountName = 'My Account'; + final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); + final Account record = new Account(Id = accountId, Name = accountName); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + System.Test.startTest(); + Map nameById = domain.getIdFieldByStringField( + Schema.Account.Id, + Schema.Account.Name + ); + System.Test.stopTest(); + System.Assert.isTrue(nameById.containsKey(accountName)); + System.Assert.areEqual(accountId, nameById.get(accountName)); + } + + @IsTest + static void itShouldReturnStringFieldByIdField() { + final String accountName = 'My Account'; + final Id accountId = fflib_IDGenerator.generate(Account.SObjectType); + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Id = accountId, Name = accountName), + new Account(Name = accountName + ' 2') // This one should be ignored + } + ); + + System.Test.startTest(); + Map result = domain.getStringFieldByIdField( + Account.Name, + Account.Id + ); + System.Test.stopTest(); + + System.Assert.areEqual( + 1, + result.size(), + 'Incorrect returned amount of results' + ); + System.Assert.isTrue( + result.keySet().contains(accountId), + 'The accountId is missing from the results' + ); + System.Assert.areEqual( + accountName, + result.get(accountId), + 'Incorrect returned account name' + ); + } + + @IsTest + static void itShouldReturnDateFieldByIdField() { + final Date today = Date.today(); + final Datetime todayDatetime = Datetime.newInstance( + today.year(), + today.month(), + today.day() + ); + final String accountName = 'My Account'; + final Id accountId = fflib_IDGenerator.generate(Account.SObjectType); + DomainAccounts domain = new DomainAccounts( + new List{ + (Account) fflib_MockSObjectUtil.addFieldValue( + new Account(Id = accountId), + Account.CreatedDate, + todayDatetime + ), + new Account(Name = accountName + ' 2') // This one should be ignored + } + ); + + System.Test.startTest(); + Map result = domain.getDateFieldByIdField( + Account.CreatedDate, + Account.Id + ); + System.Test.stopTest(); + + System.Assert.areEqual( + 1, + result.size(), + 'Incorrect returned amount of results' + ); + System.Assert.istrue( + result.keySet().contains(accountId), + 'The accountId is missing from the results' + ); + System.Assert.areEqual( + today, + result.get(accountId), + 'Incorrect returned date' + ); + } + + @IsTest + static void itShouldReturnIntegerFieldValues() { + final Integer numberOfEmployees = 10; + final String accountName = 'Test Account'; + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(NumberOfEmployees = numberOfEmployees), + new Account( + Name = accountName, + NumberOfEmployees = numberOfEmployees * 2 + ), + new Account() // This one should be ignored + } + ); + + System.Test.startTest(); + Set result = domain.getIntegerFieldValues( + Account.NumberOfEmployees + ) + .getIntegerSet(); + Set result2 = domain.getIntegerFieldValues( + Account.NumberOfEmployees, + new fflib_Criteria().equalTo(Schema.Account.Name, accountName) + ) + .getIntegerSet(); + System.Test.stopTest(); + + System.Assert.areEqual(2, result.size(), 'Incorrect amount of results'); + System.Assert.istrue( + result.contains(numberOfEmployees), + 'The expected numberOfEmployees was not returned' + ); + System.Assert.istrue( + result.contains(numberOfEmployees * 2), + 'The expected numberOfEmployees was not returned' + ); + + System.Assert.areEqual(1, result2.size(), 'Incorrect amount of results'); + System.Assert.istrue( + result2.contains(numberOfEmployees * 2), + 'The expected numberOfEmployees was not returned' + ); + } + + @IsTest + static void itShouldReturnLongFieldValues() { + final Integer numberOfEmployees = 10; + final String accountName = 'Test Account'; + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(NumberOfEmployees = numberOfEmployees), + new Account( + Name = accountName, + NumberOfEmployees = numberOfEmployees * 2 + ), + new Account() // This one should be ignored + } + ); + + System.Test.startTest(); + Set result = domain.getLongFieldValues(Account.NumberOfEmployees) + .getLongSet(); + Set result2 = domain.getLongFieldValues( + Account.NumberOfEmployees, + new fflib_Criteria().equalTo(Schema.Account.Name, accountName) + ) + .getLongSet(); + System.Test.stopTest(); + + System.Assert.areEqual(2, result.size(), 'Incorrect amount of results'); + System.Assert.istrue( + result.contains(numberOfEmployees), + 'The expected numberOfEmployees was not returned' + ); + System.Assert.istrue( + result.contains(numberOfEmployees * 2), + 'The expected numberOfEmployees was not returned' + ); + + System.Assert.areEqual(1, result2.size(), 'Incorrect amount of results'); + System.Assert.istrue( + result2.contains(numberOfEmployees * 2), + 'The expected numberOfEmployees was not returned' + ); + } + + @IsTest + static void itShouldReturnRecordIdById() { + final Id contactId = fflib_IDGenerator.generate(Schema.Contact.SObjectType); + final Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); + final Contact record = new Contact(Id = contactId, AccountId = accountId); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + System.Test.startTest(); + Map accountIdByContactId = domain.getIdFieldByIdField( + Schema.Contact.AccountId, + Schema.Contact.Id + ); + System.Test.stopTest(); + System.Assert.istrue(accountIdByContactId.containsKey(contactId)); + System.Assert.areEqual(accountId, accountIdByContactId.get(contactId)); + } + + @IsTest + static void itShouldReturnRecordByIds() { + SObjectType accountSObjectType = Schema.Account.SObjectType; + final Id idA = fflib_IDGenerator.generate(accountSObjectType); + final Id idB = fflib_IDGenerator.generate(accountSObjectType); + final Id idC = fflib_IDGenerator.generate(accountSObjectType); + + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Id = idA), + new Account(Id = idB), + new Account(Id = idC) + } + ); + + Set recordIds = new Set{ idA, idB, idC }; + System.Assert.istrue( + domain.getSObjectById().keySet().containsAll(recordIds), + 'The domain should return all the record Ids' + ); + } + + @IsTest + static void itShouldReturnRecordsIds() { + SObjectType accountSObjectType = Schema.Account.SObjectType; + Id idA = fflib_IDGenerator.generate(accountSObjectType); + Id idB = fflib_IDGenerator.generate(accountSObjectType); + Id idC = fflib_IDGenerator.generate(accountSObjectType); + + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Id = idA), + new Account(Id = idB), + new Account(Id = idC) + } + ); + + Set recordIds = new Set{ idA, idB, idC }; + System.Assert.istrue( + domain.getRecordIds().containsAll(recordIds), + 'The domain should return all the record Ids' + ); + } + + @IsTest + static void itShouldReturnRecords() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(6, domain.getRecords().size()); + } + + @IsTest + static void itShouldReturnRecordByCriteria() { + final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Name = 'A', Id = idA), + new Account( + Name = 'B', + Id = fflib_IDGenerator.generate(Schema.Account.SObjectType) + ) + } + ); + + System.Assert.areEqual( + 'A', + domain.getRecords(new fflib_Criteria().equalTo(Schema.Account.Id, idA)) + .get(0) + .get('Name') + ); + } + + @IsTest + static void itShouldReturnRecordById() { + final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Name = 'A', Id = idA), + new Account( + Name = 'B', + Id = fflib_IDGenerator.generate(Schema.Account.SObjectType) + ) + } + ); + + System.Assert.areEqual('A', domain.getRecord(idA).get('Name')); + } + + @IsTest + static void itShouldReturnRecordsById() { + final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); + final fflib_Ids ids = new fflib_Ids(new Set{ idA }); + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Name = 'A', Id = idA), + new Account( + Name = 'B', + Id = fflib_IDGenerator.generate(Schema.Account.SObjectType) + ) + } + ); + + System.Assert.areEqual('A', domain.getRecords(ids).get(0).get('Name')); + } + + @IsTest + static void itShouldReturnRecordsByIds() { + final Id idA = fflib_IDGenerator.generate(Schema.Account.SObjectType); + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Name = 'A', Id = idA), + new Account( + Name = 'B', + Id = fflib_IDGenerator.generate(Schema.Account.SObjectType) + ) + } + ); + + System.Assert.areEqual( + 'A', + domain.getRecords(new Set{ idA }).get(0).get('Name') + ); + } + + @IsTest + static void itShouldReturnRecordsWithFieldValues() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(1, domain.selectByShippingCountry('USA').size()); + } + + @IsTest + static void itShouldReturnRecordsWithoutFieldValues() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(3, domain.selectWithoutShippingCountry().size()); + } + + @IsTest + static void itShouldReturnRecordsWithoutAllFieldValues() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(1, domain.selectWithEmptyRecord().size()); + } + + @IsTest + static void itShouldReturnRecordsWithShippingCountry() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(3, domain.selectWithShippingCountry().size()); + } + + @IsTest + static void itShouldReturnRecordsWithAllFieldValues() { + DomainAccounts domain = generateDomain(); + + System.Assert.areEqual(3, domain.selectPopulatedRecords().size()); + } + + @IsTest + static void itShouldReturnFieldValues() { + DomainAccounts domain = generateDomain(); + + System.Assert.istrue( + domain.getStringFieldValues(Schema.Account.ShippingCountry) + .containsAll(new Set{ 'USA', 'Ireland', 'UK', '' }) + ); + } + + @IsTest + static void itShouldSetFieldValue() { + DomainAccounts domain = generateDomain(); + String country = 'Holland'; + domain.setShippingCountry(country); + + System.Assert.areEqual(6, domain.selectByShippingCountry(country).size()); + } + + @IsTest + static void itShouldSetFieldValueByCondition() { + DomainAccounts domain = generateDomain(); + domain.setRatingByShippingCountry( + new Map{ 'USA' => 'Hot' } + ); + + System.Assert.areEqual(1, domain.selectByRating('Hot').size()); + } + + @IsTest + static void itShouldGetTheSumOfAnnualRevenue() { + DomainAccounts domain = generateDomain(); + System.Assert.areEqual( + 11, + domain.getDecimalFieldSum(Account.AnnualRevenue) + ); + System.Assert.isTrue( + domain.getDecimalFieldValues(Account.AnnualRevenue).contains((Decimal) 5) + ); + } + + @IsTest + static void itShouldSetFieldByIdField() { + Id accountId = fflib_IDGenerator.generate(Account.SObjectType); + Account record = new Account(Id = accountId, Name = ''); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + + domain.setFieldValue( + Schema.Account.Id, + Schema.Account.Name, + new Map{ accountId => 'Hello' } + ); + + System.Assert.areEqual('Hello', domain.getFirstSObject().get('Name')); + } + + @IsTest + static void itShouldSetFieldByStringField() { + Account record = new Account(Name = 'Hello', Rating = 'Cold'); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + + domain.setFieldValue( + Schema.Account.Name, + Schema.Account.Rating, + new Map{ 'Hello' => 'Warm' } + ); + + System.Assert.areEqual('Warm', domain.getFirstSObject().get('Rating')); + } + + @IsTest + static void itShouldSetField() { + Account record = new Account(Name = 'Hello', Rating = 'Cold'); + fflib_SObjects2 domain = new fflib_SObjects2(new List{ record }); + + domain.setFieldValue( + Account.Name, + 'Hello World', + new fflib_Criteria().equalTo(Account.Rating, 'Cold') + ); + + System.Assert.areEqual('Hello World', domain.getFirstSObject().get('Name')); + } + + private static DomainAccounts generateDomain() { + DomainAccounts domain = new DomainAccounts( + new List{ + new Account(Name = 'A', ShippingCountry = 'USA', AnnualRevenue = 5), + new Account(Name = 'B', ShippingCountry = 'Ireland', AnnualRevenue = 3), + new Account(Name = 'C', ShippingCountry = 'UK', AnnualRevenue = 2), + new Account(Name = 'D', ShippingCountry = '', AnnualRevenue = 1), + new Account(Name = 'E'), + new Account() + } + ); + return domain; + } + + private class DomainAccounts extends fflib_SObjects2 { + public DomainAccounts(List records) { + super(records, Schema.Account.SObjectType); + } + + public List selectByShippingCountry(String country) { + return (List) getRecordsByFieldValues( + Schema.Account.ShippingCountry, + new Set{ country } + ); + } + + public List selectByRating(String rating) { + return (List) getRecordsByFieldValue( + Schema.Account.Rating, + rating + ); + } + + public List selectWithoutShippingCountry() { + return (List) getRecordsWithBlankFieldValues( + Schema.Account.ShippingCountry + ); + } + + public List selectWithShippingCountry() { + return (List) getRecordsWithNotBlankFieldValues( + Schema.Account.ShippingCountry + ); + } + + public List selectWithEmptyRecord() { + return (List) getRecordsWithAllBlankFieldValues( + new Set{ + Schema.Account.Name, + Schema.Account.ShippingCountry + } + ); + } + + public List selectPopulatedRecords() { + return (List) getRecordsWithAllNotBlankFieldValues( + new Set{ + Schema.Account.Name, + Schema.Account.ShippingCountry + } + ); + } + + public void setShippingCountry(String country) { + setFieldValue(Schema.Account.ShippingCountry, country); + } + + public void setRatingByShippingCountry( + Map ratingByCountry + ) { + setFieldValueByMap( + Schema.Account.ShippingCountry, + Schema.Account.Rating, + ratingByCountry + ); + } + + public void addNameError(String message) { + addError(Schema.Account.Name, message); + } + + public void clearShippingCountry() { + clearField(Schema.Account.ShippingCountry); + } + + protected override Map getRelatedFieldByDomainType() { + return new Map{ + Schema.Contact.SObjectType => Contact.AccountId + }; + } + } + + private class DomainContacts extends fflib_SObjects2 { + public DomainContacts(List records) { + super(records, Schema.Contact.SObjectType); + } + + protected override Map getRelatedFieldByDomainType() { + return new Map{ + Schema.Account.SObjectType => Contact.AccountId, + Schema.Case.SObjectType => Case.ContactId + }; + } + } + + private class DomainCases extends fflib_SObjects2 { + public DomainCases(List records) { + super(records, Schema.Case.SObjectType); + } + + protected override Map getRelatedFieldByDomainType() { + return new Map{ + Schema.Contact.SObjectType => Case.ContactId + }; + } + } }