Skip to content

Commit 6fa7ff2

Browse files
author
Theo Stone
committed
Merge branch 'master' of https://github.com/financialforcedev/fflib-apex-common into merge-from-fork-source
2 parents c78614a + 43d2bc8 commit 6fa7ff2

13 files changed

+1220
-840
lines changed

.travis.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ sudo: false
55
branches:
66
only:
77
- master
8-
env:
9-
global:
10-
- secure: "EG37gA3LxAJR9FzUSzDc4iXLmOZ0o5Qbel7mzVfxnkrbiBH59wconB6mq+/gpc5AihFLoehjjskQKEcRYl7xc256iChwzZE9bf7PTYnGtscLIQwNenPEpxPBJTw9oLJK3wgShcjMF/1ZfqHoGmzkwxnmzJKzuaZ9OW1l8uqn+Lw="
11-
- secure: "ctp6SfgSVW3HqfpdRykfIq5lPOZk900wag0RC33NfYjxoXUTu797F6KhOGojqfh1E0/Ivng1IngvFScAFyDscpIJzTu+2/FMP9qsC2bn/e4iXg8sjhmgKhjqg/rFHiAJQz/XuW0b1xom2dxQ12ipiouniooveHe1f7541LrQq6c="
128
script:
139
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && ant -lib lib/ant-salesforce.jar -Dsf.username=${SFUSER} -Dsf.password=${SFPWD} deploy || [ "${TRAVIS_PULL_REQUEST}" != "false" ]'
1410
- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] && ant -lib lib/ant-salesforce.jar -Dsf.username=${SFUSER} -Dsf.password=${SFPWD} deploy || [ "${TRAVIS_PULL_REQUEST}" = "false" ]'

fflib/src/classes/fflib_ISObjectUnitOfWork.cls

