Add a generic UnitOfWork functionality

This commit is contained in:
wim
2021-10-14 17:00:46 +01:00
parent ed3025d78c
commit dbb11f2e1e
11 changed files with 728 additions and 0 deletions
+11
View File
@@ -6,6 +6,16 @@ This extension package is dependent on the following packages:
- [fflib-apex-mocks](https://github.com/apex-enterprise-patterns/fflib-apex-mocks) - [fflib-apex-mocks](https://github.com/apex-enterprise-patterns/fflib-apex-mocks)
- [fflib-apex-common](https://github.com/apex-enterprise-patterns/fflib-apex-common) - [fflib-apex-common](https://github.com/apex-enterprise-patterns/fflib-apex-common)
## Extensions pack contents
These features are offered in this extension package:
- [**Application class redesigned**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/application-factories) <br/>A Force-DI like feature using dependency injection with the Separation of Concerns design pattern.
- [**Persitant Logger**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/Logger) <br/>
Ever want to log error messages in a Custom Object? The fflib_Logger will do it for you, even if you have a database-rollback.
- [**Extended domain features**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/Domain-Layer) <br/> Tons of extra methods in the domains to access the (S)Objects.
- [**Criteria evaluator**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/criteria-based-filter) <br/> Ability to use the same criteria filters for domains and selectors. Build SOQL WHERE clauses in an object-oriented manner. It can also evaluate a list of records against predefined conditions "(1 AND 2 AND (3 OR 4))".
- [**Generic UnitOfWork**](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki/UnitOfWork) <br/> Ever want to execute a bunch of work items in a dynamic manner, and be able to control their order of execution, and run some of it in a separate execution context? Wow... that's a lot to ask.. But, the UnitOfWork will do that for you! <br/> Be ware, this fflib_**UnitOfWork** is something different than the fflib_**SObject**UnitOfwork
## Wiki ## Wiki
A full overview of the features in this extension pack and how to use them A full overview of the features in this extension pack and how to use them
can be found in [the Wiki pages of this repository](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki). can be found in [the Wiki pages of this repository](https://github.com/wimvelzeboer/fflib-apex-extensions/wiki).
@@ -40,6 +50,7 @@ Some of the changes that have major impact are listed here;
### October 2021 ### October 2021
- **Logger functionality** <br/>A replacement of System.Debug that is using Platform events to store log messages in a Custom Object for you to monitor - **Logger functionality** <br/>A replacement of System.Debug that is using Platform events to store log messages in a Custom Object for you to monitor
- **API Upgrade to Winter'22** <br/> All Apex classes are upgraded to API 53.0 - **API Upgrade to Winter'22** <br/> All Apex classes are upgraded to API 53.0
- **UnitOfWork** <br/> Generic UnitOfWork, capable of executing work items in batch at a single instance. Each item will be executed based on its priority and can be queued in a separate execution context when necessary.
### August 2021 ### August 2021
**Added criteria based on formula; (1 OR 2) AND 3)** **Added criteria based on formula; (1 OR 2) AND 3)**
@@ -0,0 +1,113 @@
/**
* File Name: fflib_DoWork
* Description: Abstract class containing generic functionality to order, prioritise and define an execution context for the work item
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public abstract class fflib_DoWork implements fflib_IDoWork, Comparable
{
protected Integer priority = 0;
protected Boolean dedicatedQueueable;
public virtual Integer compareTo(Object compareToObject)
{
fflib_IDoWork compareTo = (fflib_IDoWork) compareToObject;
if (priority == compareTo.getPriority()) return 0;
if (priority > compareTo.getPriority()) return -1;
return 1;
}
/**
* Enable the work item to be executed in separate UnitOfWork executed in a Queueable context
*
* @return Instance of itself to allow for chaining
*/
public virtual fflib_IDoWork enableQueueable()
{
return enableQueueable(false);
}
/**
* Enable the work item to be executed as Queueable, not combined with other work items
*
* @param dedicated When true, execute in a dedicated Queueable context.
*
* @return Instance of itself to allow for chaining
*/
public virtual fflib_IDoWork enableQueueable(Boolean dedicated)
{
dedicatedQueueable = dedicated;
return this;
}
/**
* Gets the priority of this work item. The higher the number the higher the priority
*
* @return Returns the priority
*/
public virtual Integer getPriority()
{
return this.priority;
}
/**
* @return Returns true when the work item is intended to run in real-time
*/
public virtual Boolean isNotQueueable()
{
return !isQueueable();
}
/**
* @return Returns true when the work item is intended to be executed in separate UnitOfWork executed in a Queueable context
*/
public virtual Boolean isQueueable()
{
return (dedicatedQueueable != null);
}
/**
* @return Returns true when the work item is intended to be executed as Queueable, not combined with other work items
*/
public virtual Boolean isDedicatedQueueable()
{
return (dedicatedQueueable != null && dedicatedQueueable);
}
/**
* Sets the priority value of the work item, this is used to sort the work items
*
* @param priority The priority value, the working with the highest priority value will be executed first
*
* @return Instance of itself to allow for chaining
*/
public virtual fflib_IDoWork setPriority(Integer priority)
{
this.priority = priority;
return this;
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,253 @@
/**
* File Name: fflib_UnitOfWork
* Description: Unit Of Work executing a batch of work items at a single instance
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public virtual class fflib_UnitOfWork
extends fflib_DoWork
implements fflib_IUnitOfWork, fflib_IDoWork, Queueable
{
protected List<fflib_IDoWork> work;
protected List<fflib_IUnitOfWork> doNext;
public fflib_UnitOfWork()
{
this(new List<fflib_IDoWork>());
}
public fflib_UnitOfWork(fflib_IDoWork workItem)
{
this(new List<fflib_IDoWork>{ workItem });
}
public fflib_UnitOfWork(List<fflib_IDoWork> workItems)
{
this(workItems, new List<fflib_IUnitOfWork>());
}
public fflib_UnitOfWork(List<fflib_IDoWork> workItems, fflib_UnitOfWork nextWork)
{
this(workItems, new List<fflib_IUnitOfWork>{ nextWork });
}
public fflib_UnitOfWork(List<fflib_IDoWork> workItems, List<fflib_IUnitOfWork> nextWork)
{
this.work = workItems;
this.doNext = nextWork;
}
/**
* Adds a single work item is executed in a dedicated Queued context after the UnitOfWork is finished
*
* @param nextWork work item to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
public virtual fflib_IUnitOfWork addNext(fflib_IDoWork nextWork)
{
return addNext((fflib_IUnitOfWork) new fflib_UnitOfWork(nextWork));
}
/**
* Adds a UnitOfWork to be executed in a Queued context after the UnitOfWork is finished
*
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
public virtual fflib_IUnitOfWork addNext(fflib_IUnitOfWork nextWork)
{
return addNext(new List<fflib_IUnitOfWork>{ nextWork });
}
/**
* Adds an ordered list of UnitOfWork to be executed each in a separate Queued context after the UnitOfWork is finished
*
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
public virtual fflib_IUnitOfWork addNext(List<fflib_IUnitOfWork> nextWork)
{
doNext.addAll(nextWork);
return this;
}
/**
* Add a single work item to the queue
*
* @param work work item to be added
*
* @return Instance of itself to allow for method chaining
*/
public virtual fflib_UnitOfWork addWork(fflib_IDoWork work)
{
this.work.add(work);
return this;
}
/**
* Adds a list of work items to the queue
*
* @param work work items to be added
*
* @return Instance of itself to allow for method chaining
*/
public virtual fflib_UnitOfWork addWork(List<fflib_IDoWork> work)
{
this.work.addAll(work);
return this;
}
/**
* Do the work contained in the UnitOfWork
*/
public virtual void doWork()
{
this.work.sort();
doRealTimeWork();
doQueuedWork();
doNextWork();
}
/**
* Execute the work when being invoked from a queued context
*
* @param ctx QueueableContext
*/
public virtual void execute(QueueableContext ctx)
{
doWork();
}
/**
* Do start with the execution of the next work item to be executed in a Queued context
*/
protected virtual void doNextWork()
{
if (this.doNext.isEmpty()) return;
fflib_IUnitOfWork nextWork = this.doNext.remove(0);
nextWork.addNext(this.doNext);
enqueueJob(nextWork);
}
/**
* Put the given UnitOfWork in the Queue for execution
*
* @param nextWork The UnitOfWork to be executed in a Queued context
*/
protected virtual void enqueueJob(fflib_IUnitOfWork nextWork)
{
if (Test.isRunningTest())
{
nextWork.doWork();
}
else
{
System.enqueueJob(nextWork);
}
}
/**
* Put the given work item in the Queue for execution
*
* @param work The work item to be executed in a Queued context
*/
protected virtual void enqueueJob(fflib_IDoWork work)
{
if (Test.isRunningTest())
{
work.doWork();
}
else
{
System.enqueueJob(work);
}
}
/**
* Do start executing the work items marked for execution in a Queued context.
* Either in a dedicated Queued context or batched together in another UnitOfWork
*/
protected virtual void doQueuedWork()
{
if (this.work.isEmpty()) return;
List<fflib_IDoWork> workToQueue = new List<fflib_IDoWork>();
for (fflib_IDoWork workItem : work)
{
if (workItem.isNotQueueable()) continue;
if (workItem.isDedicatedQueueable())
{
enqueueJob(workItem);
}
else
{
workToQueue.add(workItem.enableQueueable(null));
}
}
if (workToQueue.isEmpty()) return;
enqueueJob((fflib_IUnitOfWork) new fflib_UnitOfWork(workToQueue));
}
/**
* Do start executing the work items which are not marked for Queued context.
*/
protected virtual void doRealTimeWork()
{
if (this.work.isEmpty()) return;
for (fflib_IDoWork workItem : work)
{
if (workItem.isQueueable()) continue;
doWork(workItem);
}
}
/**
* @param workItem
*/
protected virtual void doWork(fflib_IDoWork workItem)
{
try
{
workItem.doWork();
}
catch (Exception e)
{
throw new WorkItemException('WorkItem failed to execute with error: ' + e.getMessage() + ' ' + e.getStackTraceString());
}
}
public class WorkItemException extends Exception {}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,84 @@
/**
* File Name: fflib_IDoWork
* Description: Interface defining the method signatures for a work item used by the UnitOfWork
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface fflib_IDoWork extends Comparable
{
/**
* Do the work of this work item
*/
void doWork();
/**
* Enable the work item to be executed in separate UnitOfWork executed in a Queueable context
*
* @return Instance of itself to allow for chaining
*/
fflib_IDoWork enableQueueable();
/**
* Enable the work item to be executed as Queueable, not combined with other work items
*
* @param dedicated When true, execute in a dedicated Queueable context.
*
* @return Instance of itself to allow for chaining
*/
fflib_IDoWork enableQueueable(Boolean dedicated);
/**
* Gets the priority of this work item. The higher the number the higher the priority
*
* @return Returns the priority
*/
Integer getPriority();
/**
* @return Returns true when the work item is intended to be executed in separate UnitOfWork executed in a Queueable context
*/
Boolean isQueueable();
/**
* @return Returns true when the work item is intended to run in real-time
*/
Boolean isNotQueueable();
/**
* @return Returns true when the work item is intended to be executed as Queueable, not combined with other work items
*/
Boolean isDedicatedQueueable();
/**
* Sets the priority value of the work item, this is used to sort the work items
*
* @param priority The priority value, the working with the highest priority value will be executed first
*
* @return Instance of itself to allow for chaining
*/
fflib_IDoWork setPriority(Integer priority);
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,82 @@
/**
* File Name: fflib_IUnitOfWork
* Description: Interface class with method signatures for the Unit Of Work
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public interface fflib_IUnitOfWork
{
/**
* Adds a single work item is executed in a dedicated Queued context after the UnitOfWork is finished
*
* @param nextWork work item to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
fflib_IUnitOfWork addNext(fflib_IDoWork nextWork);
/**
* Adds a UnitOfWork to be executed in a Queued context after the UnitOfWork is finished
*
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
fflib_IUnitOfWork addNext(fflib_IUnitOfWork nextWork);
/**
* Adds an ordered list of UnitOfWork to be executed each in a separate Queued context after the UnitOfWork is finished
*
* @param nextWork UnitOfWork to be executed after the UnitOfWork is finished
*
* @return Instance of itself to allow for method chaining
*/
fflib_IUnitOfWork addNext(List<fflib_IUnitOfWork> nextWork);
/**
* Add a single work item to the queue
*
* @param work work item to be added
*
* @return Instance of itself to allow for method chaining
*/
fflib_IUnitOfWork addWork(fflib_IDoWork work);
/**
* Adds a list of work items to the queue
*
* @param work work items to be added
*
* @return Instance of itself to allow for method chaining
*/
fflib_IUnitOfWork addWork(List<fflib_IDoWork> work);
/**
* Do the work contained in the UnitOfWork
*/
void doWork();
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<status>Active</status>
</ApexClass>
@@ -0,0 +1,160 @@
/**
* File Name: fflib_UnitOfWorkTest
* Description: Unit Test class for fflib_UnitOfWork implementation
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above author notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@IsTest(IsParallel=true)
private class fflib_UnitOfWorkTest
{
public static List<String> orderOfExecution = new List<String>();
@IsTest
static void itShouldDoAllTheWork()
{
// GIVEN - Three different work items added to the fflib_DoWork
Work workA = new Work();
Work workB = new Work();
Work workC = new Work();
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
new List<fflib_IDoWork> {workA, workB, workC}
);
// WHEN - The work is being executed
System.Test.startTest();
myWork.doWork();
System.Test.stopTest();
// THEN - Then all three work items should be executed
System.assert(workA.didWork, 'Work item A did not execute');
System.assert(workB.didWork, 'Work item B did not execute');
System.assert(workC.didWork, 'Work item C did not execute');
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
@IsTest
static void itShouldOnlyDoTheRealTimeWorkAndQueueTheOther()
{
// GIVEN - Three different work items added to the fflib_DoWork
Work workA = new Work();
Work workB = new Work();
Work workC = (Work) new Work().enableQueueable();
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
new List<fflib_IDoWork> {workA, workB, workC}
);
final Integer numberOfQueueableJobs = Limits.getQueueableJobs();
// WHEN - The work is being executed
myWork.doWork(); // Execute without start-stop test due to Salesforce limitation
// THEN - Then all three work items should be executed
System.assert(workA.didWork, 'Work item A did not execute');
System.assert(workB.didWork, 'Work item B did not execute');
System.assert(workC.didWork, 'Work item C was executed in realtime while is should be queued');
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
@IsTest
static void itShouldExecuteAllTheChainedQueuedWork()
{
// GIVEN - Three different work items added to the fflib_DoWork to be executed in a chained queueable
Work workA = new Work();
Work workB = new Work();
Work workC = new Work();
fflib_IUnitOfWork myWork =
new fflib_UnitOfWork()
.addNext(workA)
.addNext(workB)
.addNext(workC);
final Integer numberOfQueueableJobs = Limits.getQueueableJobs();
// WHEN - The work is being executed
myWork.doWork(); // Execute without start-stop test due to Salesforce limitation
// THEN - Then all three work items should be executed
System.assertEquals(3, [SELECT COUNT() FROM fflib_LogEntry__c]);
}
@IsTest
static void itShouldExecuteAllItemsInTheCorrectOrder()
{
// GIVEN - Three different work items added to the fflib_DoWork
Work workA = (Work) new WorkA().setPriority(1);
Work workB = (Work) new WorkB().setPriority(2);
Work workC = (Work) new WorkC().setPriority(3);
fflib_UnitOfWork myWork = new fflib_UnitOfWork().addWork(
new List<fflib_IDoWork> {workA, workB, workC}
);
// WHEN - The work is being executed
System.Test.startTest();
myWork.doWork();
System.Test.stopTest();
// THEN - Then all three work items should be executed
System.assert(orderOfExecution.get(0).contains('WorkC'), 'Expected Work C with priority 3 to be executed first');
System.assert(orderOfExecution.get(1).contains('WorkB'), 'Expected Work B with priority 2 to be executed second');
System.assert(orderOfExecution.get(2).contains('WorkA'), 'Expected Work B with priority 1 to be executed last');
}
private virtual class Work extends fflib_DoWork
{
public Boolean didWork = false;
public virtual void doWork()
{
this.didWork = true;
insert new fflib_LogEntry__c(LogLevel__c = 'INFO', Message__c = 'Work got executed');
}
}
private class WorkA extends Work
{
public override void doWork()
{
orderOfExecution.add('WorkA');
}
}
private class WorkB extends Work
{
public override void doWork()
{
orderOfExecution.add('WorkB');
}
}
private class WorkC extends Work
{
public override void doWork()
{
orderOfExecution.add('WorkC');
}
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<status>Active</status>
</ApexClass>