diff --git a/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls b/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls index 5d9b5db087..cf31cc0e40 100644 --- a/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls +++ b/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls @@ -40,9 +40,8 @@ * will be included by default. Constructing Ordering instances manually is discouraged. * * Subselect Queries are supported with the subselectQuery methods. - * More than one sub-query can be added to a single query, but sub-queries can only be 1 level deep. - * An exception will thrown from the subselectQuery method when there is an attempt to add a subquery to a sub-query - * or to add a subquery to a query with an invalid relationship. + * More than one sub-query can be added to a single query. + * An exception will thrown from the subselectQuery method when there is an attempt to add a subquery to a query with an invalid relationship. * * Current limitations: * - Aggregate functions are not currently supported. @@ -503,9 +502,6 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr * @param relationship The ChildRelationship to be added as a subquery **/ private fflib_QueryFactory setSubselectQuery(Schema.ChildRelationship relationship, Boolean assertIsAccessible){ - if (this.relationship != null){ - throw new InvalidSubqueryRelationshipException('Invalid call to subselectQuery. You may not add a subselect query to a subselect query.'); - } if (this.subselectQueryMap == null){ this.subselectQueryMap = new Map(); } diff --git a/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls b/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls index 3eec7a8237..b9c83a6ba7 100644 --- a/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls @@ -432,27 +432,6 @@ private class fflib_QueryFactoryTest { System.assertNotEquals(e, null); } - @isTest - static void addChildQueries_invalidChildRelationshipTooDeep(){ - fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); - qf.selectField('name'); - qf.selectField('email'); - qf.setCondition( 'name like \'%test%\'' ); - qf.addOrdering( new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ).addOrdering('CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING); - Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); - - fflib_QueryFactory childQf = qf.subselectQuery(Task.SObjectType); - childQf.selectField('Id'); - childQf.selectField('Subject'); - Exception e; - try { - fflib_QueryFactory subChildQf = childQf.subselectQuery(Task.SObjectType); - } catch (fflib_QueryFactory.InvalidSubqueryRelationshipException ex) { - e = ex; - } - System.assertNotEquals(e, null); - } - @isTest static void checkFieldObjectReadSort_success(){ fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls index 68b78e2069..65e1823426 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls @@ -34,7 +34,7 @@ private with sharing class fflib_SObjectSelectorTest static testMethod void testGetSObjectName() { - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); system.assertEquals(null, selector.getSObjectFieldSetList()); system.assertEquals('Account',selector.getSObjectName()); } @@ -51,7 +51,7 @@ private with sharing class fflib_SObjectSelectorTest idSet.add(item.Id); Test.startTest(); - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); List result = (List) selector.selectSObjectsById(idSet); Test.stopTest(); @@ -76,7 +76,7 @@ private with sharing class fflib_SObjectSelectorTest idSet.add(item.Id); Test.startTest(); - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); Database.QueryLocator result = selector.queryLocatorById(idSet); System.Iterator iteratorResult = result.iterator(); Test.stopTest(); @@ -110,7 +110,7 @@ private with sharing class fflib_SObjectSelectorTest return; // Abort the test if unable to create a user with low enough acess System.runAs(testUser) { - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); try { List result = (List) selector.selectSObjectsById(idSet); @@ -139,7 +139,7 @@ private with sharing class fflib_SObjectSelectorTest return; // Abort the test if unable to create a user with low enough acess System.runAs(testUser) { - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, true); + AccountSelector selector = new AccountSelector(false, false, false, true); try { List result = (List) selector.selectSObjectsById(idSet); @@ -153,7 +153,7 @@ private with sharing class fflib_SObjectSelectorTest static testMethod void testSOQL() { - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); String soql = selector.newQueryFactory().toSOQL(); Pattern p = Pattern.compile('SELECT (.*) FROM Account ORDER BY AccountNumber DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST '); Matcher m = p.matcher(soql); @@ -165,7 +165,7 @@ private with sharing class fflib_SObjectSelectorTest static testMethod void testSOQL_defaultSorting() { - Testfflib_SObjectSelectorDefaultSorting selector = new Testfflib_SObjectSelectorDefaultSorting(false); + DefaultSortingAccountSelector selector = new DefaultSortingAccountSelector(false); String soql = selector.newQueryFactory().toSOQL(); Pattern p = Pattern.compile(String.format('SELECT (.*) FROM Account ORDER BY {0} ASC NULLS FIRST ', new List{selector.getOrderBy()})); @@ -178,7 +178,7 @@ private with sharing class fflib_SObjectSelectorTest static testMethod void testDefaultConfig() { - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + AccountSelector selector = new AccountSelector(); System.assertEquals(false, selector.isEnforcingFLS()); System.assertEquals(true, selector.isEnforcingCRUD()); System.assertEquals(false, selector.isIncludeFieldSetFields()); @@ -213,7 +213,7 @@ private with sharing class fflib_SObjectSelectorTest static void testWithoutSorting() { //Given - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, false); + AccountSelector selector = new AccountSelector(false, false, false, false); fflib_QueryFactory qf = selector.newQueryFactory(); Set expectedSelectFields = new Set{ 'Name', 'Id', 'AccountNumber', 'AnnualRevenue' }; @@ -239,7 +239,7 @@ private with sharing class fflib_SObjectSelectorTest static void testWithOrderingNullsLast() { // Build the selector to test with - Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, false); + AccountSelector selector = new AccountSelector(false, false, false, false); fflib_QueryFactory qf = selector.newQueryFactory(); // Add in the expected fields @@ -466,14 +466,14 @@ private with sharing class fflib_SObjectSelectorTest } - private class Testfflib_SObjectSelector extends fflib_SObjectSelector + private class AccountSelector extends fflib_SObjectSelector { - public Testfflib_SObjectSelector() + public AccountSelector() { super(); } - public Testfflib_SObjectSelector(Boolean includeFieldSetFields, Boolean enforceCRUD, Boolean enforceFLS, Boolean sortSelectFields) + public AccountSelector(Boolean includeFieldSetFields, Boolean enforceCRUD, Boolean enforceFLS, Boolean sortSelectFields) { super(includeFieldSetFields, enforceCRUD, enforceFLS, sortSelectFields); } @@ -498,7 +498,7 @@ private with sharing class fflib_SObjectSelectorTest return 'AccountNumber DESC, AnnualRevenue ASC NULLS LAST'; } } - + private class Testfflib_UserSObjectSelector extends fflib_SObjectSelector { public Testfflib_UserSObjectSelector() @@ -523,9 +523,9 @@ private with sharing class fflib_SObjectSelectorTest } - private class Testfflib_SObjectSelectorDefaultSorting extends fflib_SObjectSelector + private class DefaultSortingAccountSelector extends fflib_SObjectSelector { - public Testfflib_SObjectSelectorDefaultSorting(Boolean includeFieldSetFields) + public DefaultSortingAccountSelector(Boolean includeFieldSetFields) { super(includeFieldSetFields); } @@ -629,6 +629,56 @@ private with sharing class fflib_SObjectSelectorTest } } + private class ContractSelector extends fflib_SObjectSelector{ + public ContractSelector(){ + super(false,DataAccess.SYSTEM_MODE); + } + + public Schema.SObjectType getSObjectType(){ + return Contract.SObjectType; + } + + public List getSObjectFieldList(){ + return new List { + Contract.Id, + Contract.ContractNumber + }; + } + } + + private class OrderSelector extends fflib_SObjectSelector{ + public OrderSelector(){ + super(false,DataAccess.SYSTEM_MODE); + } + + public Schema.SObjectType getSObjectType(){ + return Order.SObjectType; + } + + public List getSObjectFieldList(){ + return new List { + Order.Id, + Order.OrderNumber + }; + } + } + + private class TaskSelector extends fflib_SObjectSelector{ + public TaskSelector(){ + super(false,DataAccess.SYSTEM_MODE); + } + + public Schema.SObjectType getSObjectType(){ + return Task.SObjectType; + } + + public List getSObjectFieldList(){ + return new List { + Task.Id, + Task.Subject + }; + } + } /** * Create test user @@ -762,6 +812,37 @@ private with sharing class fflib_SObjectSelectorTest System.assertEquals(expectedSOQL, actualSOQL); } + @IsTest + static void toSOQL_When_GreatGreatGrandchildRelationships_Expect_WelformedSOQL(){ + AccessLevelAccountSelector aSel = new AccessLevelAccountSelector(); + fflib_QueryFactory aQF = aSel.newQueryFactory(); + + ContractSelector cSel = new ContractSelector(); + fflib_QueryFactory cQF = cSel.addQueryFactorySubselect(aQF); + + AccessLevelOpportunitySelector oppSel = new AccessLevelOpportunitySelector(); + fflib_QueryFactory oppQF = oppSel.addQueryFactorySubselect(cQF); + + OrderSelector orderSel = new OrderSelector(); + fflib_QueryFactory orderQF = orderSel.addQueryFactorySubselect(oppQF); + + TaskSelector tSel = new TaskSelector(); + fflib_QueryFactory tQF = tSel.addQueryFactorySubselect(orderQF); + + String expected + = 'SELECT name, id, annualrevenue, accountnumber, ' + + '(SELECT id, contractnumber, ' + + '(SELECT name, id, amount, closedate, ' + + '(SELECT id, ordernumber, ' + + '(SELECT id, subject FROM Tasks ORDER BY Subject ASC NULLS FIRST ) ' + + 'FROM Orders ORDER BY OrderNumber ASC NULLS FIRST ) ' + + 'FROM Opportunities ORDER BY Name ASC NULLS FIRST ) ' + + 'FROM Contracts ORDER BY ContractNumber ASC NULLS FIRST ) ' + + 'FROM Account WITH USER_MODE ORDER BY Name ASC NULLS FIRST '; + + Assert.areEqual(expected,aQF.toSOQL()); + } + private class CampaignMemberSelector extends fflib_SObjectSelector { public CampaignMemberSelector(DataAccess access) { super(false, access);