Lines changed: 89 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,61 @@
1414
**/
1515
public interface fflib_ISObjectUnitOfWork
1616
{
17-
/**
18-
* Register a newly created SObject instance to be inserted when commitWork is called
19-
*
20-
* @param record A newly created SObject instance to be inserted during commitWork
21-
**/
22-
void registerNew(SObject record);
23-
/**
24-
* Register a list of newly created SObject instances to be inserted when commitWork is called
25-
*
26-
* @param records A list of newly created SObject instances to be inserted during commitWork
27-
**/
28-
void registerNew(List<SObject> records);
29-
/**
30-
* Register a newly created SObject instance to be inserted when commitWork is called,
31-
* you may also provide a reference to the parent record instance (should also be registered as new separatly)
32-
*
33-
* @param record A newly created SObject instance to be inserted during commitWork
34-
* @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent
35-
* @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separatly)
36-
**/
37-
void registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord);
38-
/**
39-
* Register a relationship between two records that have yet to be inserted to the database. This information will be
40-
* used during the commitWork phase to make the references only when related records have been inserted to the database.
41-
*
42-
* @param record An existing or newly created record
43-
* @param relatedToField A SObjectField referene to the lookup field that relates the two records together
44-
* @param relatedTo A SOBject instance (yet to be commited to the database)
45-
*/
46-
void registerRelationship(SObject record, Schema.sObjectField relatedToField, SObject relatedTo);
47-
/**
48-
* Register an existing record to be updated during the commitWork method
49-
*
50-
* @param record An existing record
51-
**/
52-
void registerDirty(SObject record);
17+
/**
18+
* Register a newly created SObject instance to be inserted when commitWork is called
19+
*
20+
* @param record A newly created SObject instance to be inserted during commitWork
21+
**/
22+
void registerNew(SObject record);
23+
/**
24+
* Register a list of newly created SObject instances to be inserted when commitWork is called
25+
*
26+
* @param records A list of newly created SObject instances to be inserted during commitWork
27+
**/
28+
void registerNew(List<SObject> records);
29+
/**
30+
* Register a newly created SObject instance to be inserted when commitWork is called,
31+
* you may also provide a reference to the parent record instance (should also be registered as new separatly)
32+
*
33+
* @param record A newly created SObject instance to be inserted during commitWork
34+
* @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent
35+
* @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separatly)
36+
**/
37+
void registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord);
38+
/**
39+
* Register a relationship between two records that have yet to be inserted to the database. This information will be
40+
* used during the commitWork phase to make the references only when related records have been inserted to the database.
41+
*
42+
* @param record An existing or newly created record
43+
* @param relatedToField A SObjectField referene to the lookup field that relates the two records together
44+
* @param relatedTo A SOBject instance (yet to be commited to the database)
45+
*/
46+
void registerRelationship(SObject record, Schema.sObjectField relatedToField, SObject relatedTo);
47+
/**
48+
* Registers a relationship between a record and a Messaging.Email where the record has yet to be inserted
49+
* to the database. This information will be
50+
* used during the commitWork phase to make the references only when related records have been inserted to the database.
51+
*
52+
* @param a single email message instance
53+
* @param relatedTo A SOBject instance (yet to be commited to the database)
54+
*/
55+
void registerRelationship(Messaging.SingleEmailMessage email, SObject relatedTo);
56+
/**
57+
* Register an existing record to be updated during the commitWork method
58+
*
59+
* @param record An existing record
60+
**/
61+
void registerDirty(SObject record);
62+
/**
63+
* Register specific fields on record to be updated when work is commited
64+
*
65+
* If the record has previously been registered as dirty, the dirty fields on the record in this call will overwrite
66+
* the values of the previously registered dirty record
67+
*
68+
* @param record An existing record
69+
* @param dirtyFields The fields to update if record is already registered
70+
**/
71+
void registerDirty(SObject record, List<SObjectField> dirtyFields);
5372
/**
5473
* Register an existing record to be updated when commitWork is called,
5574
* you may also provide a reference to the parent record instance (should also be registered as new separatly)
@@ -59,38 +78,38 @@ public interface fflib_ISObjectUnitOfWork
5978
* @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separatly)
6079
**/
6180
void registerDirty(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord);
62-
/**
63-
* Register a list of existing records to be updated during the commitWork method
64-
*
65-
* @param records A list of existing records
66-
**/
67-
void registerDirty(List<SObject> records);
68-
/**
69-
* Register an existing record to be deleted during the commitWork method
70-
*
71-
* @param record An existing record
72-
**/
73-
void registerDeleted(SObject record);
74-
/**
75-
* Register a list of existing records to be deleted during the commitWork method
76-
*
77-
* @param records A list of existing records
78-
**/
79-
void registerDeleted(List<SObject> records);
80-
/**
81-
* Takes all the work that has been registered with the UnitOfWork and commits it to the database
82-
**/
83-
void commitWork();
84-
/**
85-
* Register a generic peace of work to be invoked during the commitWork phase
86-
*
87-
* @param work Work to be registered
88-
**/
89-
void registerWork(fflib_SObjectUnitOfWork.IDoWork work);
90-
/**
91-
* Registers the given email to be sent during the commitWork
92-
*
93-
* @param email Email to be sent
94-
**/
95-
void registerEmail(Messaging.Email email);
81+
/**
82+
* Register a list of existing records to be updated during the commitWork method
83+
*
84+
* @param records A list of existing records
85+
**/
86+
void registerDirty(List<SObject> records);
87+
/**
88+
* Register an existing record to be deleted during the commitWork method
89+
*
90+
* @param record An existing record
91+
**/
92+
void registerDeleted(SObject record);
93+
/**
94+
* Register a list of existing records to be deleted during the commitWork method
95+
*
96+
* @param records A list of existing records
97+
**/
98+
void registerDeleted(List<SObject> records);
99+
/**
100+
* Takes all the work that has been registered with the UnitOfWork and commits it to the database
101+
**/
102+
void commitWork();
103+
/**
104+
* Register a generic peace of work to be invoked during the commitWork phase
105+
*
106+
* @param work Work to be registered
107+
**/
108+
void registerWork(fflib_SObjectUnitOfWork.IDoWork work);
109+
/**
110+
* Registers the given email to be sent during the commitWork
111+
*
112+
* @param email Email to be sent
113+
**/
114+
void registerEmail(Messaging.Email email);
96115
}

