forked from apex-enterprise-patterns/fflib-apex-common
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request apex-enterprise-patterns#5 from OrtooApps/feature/…
…dynamic-selector Feature/dynamic selector
- Loading branch information
Showing
5 changed files
with
296 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
...ework/default/ortoo-core/default/classes/fflib-extension/ortoo_DynamicSobjectSelector.cls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* Provides the ability to dynamically create a selector based on configuration. | ||
* | ||
* Should not be used in order to retrieve configuration of the managed app, but rather is for driving SOQL statements | ||
* from that configuration - for example: | ||
* The system contains configurations for how to match an email to a record in the system. | ||
* That class can be used to turn that configuration into a SOQL statement that will retrieve that record. | ||
* | ||
* @group fflib Extension | ||
*/ | ||
public inherited sharing class ortoo_DynamicSobjectSelector extends ortoo_SobjectSelector // NOPMD: specified a mini-namespace to differentiate from fflib versions | ||
{ | ||
List<String> fieldList = new List<String>(); | ||
Schema.SObjectType sobjectType; | ||
|
||
// TODO: add the ability to add sub-queries | ||
|
||
/** | ||
* Define the SObject Type that this selector will retrieve the data for. | ||
* | ||
* @param Schema.SObjectType The SObject Type that this instance will retrieve | ||
* @return ortoo_DynamicSobjectSelector Itself, allowing for a fluent interface | ||
*/ | ||
public ortoo_DynamicSobjectSelector setSobjectType( Schema.SObjectType sobjectType ) | ||
{ | ||
Contract.requires( sobjectType != null, 'setSobjectType called with a null sobjectType' ); | ||
this.sobjectType = sobjectType; | ||
return this; | ||
} | ||
|
||
/** | ||
* Define the SObject Type that this selector will retrieve the data for, stated by the String representation. | ||
* | ||
* @param String The SObject Type that this instance will retrieve | ||
* @return ortoo_DynamicSobjectSelector Itself, allowing for a fluent interface | ||
*/ | ||
public ortoo_DynamicSobjectSelector setSobjectType( String sobjectTypeName ) | ||
{ | ||
Contract.requires( sobjectTypeName != null, 'setSobjectType called with a null sobjectTypeName' ); | ||
|
||
Schema.SObjectType sobjectType = SobjectUtils.getSobjectType( sobjectTypeName ); | ||
|
||
Contract.requires( sobjectType != null, 'setSobjectType called with an sobjectTypeName that does not represent a valid SObject Type' ); | ||
|
||
return setSobjectType( sobjectType ); | ||
} | ||
|
||
/** | ||
* Add a field to be returned by the generated SOQL | ||
* | ||
* @param String | ||
* @return ortoo_DynamicSobjectSelector Itself, allowing for a fluent interface | ||
*/ | ||
public ortoo_DynamicSobjectSelector addField( String fieldToAdd ) | ||
{ | ||
Contract.requires( fieldToAdd != null, 'addField called with a null fieldToAdd' ); | ||
// could we check if the field is valid? May be hard with things like parent relationships | ||
fieldList.add( fieldToAdd ); | ||
return this; | ||
} | ||
|
||
/** | ||
* Retrieve the records that match the passed criteria. | ||
* | ||
* @param ortoo_Criteria The criteria that should be used to derive the records to return | ||
* @return List<Sobject> The result of the Selection | ||
*/ | ||
public List<Sobject> selectByCriteria( ortoo_Criteria criteria ) | ||
{ | ||
Contract.requires( criteria != null, 'selectByCriteria called with a null criteria' ); | ||
|
||
Contract.assert( sobjectType != null, 'selectByCriteria called when sobjectType has not been set' ); | ||
|
||
return Database.query( generateSoqlByCriteria( criteria ) ); | ||
} | ||
|
||
/** | ||
* Required overload in order to make this a concrete class. | ||
* Never returns any fields as the fields are added from the text representation instead, at the point of query. | ||
* | ||
* @return List<Schema.SObjectField> The configured fields | ||
*/ | ||
public List<Schema.SObjectField> getSObjectFieldList() | ||
{ | ||
return new List<Schema.SObjectField>(); | ||
} | ||
|
||
/** | ||
* Return the SObject Type that this selector will return. | ||
* | ||
* @return Schema.SObjectType The configured SObject Type | ||
*/ | ||
public Schema.SObjectType getSObjectType() | ||
{ | ||
return sobjectType; | ||
} | ||
|
||
@testVisible | ||
private String generateSoqlByCriteria( ortoo_Criteria criteria ) | ||
{ | ||
return newQueryFactory().selectFields( fieldList ).setCondition( criteria.toSOQL() ).toSOQL(); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
...ault/ortoo-core/default/classes/fflib-extension/ortoo_DynamicSobjectSelector.cls-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<apiVersion>52.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |
182 changes: 182 additions & 0 deletions
182
...ult/ortoo-core/default/classes/fflib-extension/tests/ortoo_DynamicSobjectSelectorTest.cls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
@isTest | ||
private without sharing class ortoo_DynamicSobjectSelectorTest | ||
{ | ||
@isTest | ||
private static void selectByCriteria_whenTheSobjectHasBeenSetByName_willReturnAListOfSobjects() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( 'Contact' ); | ||
|
||
List<Sobject> returnedSobjects = selector.selectByCriteria( new ortoo_Criteria() ); | ||
|
||
System.assertEquals( new List<Sobject>(), returnedSobjects, 'selectByCriteria, when the SObject Type has been set by name, will return a list of SObjects' ); | ||
} | ||
|
||
@isTest | ||
private static void selectByCriteria_whenTheSobjectHasBeenSetByType_willReturnAListOfSobjects() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( Contact.sobjectType ); | ||
|
||
List<Sobject> returnedSobjects = selector.selectByCriteria( new ortoo_Criteria() ); | ||
|
||
System.assertEquals( new List<Sobject>(), returnedSobjects, 'selectByCriteria, when the SObject Type has been set by type, will return a list of SObjects' ); | ||
} | ||
|
||
@isTest | ||
private static void selectByCriteria_whenTheSobjectTypeHasNotBeenSet_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector(); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.selectByCriteria( new ortoo_Criteria() ); | ||
} | ||
catch ( Contract.AssertException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'selectByCriteria called when sobjectType has not been set', exceptionMessage, 'selectByCriteria, when the sobject type has not been set, will throw an exception' ); | ||
} | ||
|
||
@isTest | ||
private static void selectByCriteria_whenGivenANullCriteria_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( Contact.sobjectType ); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.selectByCriteria( null ); | ||
} | ||
catch ( Contract.RequiresException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'selectByCriteria called with a null criteria', exceptionMessage, 'selectByCriteria, when given a null criteria, will throw an exception' ); | ||
} | ||
|
||
@isTest | ||
private static void addField_whenGivenAString_willAddThatFieldToTheSelection() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( Contact.sobjectType ); | ||
|
||
Test.startTest(); | ||
selector.addField( 'Name' ); | ||
String generatedSoql = selector.generateSoqlByCriteria( new ortoo_Criteria() ); | ||
Test.stopTest(); | ||
|
||
Amoss_asserts.assertStartsWith( 'SELECT Name FROM Contact', generatedSoql, 'addField, when given a string that represents a valid field, will add that field to the selection' ); | ||
} | ||
|
||
@isTest | ||
private static void addField_whenGivenMultipleStrings_willAddThoseFieldsToTheSelection() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( Contact.sobjectType ); | ||
|
||
Test.startTest(); | ||
selector.addField( 'Name' ) | ||
.addField( 'FirstName' ) | ||
.addField( 'LastName' ); | ||
String generatedSoql = selector.generateSoqlByCriteria( new ortoo_Criteria() ); | ||
Test.stopTest(); | ||
|
||
Amoss_asserts.assertStartsWith( 'SELECT FirstName, LastName, Name FROM Contact', generatedSoql, 'addField, when given multiple strings that represent valid fields, will add them to the selection' ); | ||
} | ||
|
||
@isTest | ||
private static void addField_whenPassedANullString_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector() | ||
.setSobjectType( Contact.sobjectType ); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.addField( null ); | ||
} | ||
catch ( Contract.RequiresException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'addField called with a null fieldToAdd', exceptionMessage, 'addField, when passed a null field name, will throw an exception' ); | ||
} | ||
|
||
@isTest | ||
private static void setSobjectType_whenPassedANullString_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
String nullString; | ||
|
||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector(); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.setSobjectType( nullString ); | ||
} | ||
catch ( Contract.RequiresException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'setSobjectType called with a null sobjectTypeName', exceptionMessage, 'setSobjectType, when passed a null string, will throw an exception' ); | ||
} | ||
|
||
@isTest | ||
private static void setSobjectType_whenPassedAnInvalidString_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector(); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.setSobjectType( 'NotAnSObject' ); | ||
} | ||
catch ( Contract.RequiresException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'setSobjectType called with an sobjectTypeName that does not represent a valid SObject Type', exceptionMessage, 'setSobjectType, when passed a string that does not represent an sobject, will throw an exception' ); | ||
} | ||
|
||
@isTest | ||
private static void setSobjectType_whenPassedANullSobjectType_willThrowAnException() // NOPMD: Test method name format | ||
{ | ||
SobjectType nullSobjectType; | ||
|
||
ortoo_DynamicSobjectSelector selector = new ortoo_DynamicSobjectSelector(); | ||
|
||
Test.startTest(); | ||
String exceptionMessage; | ||
try | ||
{ | ||
selector.setSobjectType( nullSobjectType ); | ||
} | ||
catch ( Contract.RequiresException e ) | ||
{ | ||
exceptionMessage = e.getMessage(); | ||
} | ||
Test.stopTest(); | ||
|
||
Amoss_Asserts.assertContains( 'setSobjectType called with a null sobjectType', exceptionMessage, 'setSobjectType, when passed a null sobject type, will throw an exception' ); | ||
} | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
...-core/default/classes/fflib-extension/tests/ortoo_DynamicSobjectSelectorTest.cls-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<apiVersion>52.0</apiVersion> | ||
<status>Active</status> | ||
</ApexClass> |