Improve performance of Dynamic Application Factories
Also added a method overload to provide a Package Name. Useful for DX based second generation packages. Now each package can have their own bindings.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="RunAllTest" type="com.illuminatedcloud.intellij" factoryName="Apex Unit Tests" show_console_on_std_err="false" show_console_on_std_out="false">
|
||||
<option name="allTests" value="true" />
|
||||
<option name="allowRunningInParallel" value="false" />
|
||||
<option name="apexLogLevel" value="DEBUG" />
|
||||
<option name="calloutsLogLevel" value="INFO" />
|
||||
<option name="connectionName" value="apex-extensions" />
|
||||
<option name="connectionType" value="SFDX" />
|
||||
<option name="contextConfiguration" value="false" />
|
||||
<option name="databaseLogLevel" value="INFO" />
|
||||
<option name="directoryPath" />
|
||||
<option name="logLevelsInitialized" value="true" />
|
||||
<option name="moduleToTestClassNames">
|
||||
<map />
|
||||
</option>
|
||||
<option name="nbaLogLevel" value="INFO" />
|
||||
<option name="profilingLogLevel" value="INFO" />
|
||||
<option name="projectPathOnTarget" />
|
||||
<option name="selectedOptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="systemLogLevel" value="DEBUG" />
|
||||
<option name="testClassToTestMethodNames">
|
||||
<map />
|
||||
</option>
|
||||
<option name="testSuiteName" />
|
||||
<option name="validationLogLevel" value="INFO" />
|
||||
<option name="visualforceLogLevel" value="INFO" />
|
||||
<option name="waveLogLevel" value="INFO" />
|
||||
<option name="workflowLogLevel" value="INFO" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -37,6 +37,7 @@ public virtual class fflib_AppBinding
|
||||
protected System.Type implementationType { get; set; }
|
||||
protected System.Type interfaceType { get; set; }
|
||||
protected String namespacePrefix { get; set; }
|
||||
protected String packageName { get; set; }
|
||||
protected fflib_SharingMode sharingMode { get; set; }
|
||||
|
||||
public Integer compareTo(Object compareTo)
|
||||
@@ -89,6 +90,14 @@ public virtual class fflib_AppBinding
|
||||
return this.namespacePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the Application Package Name for the binding
|
||||
*/
|
||||
public String getPackageName()
|
||||
{
|
||||
return this.packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the number of the order sequence in which the binding should be returned
|
||||
*/
|
||||
@@ -188,6 +197,19 @@ public virtual class fflib_AppBinding
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Application Second Generation Package Name
|
||||
*
|
||||
* @param applicationPackageName The name of the 2GP package
|
||||
*
|
||||
* @return An instance of itself to enable method chaining
|
||||
*/
|
||||
public fflib_IAppBinding setPackageName(String applicationPackageName)
|
||||
{
|
||||
this.packageName = applicationPackageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order number so that the resolver can sort the bindings.
|
||||
* The higher the number, the more priority.
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ public with sharing class fflib_AppBindingMetaDataModule
|
||||
.setImplementationType(getSystemType(binding, fflib_AppBinding__mdt.ImplementationType__c))
|
||||
.setInterfaceType(getSystemType(binding, fflib_AppBinding__mdt.InterfaceType__c))
|
||||
.setNamespacePrefix(binding.NamespacePrefix)
|
||||
.setPackageName(binding.ApplicationPackage__r.DeveloperName)
|
||||
.setSequence(binding.BindingSequence__c)
|
||||
.setSharingMode(getSharingModeFromString(binding.SharingMode__c))
|
||||
);
|
||||
|
||||
+5
@@ -132,6 +132,11 @@ public virtual class fflib_AppBindingResolver
|
||||
return this;
|
||||
}
|
||||
|
||||
public fflib_IAppBindingResolver byPackageName(String packageName)
|
||||
{
|
||||
this.bindingToResolve.setPackageName(packageName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles lazy loading of modules, if no custom module is configured (via addModule) the default module is returned
|
||||
|
||||
+68
-10
@@ -63,6 +63,67 @@ public virtual class fflib_AppBindings
|
||||
}
|
||||
}
|
||||
|
||||
public List<fflib_IAppBinding> getBindings()
|
||||
{
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
public Map<Object, Type> getImplementationByBindingObject()
|
||||
{
|
||||
this.bindings.sort();
|
||||
|
||||
Map<Object, Type> result = new Map<Object, Type>();
|
||||
for (fflib_IAppBinding binding : bindings)
|
||||
{
|
||||
Object bindingObject = binding.getBindingObject();
|
||||
if (result.containsKey(bindingObject)) continue; // Skip bindings with lower sequence
|
||||
|
||||
result.put(bindingObject, binding.getImplementationType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<Type, Type> getImplementationByInterface()
|
||||
{
|
||||
this.bindings.sort();
|
||||
|
||||
Map<Type, Type> result = new Map<Type, Type>();
|
||||
for (fflib_IAppBinding binding : bindings)
|
||||
{
|
||||
Type interfaceType = binding.getInterfaceType();
|
||||
if (result.containsKey(interfaceType)) continue; // Skip bindings with lower sequence
|
||||
|
||||
result.put(interfaceType, binding.getImplementationType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<SObjectType, Type> getImplementationBySObjectType()
|
||||
{
|
||||
Map<SObjectType, Type> result = new Map<SObjectType, Type>();
|
||||
for (fflib_IAppBinding binding : bindings)
|
||||
{
|
||||
Object bindingObject = binding.getBindingObject();
|
||||
if (bindingObject instanceof SObjectType && result.containsKey((SObjectType) bindingObject) == false)
|
||||
{
|
||||
result.put((SObjectType) bindingObject, binding.getImplementationType());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<System.Type> getImplementations()
|
||||
{
|
||||
List<System.Type> result = new List<System.Type>();
|
||||
for (fflib_IAppBinding binding : bindings)
|
||||
{
|
||||
result.add(
|
||||
binding.getImplementationType()
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public fflib_IAppBindings selectByObject(Object bindingObject)
|
||||
{
|
||||
if (null == bindingObject) return this;
|
||||
@@ -149,21 +210,18 @@ public virtual class fflib_AppBindings
|
||||
return new fflib_AppBindings(result);
|
||||
}
|
||||
|
||||
public List<fflib_IAppBinding> getBindings()
|
||||
public fflib_IAppBindings selectByPackageName(String packageName)
|
||||
{
|
||||
return this.bindings;
|
||||
}
|
||||
if (null == packageName) return this;
|
||||
|
||||
public List<System.Type> getImplementations()
|
||||
{
|
||||
List<System.Type> result = new List<System.Type>();
|
||||
List<fflib_IAppBinding> result = new List<fflib_IAppBinding>();
|
||||
for (fflib_IAppBinding binding : bindings)
|
||||
{
|
||||
result.add(
|
||||
binding.getImplementationType()
|
||||
);
|
||||
if (binding.getPackageName() != packageName) continue;
|
||||
|
||||
result.add(binding);
|
||||
}
|
||||
return result;
|
||||
return new fflib_AppBindings(result);
|
||||
}
|
||||
|
||||
public List<Object> getObjects()
|
||||
|
||||
+4
-1
@@ -75,6 +75,7 @@ public virtual without sharing class fflib_AppBindingsSelector extends fflib_SOb
|
||||
|
||||
return (List<fflib_AppBinding__mdt>) Database.query(
|
||||
newQueryFactory()
|
||||
.selectField('ApplicationPackage__r.DeveloperName')
|
||||
.selectField('BindingObject__r.QualifiedApiName')
|
||||
.toSOQL());
|
||||
*/
|
||||
@@ -84,7 +85,9 @@ public virtual without sharing class fflib_AppBindingsSelector extends fflib_SOb
|
||||
'SELECT {0} FROM {1}',
|
||||
new List<String>
|
||||
{
|
||||
getFieldListString() + ', BindingObject__r.QualifiedApiName',
|
||||
getFieldListString()
|
||||
+ ', BindingObject__r.QualifiedApiName'
|
||||
+ ', ApplicationPackage__r.DeveloperName',
|
||||
getSObjectType().getDescribe().getName()
|
||||
}
|
||||
)
|
||||
|
||||
+2
@@ -30,6 +30,8 @@
|
||||
*/
|
||||
public abstract class fflib_DynamicAppFactory
|
||||
{
|
||||
protected String packageName;
|
||||
|
||||
protected fflib_SharingMode sharingMode
|
||||
{
|
||||
get
|
||||
|
||||
+50
-36
@@ -33,9 +33,8 @@ public virtual class fflib_DynamicDomainFactory
|
||||
extends fflib_DynamicAppFactory
|
||||
implements fflib_IDomainFactory
|
||||
{
|
||||
protected Map<Object, fflib_IDomain> mockDomainByObjectType = new Map<Object, fflib_IDomain>();
|
||||
protected fflib_ISelectorFactory selectorFactory;
|
||||
|
||||
protected fflib_ClassicDomainFactory domainFactory;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
@@ -44,9 +43,25 @@ public virtual class fflib_DynamicDomainFactory
|
||||
* @param selectorFactory A reference to the selector factory instance
|
||||
*/
|
||||
public fflib_DynamicDomainFactory(fflib_IAppBindingResolver resolver, fflib_ISelectorFactory selectorFactory)
|
||||
{
|
||||
this(resolver, selectorFactory, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param resolver A reference to the resolve instance
|
||||
* @param selectorFactory A reference to the selector factory instance
|
||||
* @param packageName The package name
|
||||
*/
|
||||
public fflib_DynamicDomainFactory(
|
||||
fflib_IAppBindingResolver resolver,
|
||||
fflib_ISelectorFactory selectorFactory,
|
||||
String packageName)
|
||||
{
|
||||
this.resolver = resolver;
|
||||
this.selectorFactory = selectorFactory;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public fflib_IDomain newInstance(Set<Id> recordIds)
|
||||
@@ -61,42 +76,16 @@ public virtual class fflib_DynamicDomainFactory
|
||||
|
||||
public fflib_IDomain newInstance(List<SObject> records)
|
||||
{
|
||||
SObjectType sObjectType = records.getSObjectType();
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
if (sObjectType == null)
|
||||
throw new DeveloperException('Could not determine the SObjectType of the provided records');
|
||||
|
||||
return newInstance(
|
||||
(List<Object>) records,
|
||||
(Object) sObjectType
|
||||
);
|
||||
return this.domainFactory.newInstance(records);
|
||||
}
|
||||
|
||||
public fflib_IDomain newInstance(List<Object> objects, Object objectType)
|
||||
{
|
||||
if (hasMock(objectType)) return (fflib_IDomain) getMock(objectType);
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
List<fflib_IAppBinding> bindings = resolver.byObject(objectType)
|
||||
.byType(fflib_AppBindingType.Domain)
|
||||
.resolve();
|
||||
|
||||
if (bindings.isEmpty())
|
||||
throw new DeveloperException('Unable to find domain binding for ' + objectType);
|
||||
|
||||
System.Type domainImplementationType = bindings.get(0).getImplementationType();
|
||||
|
||||
Object instance = domainImplementationType.newInstance();
|
||||
|
||||
if (!(instance instanceof fflib_IDomainConstructor))
|
||||
throw new DeveloperException(
|
||||
String.format(
|
||||
'Domain implementation {0} should be an instance of fflib_IDomainConstructor',
|
||||
new List<String> { domainImplementationType.getName() }
|
||||
)
|
||||
);
|
||||
|
||||
return ((fflib_IDomainConstructor) domainImplementationType.newInstance())
|
||||
.construct(objects);
|
||||
return this.domainFactory.newInstance(objects, objectType);
|
||||
}
|
||||
|
||||
public fflib_IDomain newInstance(List<SObject> records, SObjectType domainSObjectType)
|
||||
@@ -106,22 +95,47 @@ public virtual class fflib_DynamicDomainFactory
|
||||
|
||||
public void setMock(fflib_ISObjectDomain mockDomain)
|
||||
{
|
||||
super.setMock(mockDomain.getType(), mockDomain);
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
this.domainFactory.setMock(mockDomain);
|
||||
}
|
||||
|
||||
public void setMock(Schema.SObjectType sObjectType, fflib_ISObjectDomain mockDomain)
|
||||
{
|
||||
super.setMock(sObjectType, mockDomain);
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
this.domainFactory.setMock(sObjectType, mockDomain);
|
||||
}
|
||||
|
||||
public void setMock(fflib_IDomain mockDomain)
|
||||
{
|
||||
super.setMock(mockDomain.getType(), mockDomain);
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
this.domainFactory.setMock(mockDomain);
|
||||
}
|
||||
|
||||
public void setMock(Object domainType, fflib_IDomain mockDomain)
|
||||
{
|
||||
super.setMock(domainType, mockDomain);
|
||||
if (domainFactory == null) loadBindings();
|
||||
|
||||
this.domainFactory.setMock(domainType, mockDomain);
|
||||
}
|
||||
|
||||
private void loadBindings()
|
||||
{
|
||||
fflib_IAppBindings appBindings = new fflib_AppBindings(resolver.resolve());
|
||||
|
||||
// If a package name is provide we should filter on package name
|
||||
if (String.isNotBlank(this.packageName))
|
||||
{
|
||||
appBindings = appBindings.selectByPackageName(this.packageName);
|
||||
}
|
||||
|
||||
Map<Object, Type> constructorByObject =
|
||||
appBindings.selectByType(fflib_AppBindingType.Domain)
|
||||
.getImplementationByBindingObject();
|
||||
|
||||
this.domainFactory = new fflib_ClassicDomainFactory(this.selectorFactory, constructorByObject);
|
||||
}
|
||||
|
||||
public class DeveloperException extends Exception {}
|
||||
|
||||
+36
-51
@@ -32,86 +32,66 @@ public virtual class fflib_DynamicSelectorFactory
|
||||
extends fflib_DynamicAppFactory
|
||||
implements fflib_ISelectorFactory
|
||||
{
|
||||
public static final fflib_AppBindingType SELECTOR_BINDING_TYPE = fflib_AppBindingType.Selector;
|
||||
|
||||
protected Map<Object, Object> mockSelectorImplByObjectType = new Map<Object, Object>();
|
||||
protected fflib_ClassicSelectorFactory selectorFactory;
|
||||
|
||||
public fflib_DynamicSelectorFactory(fflib_IAppBindingResolver resolver)
|
||||
{
|
||||
this(resolver, null);
|
||||
}
|
||||
|
||||
public fflib_DynamicSelectorFactory(fflib_IAppBindingResolver resolver, String packageName)
|
||||
{
|
||||
this.resolver = resolver;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public fflib_ISObjectSelector newInstance(SObjectType sObjectType)
|
||||
{
|
||||
if (hasMock(sObjectType)) return (fflib_ISObjectSelector) getMock(sObjectType);
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
List<fflib_IAppBinding> bindings = resolver
|
||||
.byType(SELECTOR_BINDING_TYPE)
|
||||
.byObject(sObjectType)
|
||||
.bySharingMode(sharingMode)
|
||||
.resolve();
|
||||
|
||||
if (bindings.isEmpty())
|
||||
throw new DeveloperException(
|
||||
String.format(
|
||||
'Unable to find selector binding for {0} with sharing mode {1}',
|
||||
new List<String>
|
||||
{
|
||||
sObjectType.getDescribe().getName(),
|
||||
sharingMode.name()
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return (fflib_ISObjectSelector) bindings.get(0).newImplInstance();
|
||||
return this.selectorFactory.newInstance(sObjectType);
|
||||
}
|
||||
|
||||
public List<SObject> selectById(Set<Id> recordIds)
|
||||
{
|
||||
if (null == recordIds || recordIds.size() == 0)
|
||||
throw new fflib_Application.DeveloperException('Invalid record Id\'s set');
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
return selectById(recordIds, new List<Id>(recordIds)[0].getSObjectType());
|
||||
return this.selectorFactory.selectById(recordIds);
|
||||
}
|
||||
|
||||
public List<SObject> selectById(Set<Id> recordIds, SObjectType sObjectType)
|
||||
{
|
||||
if (null == recordIds || recordIds.size() == 0)
|
||||
throw new fflib_Application.DeveloperException('Invalid record Id\'s set');
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
return newInstance(sObjectType)
|
||||
.selectSObjectsById(recordIds);
|
||||
return this.selectorFactory.selectById(recordIds, sObjectType);
|
||||
}
|
||||
|
||||
public List<SObject> selectByRelationship(List<SObject> relatedRecords, SObjectField relationshipField)
|
||||
{
|
||||
Set<Id> relatedIds =
|
||||
new RelatedRecordsDomain(relatedRecords)
|
||||
.getRelatedIds(relationshipField);
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
if (relatedIds.isEmpty()) return new List<SObject>();
|
||||
|
||||
return selectById(relatedIds);
|
||||
return this.selectorFactory.selectByRelationship(relatedRecords, relationshipField);
|
||||
}
|
||||
|
||||
public void replaceWith(SObjectType sObjectType, Type replacementImplType)
|
||||
{
|
||||
resolver.byType(fflib_AppBindingType.Selector)
|
||||
.byObject(sObjectType)
|
||||
.bySharingMode(this.sharingMode)
|
||||
.resolve()
|
||||
.get(0)
|
||||
.setImplementationType(replacementImplType);
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
this.selectorFactory.replaceWith(sObjectType, replacementImplType);
|
||||
}
|
||||
|
||||
public void setMock(fflib_ISObjectSelector selectorInstance)
|
||||
{
|
||||
setMock(selectorInstance.sObjectType(), selectorInstance);
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
this.selectorFactory.setMock(selectorInstance);
|
||||
}
|
||||
|
||||
public void setMock(SObjectType sObjectType, fflib_ISObjectSelector selectorInstance)
|
||||
{
|
||||
super.setMock(sObjectType, selectorInstance);
|
||||
if (this.selectorFactory == null) loadBindings();
|
||||
|
||||
this.selectorFactory.setMock(sObjectType, selectorInstance);
|
||||
}
|
||||
|
||||
public fflib_DynamicSelectorFactory setSharingMode(fflib_SharingMode sharingMode)
|
||||
@@ -120,17 +100,22 @@ public virtual class fflib_DynamicSelectorFactory
|
||||
return this;
|
||||
}
|
||||
|
||||
private class RelatedRecordsDomain extends fflib_SObjects2
|
||||
private void loadBindings()
|
||||
{
|
||||
public RelatedRecordsDomain(List<SObject> records)
|
||||
fflib_IAppBindings appBindings = new fflib_AppBindings(resolver.resolve());
|
||||
|
||||
// If a package name is provide we should filter on package name
|
||||
if (String.isNotBlank(this.packageName))
|
||||
{
|
||||
super(records);
|
||||
appBindings = appBindings.selectByPackageName(this.packageName);
|
||||
}
|
||||
|
||||
public Set<Id> getRelatedIds(Schema.SObjectField relationshipField)
|
||||
{
|
||||
return getIdFieldValues(relationshipField);
|
||||
}
|
||||
Map<SObjectType, Type> implementationBySObjectType =
|
||||
appBindings.selectByType(fflib_AppBindingType.Selector)
|
||||
.selectBySharingMode(this.sharingMode)
|
||||
.getImplementationBySObjectType();
|
||||
|
||||
this.selectorFactory = new fflib_ClassicSelectorFactory(implementationBySObjectType);
|
||||
}
|
||||
|
||||
public class DeveloperException extends Exception {}
|
||||
|
||||
+32
-30
@@ -32,53 +32,38 @@ public virtual class fflib_DynamicServiceFactory
|
||||
extends fflib_DynamicAppFactory
|
||||
implements fflib_IServiceFactory
|
||||
{
|
||||
public static final fflib_AppBindingType SERVICE_BINDING_TYPE = fflib_AppBindingType.Service;
|
||||
|
||||
protected Map<System.Type, Object> mockServiceImplByObjectType = new Map<System.Type, Object>();
|
||||
protected fflib_ClassicServiceFactory serviceFactory;
|
||||
|
||||
public fflib_DynamicServiceFactory(fflib_IAppBindingResolver resolver)
|
||||
{
|
||||
this(resolver, null);
|
||||
}
|
||||
|
||||
public fflib_DynamicServiceFactory(fflib_IAppBindingResolver resolver, String packageName)
|
||||
{
|
||||
this.resolver = resolver;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public Object newInstance(Type serviceInterfaceType)
|
||||
{
|
||||
if (hasMock(serviceInterfaceType)) return getMock(serviceInterfaceType);
|
||||
if (serviceFactory == null) loadBindings();
|
||||
|
||||
List<fflib_IAppBinding> bindings = resolver
|
||||
.byType(SERVICE_BINDING_TYPE)
|
||||
.byInterfaceType(serviceInterfaceType)
|
||||
.bySharingMode(sharingMode)
|
||||
.resolve();
|
||||
|
||||
if (bindings.isEmpty())
|
||||
throw new DeveloperException(
|
||||
String.format(
|
||||
'Unable to find service binding for {0} with sharing more {1}',
|
||||
new List<String>
|
||||
{
|
||||
serviceInterfaceType.getName(),
|
||||
sharingMode.name()
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return bindings.get(0).newImplInstance();
|
||||
return this.serviceFactory.newInstance(serviceInterfaceType);
|
||||
}
|
||||
|
||||
public void replaceWith(Type serviceInterfaceType, Type replacementImplType)
|
||||
{
|
||||
resolver.byType(SERVICE_BINDING_TYPE)
|
||||
.byInterfaceType(serviceInterfaceType)
|
||||
.bySharingMode(sharingMode)
|
||||
.resolve()
|
||||
.get(0) // TODO - validate that there are any resolved bindings
|
||||
.setImplementationType(replacementImplType);
|
||||
if (serviceFactory == null) loadBindings();
|
||||
|
||||
this.serviceFactory.replaceWith(serviceInterfaceType, replacementImplType);
|
||||
}
|
||||
|
||||
public void setMock(Type serviceInterfaceType, Object serviceImpl)
|
||||
{
|
||||
super.setMock(serviceInterfaceType, serviceImpl);
|
||||
if (serviceFactory == null) loadBindings();
|
||||
|
||||
this.serviceFactory.setMock(serviceInterfaceType, serviceImpl);
|
||||
}
|
||||
|
||||
public fflib_DynamicServiceFactory setSharingMode(fflib_SharingMode sharingMode)
|
||||
@@ -87,5 +72,22 @@ public virtual class fflib_DynamicServiceFactory
|
||||
return this;
|
||||
}
|
||||
|
||||
private void loadBindings()
|
||||
{
|
||||
fflib_IAppBindings appBindings = new fflib_AppBindings(resolver.resolve());
|
||||
|
||||
// If a package name is provide we should filter on package name
|
||||
if (String.isNotBlank(this.packageName))
|
||||
{
|
||||
appBindings = appBindings.selectByPackageName(this.packageName);
|
||||
}
|
||||
|
||||
Map<Type, Type> implementationByInterface =
|
||||
appBindings.selectByType(fflib_AppBindingType.Service)
|
||||
.getImplementationByInterface();
|
||||
|
||||
this.serviceFactory = new fflib_ClassicServiceFactory(implementationByInterface);
|
||||
}
|
||||
|
||||
public class DeveloperException extends Exception {}
|
||||
}
|
||||
+11
@@ -56,6 +56,10 @@ public interface fflib_IAppBinding
|
||||
* @return Returns the Namespace for the binding
|
||||
*/
|
||||
String getNamespacePrefix();
|
||||
/**
|
||||
* @return Returns the Application Package Name for the binding
|
||||
*/
|
||||
String getPackageName();
|
||||
|
||||
/**
|
||||
* @return Returns the number of the order sequence in which the binding should be returned
|
||||
@@ -127,6 +131,13 @@ public interface fflib_IAppBinding
|
||||
*/
|
||||
fflib_IAppBinding setNamespacePrefix(String namespacePrefix);
|
||||
|
||||
/**
|
||||
* @param applicationPackageName The name of the Application Second Generation Package
|
||||
*
|
||||
* @return An instance of itself to enable method chaining
|
||||
*/
|
||||
fflib_IAppBinding setPackageName(String applicationPackageName);
|
||||
|
||||
/**
|
||||
* Set the order number so that the resolver can sort the bindings.
|
||||
* The higher the number, the more priority.
|
||||
|
||||
+1
@@ -37,5 +37,6 @@ public interface fflib_IAppBindingResolver
|
||||
fflib_IAppBindingResolver byInterfaceType(System.Type interfaceType);
|
||||
fflib_IAppBindingResolver bySharingMode(fflib_SharingMode sharingMode);
|
||||
fflib_IAppBindingResolver byNamespacePrefix(String namespacePrefix);
|
||||
fflib_IAppBindingResolver byPackageName(String packageName);
|
||||
List<fflib_IAppBinding> resolve();
|
||||
}
|
||||
+7
-3
@@ -34,13 +34,17 @@ public interface fflib_IAppBindings
|
||||
void addBinding(fflib_IAppBinding binding);
|
||||
void addBindings(List<fflib_IAppBinding> bindings);
|
||||
|
||||
List<fflib_IAppBinding> getBindings();
|
||||
Map<Object, Type> getImplementationByBindingObject();
|
||||
Map<Type, Type> getImplementationByInterface();
|
||||
Map<SObjectType, Type> getImplementationBySObjectType();
|
||||
List<System.Type> getImplementations();
|
||||
|
||||
fflib_IAppBindings selectByObject(Object bindingObject);
|
||||
fflib_IAppBindings selectBySequence(Decimal sequence);
|
||||
fflib_IAppBindings selectByType(fflib_AppBindingType bindingType);
|
||||
fflib_IAppBindings selectByInterfaceType(System.Type interfaceType);
|
||||
fflib_IAppBindings selectBySharingMode(fflib_SharingMode sharingMode);
|
||||
fflib_IAppBindings selectByNamespacePrefix(String namespacePrefix);
|
||||
|
||||
List<fflib_IAppBinding> getBindings();
|
||||
List<System.Type> getImplementations();
|
||||
fflib_IAppBindings selectByPackageName(String packageName);
|
||||
}
|
||||
+4
@@ -18,6 +18,10 @@
|
||||
<behavior>Required</behavior>
|
||||
<field>Type__c</field>
|
||||
</layoutItems>
|
||||
<layoutItems>
|
||||
<behavior>Edit</behavior>
|
||||
<field>ApplicationPackage__c</field>
|
||||
</layoutItems>
|
||||
</layoutColumns>
|
||||
<layoutColumns>
|
||||
<layoutItems>
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Layout xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<layoutSections>
|
||||
<customLabel>false</customLabel>
|
||||
<detailHeading>false</detailHeading>
|
||||
<editHeading>true</editHeading>
|
||||
<label>Information</label>
|
||||
<layoutColumns>
|
||||
<layoutItems>
|
||||
<behavior>Required</behavior>
|
||||
<field>MasterLabel</field>
|
||||
</layoutItems>
|
||||
<layoutItems>
|
||||
<behavior>Required</behavior>
|
||||
<field>DeveloperName</field>
|
||||
</layoutItems>
|
||||
</layoutColumns>
|
||||
<layoutColumns>
|
||||
<layoutItems>
|
||||
<behavior>Edit</behavior>
|
||||
<field>IsProtected</field>
|
||||
</layoutItems>
|
||||
<layoutItems>
|
||||
<behavior>Required</behavior>
|
||||
<field>NamespacePrefix</field>
|
||||
</layoutItems>
|
||||
</layoutColumns>
|
||||
<style>TwoColumnsTopToBottom</style>
|
||||
</layoutSections>
|
||||
<layoutSections>
|
||||
<customLabel>false</customLabel>
|
||||
<detailHeading>false</detailHeading>
|
||||
<editHeading>true</editHeading>
|
||||
<label>System Information</label>
|
||||
<layoutColumns>
|
||||
<layoutItems>
|
||||
<behavior>Readonly</behavior>
|
||||
<field>CreatedById</field>
|
||||
</layoutItems>
|
||||
</layoutColumns>
|
||||
<layoutColumns>
|
||||
<layoutItems>
|
||||
<behavior>Readonly</behavior>
|
||||
<field>LastModifiedById</field>
|
||||
</layoutItems>
|
||||
</layoutColumns>
|
||||
<style>TwoColumnsTopToBottom</style>
|
||||
</layoutSections>
|
||||
<layoutSections>
|
||||
<customLabel>false</customLabel>
|
||||
<detailHeading>false</detailHeading>
|
||||
<editHeading>false</editHeading>
|
||||
<layoutColumns/>
|
||||
<style>CustomLinks</style>
|
||||
</layoutSections>
|
||||
<relatedLists>
|
||||
<fields>MasterLabel</fields>
|
||||
<fields>DeveloperName</fields>
|
||||
<fields>NamespacePrefix</fields>
|
||||
<relatedList>fflib_AppBinding__mdt.ApplicationPackage__c</relatedList>
|
||||
</relatedLists>
|
||||
<showEmailCheckbox>false</showEmailCheckbox>
|
||||
<showHighlightsPanel>false</showHighlightsPanel>
|
||||
<showInteractionLogPanel>false</showInteractionLogPanel>
|
||||
<showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
|
||||
<showSubmitAndAttachButton>false</showSubmitAndAttachButton>
|
||||
</Layout>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<fullName>ApplicationPackage__c</fullName>
|
||||
<externalId>false</externalId>
|
||||
<fieldManageability>DeveloperControlled</fieldManageability>
|
||||
<label>Application Package</label>
|
||||
<referenceTo>fflib_ApplicationPackage__mdt</referenceTo>
|
||||
<relationshipLabel>Application Bindings</relationshipLabel>
|
||||
<relationshipName>ApplicationBindings</relationshipName>
|
||||
<required>false</required>
|
||||
<type>MetadataRelationship</type>
|
||||
<unique>false</unique>
|
||||
</CustomField>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<description>Define individual packages if your application contains multiple second generation packages (2GP)</description>
|
||||
<label>Application Package</label>
|
||||
<pluralLabel>Application Packages</pluralLabel>
|
||||
<visibility>Public</visibility>
|
||||
</CustomObject>
|
||||
@@ -111,6 +111,5 @@ public with sharing class fflib_MockSObjectUtil
|
||||
Lead record = new Lead(Id = fflib_IDGenerator.generate(Schema.Lead.SObjectType));
|
||||
SObject result = addFieldValue(record, Lead.ConvertedDate, today);
|
||||
System.assertEquals(today, result.get(Lead.ConvertedDate));
|
||||
System.debug('result: '+result);
|
||||
}
|
||||
}
|
||||
+53
-3
@@ -31,15 +31,24 @@
|
||||
@IsTest(IsParallel=true)
|
||||
public class fflib_DynamicDomainFactoryTest
|
||||
{
|
||||
private static final SObjectType ACCOUNT_OBJECT_TYPE = Schema.Account.SObjectType;
|
||||
private static final String PACKAGE_FORCE_APP = 'force-app';
|
||||
private static final String PACKAGE_OTHER_APP = 'other-app';
|
||||
|
||||
private static final fflib_IAppBindingResolver bindingResolver =
|
||||
new fflib_AppBindingResolver();
|
||||
|
||||
|
||||
private static final fflib_DynamicDomainFactory Domain =
|
||||
new fflib_DynamicDomainFactory(
|
||||
bindingResolver,
|
||||
fflib_DynamicSelectorFactoryTest.Selector);
|
||||
private static final SObjectType ACCOUNT_OBJECT_TYPE = Schema.Account.SObjectType;
|
||||
fflib_DynamicSelectorFactoryTest.Selector,
|
||||
PACKAGE_FORCE_APP);
|
||||
|
||||
private static final fflib_DynamicDomainFactory OtherDomain =
|
||||
new fflib_DynamicDomainFactory(
|
||||
bindingResolver,
|
||||
fflib_DynamicSelectorFactoryTest.Selector,
|
||||
PACKAGE_OTHER_APP);
|
||||
|
||||
@IsTest
|
||||
static void itShouldReturnDomain()
|
||||
@@ -107,6 +116,19 @@ public class fflib_DynamicDomainFactoryTest
|
||||
System.Assert.isTrue(instanceB.getRecords().isEmpty());
|
||||
}
|
||||
|
||||
@IsTest
|
||||
static void itShouldReturnTheRightDomainForThePackage()
|
||||
{
|
||||
fflib_AppBindingsSelector.SELECTOR_IMPL_TYPE = AppBindingsSelectorMock.class;
|
||||
|
||||
final List<Account> records = generateAccountRecords();
|
||||
|
||||
System.Test.startTest();
|
||||
fflib_IDomain otherDomain = OtherDomain.newInstance(records);
|
||||
System.Test.stopTest();
|
||||
|
||||
System.Assert.isTrue(otherDomain instanceof fflib_SObjects2, 'Incorrect returned instance');
|
||||
}
|
||||
|
||||
private static List<Account> generateAccountRecords()
|
||||
{
|
||||
@@ -128,18 +150,46 @@ public class fflib_DynamicDomainFactoryTest
|
||||
}
|
||||
}
|
||||
|
||||
public class OtherDomainConstructor implements fflib_IDomainConstructor
|
||||
{
|
||||
public fflib_IDomain construct(List<Object> objects)
|
||||
{
|
||||
return new fflib_SObjects2((List<SObject>) objects);
|
||||
}
|
||||
}
|
||||
|
||||
private class AppBindingsSelectorMock extends fflib_AppBindingsSelector
|
||||
{
|
||||
public override List<fflib_AppBinding__mdt> selectAll()
|
||||
{
|
||||
fflib_ApplicationPackage__mdt forceApp =
|
||||
new fflib_ApplicationPackage__mdt(
|
||||
Id = fflib_IDGenerator.generate(fflib_ApplicationPackage__mdt.SObjectType),
|
||||
DeveloperName = PACKAGE_FORCE_APP);
|
||||
fflib_ApplicationPackage__mdt otherApp =
|
||||
new fflib_ApplicationPackage__mdt(
|
||||
Id = fflib_IDGenerator.generate(fflib_ApplicationPackage__mdt.SObjectType),
|
||||
DeveloperName = PACKAGE_OTHER_APP);
|
||||
|
||||
return new List<fflib_AppBinding__mdt>
|
||||
{
|
||||
new fflib_AppBinding__mdt(
|
||||
ApplicationPackage__c = forceApp.Id,
|
||||
ApplicationPackage__r = forceApp,
|
||||
Type__c = 'Domain',
|
||||
BindingObject__c = '',
|
||||
BindingObjectAlternate__c = ACCOUNT_OBJECT_TYPE.getDescribe().getName(),
|
||||
ImplementationType__c = MyDomainConstructor.class.getName(),
|
||||
SharingMode__c = 'None'
|
||||
),
|
||||
new fflib_AppBinding__mdt(
|
||||
ApplicationPackage__c = otherApp.Id,
|
||||
ApplicationPackage__r = otherApp,
|
||||
Type__c = 'Domain',
|
||||
BindingObject__c = '',
|
||||
BindingObjectAlternate__c = ACCOUNT_OBJECT_TYPE.getDescribe().getName(),
|
||||
ImplementationType__c = OtherDomainConstructor.class.getName(),
|
||||
SharingMode__c = 'None'
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ private class fflib_SObjects2Test
|
||||
}
|
||||
);
|
||||
System.Test.stopTest();
|
||||
System.debug('result: ' + JSON.serializePretty(result));
|
||||
|
||||
// THEN it should return the AccountId By CaseId
|
||||
System.Assert.areEqual(1, result.size(), 'Expected one account');
|
||||
@@ -194,7 +193,6 @@ private class fflib_SObjects2Test
|
||||
}
|
||||
);
|
||||
System.Test.stopTest();
|
||||
System.debug('result: ' + JSON.serializePretty(result));
|
||||
|
||||
// THEN it should return the AccountId By CaseId
|
||||
System.Assert.areEqual(1, result.size(), 'Expected one account');
|
||||
|
||||
@@ -91,7 +91,6 @@ private class fflib_LogEventAppenderTest
|
||||
fflib_LogEventAppender eventAppender = new fflib_LogEventAppender();
|
||||
|
||||
fflib_LogLevelFilter__c orgSettings = fflib_LogLevelFilter__c.getOrgDefaults();
|
||||
System.debug(orgSettings);
|
||||
orgSettings.LogLevel__c = 'INFO';
|
||||
insert orgSettings;
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ private class fflib_LoggerTest
|
||||
// THEN - It should be appended to the logAppender
|
||||
final List<String> result = APPENDER_MOCK
|
||||
.getAppendedMessages(System.LoggingLevel.DEBUG);
|
||||
System.debug(result);
|
||||
System.Assert.areEqual(1, result.size(), 'Expected only one error');
|
||||
System.Assert.isTrue(result.get(0).startsWith(ERROR_MESSAGE_FORMATTED_WITH_EXCEPTION), 'Incorrect returned error message');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user