fflib/src/classes/fflib_QueryFactory.cls

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
6464
private Set<String> fields;
6565
private String conditionExpression;
6666
private Integer limitCount;
67-
private Integer offset;
67+
private Integer offsetCount;
6868
private List<Ordering> order;
6969
/**
7070
* Integrate checking for READ Field Level Security within the selectField(s) methods
@@ -347,13 +347,34 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
347347
public Integer getLimit(){
348348
return this.limitCount;
349349
}
350+
/**
351+
* @param offsetCount if not null causes a OFFSET caluse to be added to the resulting query.
352+
**/
353+
public fflib_QueryFactory setOffset(Integer offsetCount){
354+
this.offsetCount = offsetCount;
355+
return this;
356+
}
357+
/**
358+
* @returns the current value of the OFFSET clause, if any.
359+
**/
360+
public Integer getOffset(){
361+
return this.offsetCount;
362+
}
350363
/**
351364
* @param o an instance of {@link fflib_QueryFactory.Ordering} to be added to the query's ORDER BY clause.
352365
**/
353366
public fflib_QueryFactory addOrdering(Ordering o){
354367
this.order.add(o);
355368
return this;
356369
}
370+
371+
/**
372+
* @param o an instance of {@link fflib_QueryFactory.Ordering} to remove all existing (for instance defaults) and be added to the query's ORDER BY clause.
373+
**/
374+
public fflib_QueryFactory setOrdering(Ordering o){
375+
this.order = new List<Ordering>{ o };
376+
return this;
377+
}
357378
/**
358379
* @returns the list of orderings that will be used as the query's ORDER BY clause. You may remove elements from the returned list, or otherwise mutate it, to remove previously added orderings.
359380
**/
@@ -580,6 +601,68 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
580601
return this;
581602
}
582603

