diff --git a/.github/workflows/deploy.and.test.yml b/.github/workflows/deploy.and.test.yml
index 9372c5f5a25..ca7ee3c2602 100644
--- a/.github/workflows/deploy.and.test.yml
+++ b/.github/workflows/deploy.and.test.yml
@@ -15,21 +15,28 @@ jobs:
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- - name: Install SFDX CLI and authorize DevHub
- uses: apex-enterprise-patterns/setup-sfdx@v1 #We're using a fork of https://github.com/sfdx-actions/setup-sfdx for safety
+ - name: Install SF CLI and authorize DevHub
+ uses: apex-enterprise-patterns/setup-sfdx@v2 #We're using a fork of https://github.com/sfdx-actions/setup-sfdx for safety
with:
sfdx-auth-url: ${{ secrets.DEVHUB_SFDXURL }}
- - run: sfdx force:config:set defaultdevhubusername=SFDX-ENV -g #Even though the setup-sfdx action uses --setdefaultdevhubusername, it doesn't seem to stick since it uses --setdefaultusername so we brute force it here
- - run: echo y | sfdx plugins:install shane-sfdx-plugins
- - run: sfdx force:org:create -f config/project-scratch-def.json --setdefaultusername -d 1
- - run: sfdx shane:github:src:install -c -g apex-enterprise-patterns -r fflib-apex-mocks -p sfdx-source/apex-mocks
- - run: sfdx force:source:push
- - run: sfdx force:apex:test:run -w 5
- #Intentionally install the Sample Code after the core AEP Commons test pass succeeds so that we don't deploy anything in Sample Code
- #that could mask a test failure. A much more involved solution would've been to do a workflow_dispatch to the samplecode project.
- - run: sfdx shane:github:src:install -c -g apex-enterprise-patterns -r fflib-apex-common-samplecode -p sfdx-source/apex-common-samplecode
- - run: sfdx force:apex:test:run -w 5
+ - name: Install the required plugins
+ run: echo y | sf plugins install shane-sfdx-plugins
+ - name: Setup the config parameters needed
+ run: sf config set target-dev-hub SFDX-ENV --global #Even though the setup-sfdx action uses --setdefaultdevhubusername, it doesn't seem to stick since it uses --setdefaultusername so we brute force it here
+ - name: Create the scratch org
+ run: sf org create scratch --definition-file config/project-scratch-def.json --set-default --duration-days 1 --no-track-source
+ - name: Install required dependency frameworks
+ run: sf shane github src install --convert --githubuser apex-enterprise-patterns --repo fflib-apex-mocks --path sfdx-source/apex-mocks
+ - name: Deploy and compile the codebase
+ run: sf project deploy start
+ - name: Run the core framework tests
+ run: sf apex run test --wait 5
+ # Intentionally install the Sample Code after the core AEP Commons test pass succeeds so that we don't deploy anything in Sample Code
+ # that could mask a test failure. A much more involved solution would've been to do a workflow_dispatch to the samplecode project.
+ - name: Install sample code project to verify with
+ run: sf shane github src install --convert --githubuser apex-enterprise-patterns --repo fflib-apex-common-samplecode --path sfdx-source/apex-common-samplecode
+ - name: Run the core framework tests and the sample code project tests
+ run: sf apex run test --wait 5
- name: Destroy scratch org
- run: sfdx force:org:delete -p
+ run: sf org delete scratch --no-prompt
if: always()
-
diff --git a/.github/workflows/manage.sf.api.versions.yml b/.github/workflows/manage.sf.api.versions.yml
new file mode 100644
index 00000000000..6900179c01d
--- /dev/null
+++ b/.github/workflows/manage.sf.api.versions.yml
@@ -0,0 +1,22 @@
+name: Manage SF API Versions
+on:
+ workflow_dispatch:
+ inputs:
+ api-version:
+ description: 'api version in the format XX e.g 58'
+ required: true
+ type: string
+jobs:
+ update:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: apex-enterprise-patterns/manage-sf-api-version@v1.0.0
+ with:
+ api-version: ${{inputs.api-version}}
+ - uses: peter-evans/create-pull-request@v5
+ with:
+ title: 'Bump API Versions to ${{inputs.api-version}}.0'
+ body: 'Automatically bumped by GitHub Actions '
+ branch: 'devops/bump-api-versions-v${{inputs.api-version}}.0'
+ commit-message: 'chore: bump api to v${{inputs.api-version}}.0'
diff --git a/README.md b/README.md
index b1328c3d571..f048e626413 100644
--- a/README.md
+++ b/README.md
@@ -32,14 +32,6 @@ Application Enterprise Patterns on Force.com
Design patterns are an invaluable tool for developers and architects looking to build enterprise solutions. Here are presented some tried and tested enterprise application engineering patterns that have been used in other platforms and languages. We will discuss and illustrate how patterns such as Data Mapper, Service Layer, Unit of Work and of course Model View Controller can be applied to Force.com. Applying these patterns can help manage governed resources (such as DML) better, encourage better separation-of-concerns in your logic and enforce Force.com coding best practices.
-Dreamforce Session and Slides
------------------------------
-
-- View slides for the **Dreamforce 2013** session [here](https://docs.google.com/file/d/0B6brfGow3cD8RVVYc1dCX2s0S1E/edit)
-- Video recording of the **Dreamforce 2013** session [here](http://www.youtube.com/watch?v=qlq46AEAlLI).
-- Video recording of the **Advanced Apex Enterprise Dreamforce 2014** session [here](http://dreamforce.vidyard.com/watch/7QtP2628KmtXfmiwI-7B1w%20).
-- View slides for the **Dreamforce 2015** session [here](http://www.slideshare.net/andyinthecloud/building-strong-foundations-apex-enterprise-patterns)
-
Documentation
-------------
@@ -52,11 +44,19 @@ Documentation
- [Apex Enterprise Patterns - Service Layer](http://wiki.developerforce.com/page/Apex_Enterprise_Patterns_-_Service_Layer)
- [Apex Enterprise Patterns - Domain Layer](http://wiki.developerforce.com/page/Apex_Enterprise_Patterns_-_Domain_Layer)
- [Apex Enterprise Patterns - Selector Layer](https://github.com/financialforcedev/df12-apex-enterprise-patterns#data-mapper-selector)
+- View slides for the **Dreamforce 2013** session [here](https://docs.google.com/file/d/0B6brfGow3cD8RVVYc1dCX2s0S1E/edit)
+- View slides for the **Dreamforce 2015** session [here](http://www.slideshare.net/andyinthecloud/building-strong-foundations-apex-enterprise-patterns)
+
+**Related Webinars**
+- [Advanced Apex Enterprise Patterns](https://www.youtube.com/watch?v=BLXp0ZP0cF0)
+- [Apex Hours (August 2020): Apex Enterprise Patterns](https://www.apexhours.com/apex-enterprise-patterns/)
+
+**Related Book**
+- [Salesforce Platform Enterprise Architecture, 4th Edition, by Andrew Fawcett](https://www.amazon.com/Salesforce-Platform-Enterprise-Architecture-applications-ebook/dp/B0BD8TBT75/)
**Other Related Blogs**
- [Preview of Advanced Apex Patterns Session (Application Factory and ApexMocks Features)](http://andyinthecloud.com/2014/08/26/preview-of-advanced-apex-enterprise-patterns-session/)
- [Unit Testing with the Domain Layer](http://andyinthecloud.com/2014/03/23/unit-testing-with-the-domain-layer/)
-- [MavensMate Templates](http://andyinthecloud.com/2014/05/23/mavensmate-templates-and-apex-enterprise-patterns/)
- [FinancialForce Apex Common Updates](http://andyinthecloud.com/2014/06/28/financialforce-apex-common-updates/)
diff --git a/config/multicurrency-scratch-def.json b/config/multicurrency-scratch-def.json
new file mode 100644
index 00000000000..13823162894
--- /dev/null
+++ b/config/multicurrency-scratch-def.json
@@ -0,0 +1,15 @@
+{
+ "orgName": "apex-common",
+ "edition": "Developer",
+ "features": [
+ "MultiCurrency"
+ ],
+ "settings": {
+ "currencySettings":{
+ "enableMultiCurrency": true
+ },
+ "lightningExperienceSettings": {
+ "enableS1DesktopEnabled": true
+ }
+ }
+}
diff --git a/sfdx-project.json b/sfdx-project.json
index 88c7589efa9..88ba4540e14 100644
--- a/sfdx-project.json
+++ b/sfdx-project.json
@@ -1,11 +1,11 @@
{
- "packageDirectories": [
- {
- "path": "sfdx-source/apex-common",
- "default": true
- }
- ],
- "namespace": "",
- "sfdcLoginUrl": "https://login.salesforce.com",
- "sourceApiVersion": "57.0"
+ "packageDirectories": [
+ {
+ "path": "sfdx-source/apex-common",
+ "default": true
+ }
+ ],
+ "namespace": "",
+ "sfdcLoginUrl": "https://login.salesforce.com",
+ "sourceApiVersion": "60.0"
}
\ No newline at end of file
diff --git a/sfdx-source/apex-common/main/classes/fflib_Application.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_Application.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_Application.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_Application.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IDomain.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IDomain.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IDomain.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IDomain.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IDomainFactory.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IDomainFactory.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IDomainFactory.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IDomainFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IObjects.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IObjects.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IObjects.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IObjects.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_ISObjects.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_ISObjects.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_ISObjects.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_ISObjects.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_ISelectorFactory.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_ISelectorFactory.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_ISelectorFactory.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_ISelectorFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IServiceFactory.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IServiceFactory.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IServiceFactory.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IServiceFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_IUnitOfWorkFactory.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_IUnitOfWorkFactory.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_IUnitOfWorkFactory.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_IUnitOfWorkFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_Objects.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_Objects.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_Objects.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_Objects.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls
index ac1575a5e57..0eac50185f0 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls
@@ -100,12 +100,18 @@ public class fflib_SObjectDescribe {
* e.g. getting the Account field on Contact would fail without being referenced as AccountId - both work here.
**/
public Schema.SObjectField getField(String fieldName, Boolean implyNamespace){
- Schema.SObjectField result = wrappedFields.get(
- (fieldName.endsWithIgnoreCase('__r') ? //resolve custom field cross-object (__r) syntax
- (fieldName.removeEndIgnoreCase('__r')+'__c') :
- fieldName),
- implyNamespace
- );
+ String fieldNameAdjusted = fieldName;
+
+ if ( fieldName.endsWithIgnoreCase('__r') ) //resolve custom field cross-object (__r) syntax
+ {
+ fieldNameAdjusted = fieldName.removeEndIgnoreCase('__r') + '__c';
+ }
+ else if ( fieldName.endsWithIgnoreCase('__pr') ) //resolve custom field cross-object (__pr) syntax for person accounts
+ {
+ fieldNameAdjusted = fieldName.removeEndIgnoreCase('__pr') + '__pc';
+ }
+
+ Schema.SObjectField result = wrappedFields.get( fieldNameAdjusted, implyNamespace );
if(result == null){
result = wrappedFields.get(fieldName+'Id', implyNamespace); //in case it's a standard lookup in cross-object format
}
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls
index e0def0a7ed3..3abbdc4e380 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls
@@ -433,12 +433,12 @@ public abstract with sharing class fflib_SObjectSelector
}
// Automatically select the CurrencyIsoCode for MC orgs (unless the object is a known exception to the rule)
if(UserInfo.isMultiCurrencyOrganization() && CURRENCY_ISO_CODE_ENABLED){
- queryFactory.selectField(relationshipFieldPath+'.CurrencyIsoCode');
+ queryFactory.selectField(relationshipFieldPath+'.CurrencyIsoCode', getSObjectType());
}
}
/**
- * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the parentQueryFactory
+ * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the child QueryFactory
**/
public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory)
{
@@ -461,7 +461,7 @@ public abstract with sharing class fflib_SObjectSelector
}
/**
- * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the parentQueryFactory
+ * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the child QueryFactory
**/
public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory, String relationshipName)
{
@@ -533,7 +533,7 @@ public abstract with sharing class fflib_SObjectSelector
// Automatically select the CurrencyIsoCode for MC orgs (unless the object is a known exception to the rule)
if(UserInfo.isMultiCurrencyOrganization() && CURRENCY_ISO_CODE_ENABLED)
- queryFactory.selectField('CurrencyIsoCode');
+ queryFactory.selectField('CurrencyIsoCode', getSObjectType());
}
// Parse the getOrderBy()
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls
index d02ed406351..bc778ed4584 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls
@@ -109,6 +109,11 @@ public virtual class fflib_SObjectUnitOfWork
}
public virtual void eventPublish(List objList)
{
+ if (objList.isEmpty())
+ {
+ return;
+ }
+
EventBus.publish(objList);
}
public virtual void emptyRecycleBin(List objList)
@@ -702,7 +707,7 @@ public virtual class fflib_SObjectUnitOfWork
{
for (Schema.SObjectType sObjectType : m_sObjectTypes)
{
- m_relationships.get(sObjectType.getDescribe().getName()).resolve();
+ m_relationships.get(sObjectType.getDescribe().getName()).resolve();
m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values());
}
}
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjects.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SObjects.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SObjects.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SObjects.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml b/sfdx-source/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml
+++ b/sfdx-source/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml
index cac127cd1a1..c7804fa6d37 100644
--- a/sfdx-source/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml
@@ -1,4 +1,4 @@
- 57.0
+ 60.0
diff --git a/sfdx-source/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls
index 11c464b2c9e..c89b19e7fb0 100644
--- a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls
+++ b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls
@@ -80,7 +80,7 @@ private with sharing class fflib_SObjectSelectorTest
Database.QueryLocator result = selector.queryLocatorById(idSet);
System.Iterator iteratorResult = result.iterator();
Test.stopTest();
-
+
System.assert(true, iteratorResult.hasNext());
Account account = (Account) iteratorResult.next();
system.assertEquals('TestAccount2',account.Name);
@@ -155,7 +155,7 @@ private with sharing class fflib_SObjectSelectorTest
{
Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector();
String soql = selector.newQueryFactory().toSOQL();
- Pattern p = Pattern.compile('SELECT (.*) FROM Account ORDER BY Name DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
+ Pattern p = Pattern.compile('SELECT (.*) FROM Account ORDER BY AccountNumber DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
Matcher m = p.matcher(soql);
System.assert(m.matches(), 'Generated SOQL does not match expected pattern. Here is the generated SOQL: ' + soql);
System.assertEquals(1, m.groupCount(), 'Unexpected number of groups captured.');
@@ -167,7 +167,8 @@ private with sharing class fflib_SObjectSelectorTest
{
Testfflib_SObjectSelectorDefaultSorting selector = new Testfflib_SObjectSelectorDefaultSorting(false);
String soql = selector.newQueryFactory().toSOQL();
- Pattern p = Pattern.compile('SELECT (.*) FROM Account ORDER BY Name ASC NULLS FIRST ');
+ Pattern p = Pattern.compile(String.format('SELECT (.*) FROM Account ORDER BY {0} ASC NULLS FIRST ',
+ new List{selector.getOrderBy()}));
Matcher m = p.matcher(soql);
System.assert(m.matches(), 'Generated SOQL does not match expected pattern. Here is the generated SOQL: ' + soql);
System.assertEquals(1, m.groupCount(), 'Unexpected number of groups captured.');
@@ -208,7 +209,7 @@ private with sharing class fflib_SObjectSelectorTest
}
- @isTest
+ @IsTest
static void testWithoutSorting()
{
//Given
@@ -225,7 +226,7 @@ private with sharing class fflib_SObjectSelectorTest
String soql = qf.toSOQL();
//Then
- Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM Account ORDER BY Name DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
+ Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM Account ORDER BY AccountNumber DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
Matcher soqlMatcher = soqlPattern.matcher(soql);
soqlMatcher.matches();
@@ -234,7 +235,7 @@ private with sharing class fflib_SObjectSelectorTest
}
// Test case of ordering with NULLS LAST option passed into the ordering method
- @isTest
+ @IsTest
static void testWithOrderingNullsLast()
{
// Build the selector to test with
@@ -252,7 +253,7 @@ private with sharing class fflib_SObjectSelectorTest
String soql = qf.toSOQL();
// Assert that the
- Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM Account ORDER BY Name DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
+ Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM Account ORDER BY AccountNumber DESC NULLS FIRST , AnnualRevenue ASC NULLS LAST ');
Matcher soqlMatcher = soqlPattern.matcher(soql);
system.assert(soqlMatcher.matches(), 'The SOQL should have that expected.');
}
@@ -299,7 +300,7 @@ private with sharing class fflib_SObjectSelectorTest
//Then
String soql = qf.toSOQL();
- Pattern soqlPattern = Pattern.compile('SELECT Id, \\(SELECT (.*) FROM Users ORDER BY Name ASC NULLS FIRST \\) +FROM Account');
+ Pattern soqlPattern = Pattern.compile(String.format('SELECT Id, \\(SELECT (.*) FROM Users ORDER BY {0} ASC NULLS FIRST \\) +FROM Account',new List{selector.getOrderBy()}));
Matcher soqlMatcher = soqlPattern.matcher(soql);
System.assert(soqlMatcher.matches(), 'Generated SOQL does not match expected pattern. Here is the generated SOQL: ' + soql);
@@ -324,7 +325,8 @@ private with sharing class fflib_SObjectSelectorTest
//Then
String soql = qf.toSOQL();
- Pattern soqlPattern = Pattern.compile('SELECT Id, \\(SELECT (.*) FROM Users ORDER BY Name ASC NULLS FIRST \\) +FROM Account');
+ Pattern soqlPattern = Pattern.compile(String.format('SELECT Id, \\(SELECT (.*) FROM Users ORDER BY {0} ASC NULLS FIRST \\) +FROM Account',
+ new List{selector.getOrderBy()}));
Matcher soqlMatcher = soqlPattern.matcher(soql);
System.assert(soqlMatcher.matches(), 'Generated SOQL does not match expected pattern. Here is the generated SOQL: ' + soql);
@@ -376,7 +378,7 @@ private with sharing class fflib_SObjectSelectorTest
static void toSOQL_When_UserModeAndUserCannnotReadObject_Expect_QueryException(){
AccessLevelOpportunitySelector sel = new AccessLevelOpportunitySelector();
- User u = getLimitedReadOnlyUser();
+ User u = getMinimumAccessUser();
System.runAs(u){
try{
System.debug(sel.newQueryFactory().toSOQL());
@@ -397,7 +399,7 @@ private with sharing class fflib_SObjectSelectorTest
static void toSOQL_When_SystemModeAndUserCannnotReadObject_Expect_Success(){
AccessLevelOpportunitySelector sel = new AccessLevelOpportunitySelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
- User u = getLimitedReadOnlyUser();
+ User u = getMinimumAccessUser();
System.runAs(u){
sel.selectSObjectsById(new Set{fflib_IDGenerator.generate(Opportunity.SObjectType)});
}
@@ -408,7 +410,7 @@ private with sharing class fflib_SObjectSelectorTest
AccessLevelAccountSelector sel = new AccessLevelAccountSelector();
//Account has Read access by the limited read only user but no FLS access
- User u = getLimitedReadOnlyUser();
+ User u = getMinimumAccessUser();
System.runAs(u){
try{
System.debug(sel.newQueryFactory().toSOQL());
@@ -431,7 +433,7 @@ private with sharing class fflib_SObjectSelectorTest
AccessLevelAccountSelector sel = new AccessLevelAccountSelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
//Account has Read access by the limited read only user but no FLS access
- User u = getLimitedReadOnlyUser();
+ User u = getMinimumAccessUser();
System.runAs(u){
sel.selectSObjectsById(new Set{fflib_IDGenerator.generate(Account.SObjectType)});
}
@@ -443,7 +445,8 @@ private with sharing class fflib_SObjectSelectorTest
String soql = sel.createSelectAllWithAccountSOQL();
- String expected = 'SELECT name, id, amount, closedate, account\\.name, account\\.billingpostalcode(.*)FROM Opportunity WITH SYSTEM_MODE ORDER BY Name ASC NULLS FIRST ';
+ String expected = String.format('SELECT name, id, amount, closedate, (currencyisocode, )?account\\.name, account\\.billingpostalcode(, currencyisocode)? FROM Opportunity WITH SYSTEM_MODE ORDER BY {0} ASC NULLS FIRST ',
+ new List{sel.getOrderBy()});
Pattern soqlPattern = Pattern.compile(expected);
Matcher soqlMatcher = soqlPattern.matcher(soql);
System.assert(soqlMatcher.matches(),'Expected: ' + expected + ' Actual:' + soql);
@@ -455,7 +458,8 @@ private with sharing class fflib_SObjectSelectorTest
String soql = sel.createSelectAccountWithOpportunitiesSOQL();
- String expected = 'SELECT name, id, annualrevenue, accountnumber,(.*)\\(SELECT name, id, amount, closedate FROM Opportunities ORDER BY Name ASC NULLS FIRST \\) FROM Account WITH SYSTEM_MODE ORDER BY Name ASC NULLS FIRST ';
+ String expected = String.format('SELECT name, id, annualrevenue, accountnumber, (currencyisocode, )?\\(SELECT name, id, amount, closedate(, currencyisocode)? FROM Opportunities ORDER BY {0} ASC NULLS FIRST \\) FROM Account WITH SYSTEM_MODE ORDER BY {0} ASC NULLS FIRST ',
+ new List{sel.getOrderBy()});
Pattern soqlPattern = Pattern.compile(expected);
Matcher soqlMatcher = soqlPattern.matcher(soql);
System.assert(soqlMatcher.matches(),'Expected: ' + expected + ' Actual:' + soql);
@@ -491,7 +495,7 @@ private with sharing class fflib_SObjectSelectorTest
public override String getOrderBy()
{
- return 'Name DESC, AnnualRevenue ASC NULLS LAST';
+ return 'AccountNumber DESC, AnnualRevenue ASC NULLS LAST';
}
}
@@ -632,7 +636,7 @@ private with sharing class fflib_SObjectSelectorTest
private static User createChatterExternalUser()
{
// Can only proceed with test if we have a suitable profile - Chatter External license has no access to Opportunity
- List testProfiles = [Select Id From Profile where UserLicense.Name='Chatter External' limit 1];
+ List testProfiles = [SELECT Id FROM Profile WHERE UserLicense.Name='Chatter External' LIMIT 1];
if(testProfiles.size()!=1)
return null;
@@ -647,18 +651,18 @@ private with sharing class fflib_SObjectSelectorTest
return testUser;
}
- private static User getLimitedReadOnlyUser(){
- return fflib_SecurityUtilsTest.setupTestUser('Read Only');
+ private static User getMinimumAccessUser(){
+ return fflib_SecurityUtilsTest.setupTestUser(true);
}
- @isTest
+ @IsTest
static void toSOQL_When_PolymorphicSelect_Expect_RelatedType() {
//Given
CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.LEGACY);
fflib_QueryFactory qf = cmSelector.newQueryFactory();
- new Testfflib_LeadSelector().configureQueryFactoryFields(qf, 'Lead');
- new Testfflib_UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
+ new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
+ new UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
Set expectedSelectFields = new Set{
@@ -666,13 +670,16 @@ private with sharing class fflib_SObjectSelectorTest
};
if (UserInfo.isMultiCurrencyOrganization()) {
expectedSelectFields.add('CurrencyIsoCode');
+ expectedSelectFields.add('Lead.CurrencyIsoCode');
+ expectedSelectFields.add('Lead.Owner.CurrencyIsoCode'); // Because the Selector is for User; Group would not have
}
//When
String soql = qf.toSOQL();
//Then
- Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM CampaignMember ORDER BY CreatedDate ASC NULLS FIRST ');
+ Pattern soqlPattern = Pattern.compile(String.format('SELECT (.*) FROM CampaignMember ORDER BY {0} ASC NULLS FIRST ',
+ new List{cmSelector.getOrderBy()}));
Matcher soqlMatcher = soqlPattern.matcher(soql);
soqlMatcher.matches();
@@ -680,21 +687,73 @@ private with sharing class fflib_SObjectSelectorTest
System.assertEquals(expectedSelectFields, new Set(actualSelectFields));
}
- @isTest
- static void toSOQL_When_SystemModePolymorphicSelect_Expect_RelatedType() {
- CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
+ @IsTest
+ static void toSOQL_When_PolymorphicSelectInMulticurrency_Expect_RelatedType() {
+ //Given
+
+ CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.LEGACY);
fflib_QueryFactory qf = cmSelector.newQueryFactory();
- new Testfflib_LeadSelector().configureQueryFactoryFields(qf, 'Lead');
- new Testfflib_UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
+ new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
+ new GroupSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
+
- List expectedSelectFields = new List{
- 'id', 'status', 'lead.ownerid', 'lead.id', 'lead.owner.userroleid', 'lead.owner.id'
+ Set expectedSelectFields = new Set{
+ 'Id', 'Status', 'Lead.Id', 'Lead.OwnerId', 'Lead.Owner.Id'
};
+ Set unexpectedSelectFields = new Set();
if (UserInfo.isMultiCurrencyOrganization()) {
expectedSelectFields.add('CurrencyIsoCode');
+ expectedSelectFields.add('Lead.CurrencyIsoCode');
+
+ unexpectedSelectFields.add('Lead.Owner.CurrencyIsoCode'); // Because Group does NOT have CurrencyIsoCode
+ }
+
+ //When
+ String soql = qf.toSOQL();
+ System.debug(soql);
+
+ //Then
+ Pattern soqlPattern = Pattern.compile('SELECT (.*) FROM CampaignMember ORDER BY CreatedDate ASC NULLS FIRST ');
+ Matcher soqlMatcher = soqlPattern.matcher(soql);
+ soqlMatcher.matches();
+
+ List actualSelectFields = soqlMatcher.group(1).deleteWhiteSpace().split(',');
+ Set actualSelectFieldsSet = new Set(actualSelectFields);
+ Assert.areEqual(expectedSelectFields, actualSelectFieldsSet);
+ if (unexpectedSelectFields.size() > 0) {
+ Assert.isFalse(
+ actualSelectFieldsSet.containsAll(unexpectedSelectFields),
+ String.format('The fields {0} were not expected on actualSelectFieldsSet {1}', new List>{unexpectedSelectFields, actualSelectFieldsSet})
+ );
+ }
+ }
+
+ @IsTest
+ static void toSOQL_When_SystemModePolymorphicSelect_Expect_RelatedType() {
+ CampaignMemberSelector cmSelector = new CampaignMemberSelector(fflib_SObjectSelector.DataAccess.SYSTEM_MODE);
+ fflib_QueryFactory qf = cmSelector.newQueryFactory();
+ new LeadSelector().configureQueryFactoryFields(qf, 'Lead');
+ new UserSelector().configureQueryFactoryFields(qf, 'Lead.Owner');
+
+ List expectedSelectFields = new List();
+ expectedSelectFields.add('id');
+ expectedSelectFields.add('status');
+ if (UserInfo.isMultiCurrencyOrganization()) {
+ expectedSelectFields.add('currencyisocode');
+ }
+ expectedSelectFields.add('lead.ownerid');
+ expectedSelectFields.add('lead.id');
+ if (UserInfo.isMultiCurrencyOrganization()) {
+ expectedSelectFields.add('lead.currencyisocode');
+ }
+ expectedSelectFields.add('lead.owner.userroleid');
+ expectedSelectFields.add('lead.owner.id');
+ if (UserInfo.isMultiCurrencyOrganization()) {
+ expectedSelectFields.add('lead.owner.currencyisocode');
}
- String expectedSOQL = 'SELECT ' + String.join(expectedSelectFields,', ') + ' FROM CampaignMember WITH SYSTEM_MODE ORDER BY CreatedDate ASC NULLS FIRST ';
+ String expectedSOQL = String.format('SELECT ' + String.join(expectedSelectFields,', ') + ' FROM CampaignMember WITH SYSTEM_MODE ORDER BY {0} ASC NULLS FIRST ',
+ new List{cmSelector.getOrderBy()});
//When
String actualSOQL = qf.toSOQL();
@@ -703,7 +762,6 @@ private with sharing class fflib_SObjectSelectorTest
System.assertEquals(expectedSOQL, actualSOQL);
}
-
private class CampaignMemberSelector extends fflib_SObjectSelector {
public CampaignMemberSelector(DataAccess access) {
super(false, access);
@@ -721,8 +779,8 @@ private with sharing class fflib_SObjectSelectorTest
}
}
- private class Testfflib_UserSelector extends fflib_SObjectSelector {
- public Testfflib_UserSelector() {
+ private class UserSelector extends fflib_SObjectSelector {
+ public UserSelector() {
super();
}
@@ -738,8 +796,24 @@ private with sharing class fflib_SObjectSelectorTest
}
}
- private class Testfflib_LeadSelector extends fflib_SObjectSelector {
- public Testfflib_LeadSelector() {
+ private class GroupSelector extends fflib_SObjectSelector {
+ public GroupSelector() {
+ super();
+ }
+
+ public List getSObjectFieldList() {
+ return new List{
+ Group.Id
+ };
+ }
+
+ public Schema.SObjectType getSObjectType() {
+ return Group.sObjectType;
+ }
+ }
+
+ private class LeadSelector extends fflib_SObjectSelector {
+ public LeadSelector() {
super();
}
diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml
index 754ecb15544..f5e18fd16c7 100644
--- a/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml
+++ b/sfdx-source/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml
@@ -1,5 +1,5 @@
- 57.0
+ 60.0
Active
diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls
index 57cd6bfe6a5..e83ef817094 100644
--- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls
+++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls
@@ -609,7 +609,7 @@ private with sharing class fflib_SObjectUnitOfWorkTest
}
@IsTest
- private static void testRegisterDirtyRelatedToNewSobject() {
+ private static void testRegisterDirtyWithRelationship() {
// GIVEN an existing opportunity
Opportunity existingOpp = new Opportunity(
Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType),
@@ -617,17 +617,17 @@ private with sharing class fflib_SObjectUnitOfWorkTest
StageName = 'Closed',
CloseDate = System.today()
);
- // AND a new Account to which the existing opportunity will be related to
+ // AND an existing Account to which the existing opportunity will be related to
Account newAccount = new Account(
Name = 'New Account'
);
// WHEN
Test.startTest();
- MockDML mockDML = new MockDML();
+ MockDMLWithInsertIds mockDML = new MockDMLWithInsertIds();
List mySobjects = new List{
- Account.SObjectType,
- Opportunity.SObjectType
+ Opportunity.SObjectType,
+ Account.SObjectType
};
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(mySobjects, mockDML);
uow.registerNew(newAccount);
@@ -636,7 +636,7 @@ private with sharing class fflib_SObjectUnitOfWorkTest
Test.stopTest();
// THEN
- System.assert(
+ System.Assert.isTrue(
new fflib_MatcherDefinitions.SObjectsWith(
new List