604+
/**
605+
* Remove existing ordering and set a field to be sorted on. This may be a direct field or a field
606+
* related through an object lookup or master-detail relationship.
607+
* Use the set to store unique field names, since we only want to sort
608+
* by the same field one time. The sort expressions are stored in a list
609+
* so that they are applied to the SOQL in the same order that they
610+
* were added in.
611+
* @param fieldName The string value of the field to be sorted on
612+
* @param SortOrder the direction to be sorted on (ASCENDING or DESCENDING)
613+
* @param nullsLast whether to sort null values last (NULLS LAST keyword included).
614+
**/
615+
public fflib_QueryFactory setOrdering(String fieldName, SortOrder direction, Boolean nullsLast){
616+
Ordering ordr = new Ordering(getFieldPath(fieldName), direction, nullsLast);
617+
return setOrdering(ordr);
618+
}
619+
620+
/**
621+
* Remove existing ordering and set a field to be sorted on. This may be a direct field or a field
622+
* related through an object lookup or masterdetail relationship.
623+
* Use the set to store unique field names, since we only want to sort
624+
* by the same field one time. The sort expressions are stored in a list
625+
* so that they are applied to the SOQL in the same order that they
626+
* were added in.
627+
* @param field The SObjectfield to sort. This can only be a direct reference.
628+
* @param SortOrder the direction to be sorted on (ASCENDING or DESCENDING)
629+
* @param nullsLast whether to sort null values last (NULLS LAST keyword included).
630+
**/
631+
public fflib_QueryFactory setOrdering(SObjectField field, SortOrder direction, Boolean nullsLast){
632+
Ordering ordr = new Ordering(getFieldTokenPath(field), direction, nullsLast);
633+
return setOrdering(ordr);
634+
}
635+
636+
/**
637+
* Remove existing ordering and set a field to be sorted on. This may be a direct field or a field
638+
* related through an object lookup or master-detail relationship.
639+
* Use the set to store unique field names, since we only want to sort
640+
* by the same field one time. The sort expressions are stored in a list
641+
* so that they are applied to the SOQL in the same order that they
642+
* were added in.
643+
* @param fieldName The string value of the field to be sorted on
644+
* @param SortOrder the direction to be sorted on (ASCENDING or DESCENDING)
645+
**/
646+
public fflib_QueryFactory setOrdering(String fieldName, SortOrder direction){
647+
Ordering ordr = new Ordering(getFieldPath(fieldName), direction);
648+
return setOrdering(ordr);
649+
}
650+
651+
/**
652+
* Remove existing ordering and set a field to be sorted on. This may be a direct field or a field
653+
* related through an object lookup or masterdetail relationship.
654+
* Use the set to store unique field names, since we only want to sort
655+
* by the same field one time. The sort expressions are stored in a list
656+
* so that they are applied to the SOQL in the same order that they
657+
* were added in.
658+
* @param field The SObjectfield to sort. This can only be a direct reference.
659+
* @param SortOrder the direction to be sorted on (ASCENDING or DESCENDING)
660+
**/
661+
public fflib_QueryFactory setOrdering(SObjectField field, SortOrder direction){
662+
Ordering ordr = new Ordering(getFieldTokenPath(field), direction);
663+
return setOrdering(ordr);
664+
}
665+
583666
/**
584667
* Convert the values provided to this instance into a full SOQL string for use with Database.query
585668
* Check to see if subqueries queries need to be added after the field list.
@@ -618,6 +701,10 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
618701

619702
if(limitCount != null)
620703
result += ' LIMIT '+limitCount;
704+
705+
if(offsetCount != null)
706+
result += ' OFFSET '+offsetCount;
707+
621708
return result;
622709
}
623710

@@ -629,6 +716,7 @@ public class fflib_QueryFactory { //No explicit sharing declaration - inherit fr
629716

630717
fflib_QueryFactory clone = new fflib_QueryFactory(this.table)
631718
.setLimit(this.limitCount)
719+
.setOffset(this.offsetCount)
632720
.setCondition(this.conditionExpression)
633721
.setEnforceFLS(this.enforceFLS);
634722

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3-
<apiVersion>37.0</apiVersion>
3+
<apiVersion>42.0</apiVersion>
44
<status>Active</status>
55
</ApexClass>

fflib/src/classes/fflib_QueryFactoryTest.cls

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,50 @@ private class fflib_QueryFactoryTest {
161161
System.assert( Pattern.matches('SELECT.*Email.*FROM.*',query), 'Expected Name field in query, got '+query);
162162
}
163163

164+
@isTest
165+
static void setOrdering_ReplacesPreviousOrderingsWithExpectedOrdering(){
166+
fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType);
167+
qf.selectField('name');
168+
qf.selectField('email');
169+
qf.setCondition( 'name = \'test\'' );
170+
171+
//test base method with ordeting by OwnerId Descending
172+
qf.setOrdering( new fflib_QueryFactory.Ordering('Contact','OwnerId',fflib_QueryFactory.SortOrder.DESCENDING) );
173+
174+
System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace default Orderings');
175+
System.assertEquals(Contact.OwnerId.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field OwnerId');
176+
System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.');
177+
178+
//test method overload with ordering by LastModifiedDate Ascending
179+
qf.setOrdering('LastModifiedDate', fflib_QueryFactory.SortOrder.ASCENDING, true);
180+
181+
System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings');
182+
System.assertEquals(Contact.LastModifiedDate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field LastModifiedDate');
183+
System.assertEquals(fflib_QueryFactory.SortOrder.ASCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.');
184+
185+
//test method overload with ordering by CreatedDate Descending
186+
qf.setOrdering(Contact.CreatedDate, fflib_QueryFactory.SortOrder.DESCENDING, true);
187+
188+
System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings');
189+
System.assertEquals(Contact.CreatedDate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field CreatedDate');
190+
System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.');
191+
192+
//test method overload with ordering by CreatedBy.Name Descending
193+
qf.setOrdering('CreatedBy.Name', fflib_QueryFactory.SortOrder.DESCENDING);
194+
195+
System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings');
196+
System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.');
197+
198+
//test method overload with ordering by Birthdate Ascending
199+
qf.setOrdering(Contact.Birthdate, fflib_QueryFactory.SortOrder.ASCENDING);
200+
201+
System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings');
202+
System.assertEquals(Contact.Birthdate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field Birthdate');
203+
System.assertEquals(fflib_QueryFactory.SortOrder.ASCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.');
204+
205+
String query = qf.toSOQL();
206+
}
207+
164208
@isTest
165209
static void invalidField_string(){
166210
fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3-
<apiVersion>37.0</apiVersion>
3+
<apiVersion>42.0</apiVersion>
44
<status>Active</status>
55
</ApexClass>

0 commit comments

Comments
 (0)