diff --git a/.gitignore b/.gitignore
index 0a4980a..2ddff4e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
node_modules/
yarn.lock
.sfdx/
+.sf/
.localdevserver/
debug.log
DEVHUB_SFDX_URL.txt
diff --git a/README.md b/README.md
index 384e4f4..fa479de 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,12 @@
## Deployment
-
+
-
+
diff --git a/core/classes/AbstractCacheRepo.cls-meta.xml b/core/classes/AbstractCacheRepo.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/AbstractCacheRepo.cls-meta.xml
+++ b/core/classes/AbstractCacheRepo.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/core/classes/FlowRoundRobinAssigner.cls b/core/classes/FlowRoundRobinAssigner.cls
index f017caf..90c29b4 100644
--- a/core/classes/FlowRoundRobinAssigner.cls
+++ b/core/classes/FlowRoundRobinAssigner.cls
@@ -26,10 +26,15 @@ global without sharing class FlowRoundRobinAssigner {
@InvocableMethod(category='Round Robin' label='Round robin records')
global static void assign(List flowInputs) {
- for (FlowInput input : flowInputs) {
- if (input.recordsToRoundRobin?.isEmpty() == false || input.recordToRoundRobin != null) {
- SELF.trackAssignedIds(input);
- SELF.roundRobin(input);
+ if (hasBeenUpdated == false) {
+ for (FlowInput input : flowInputs) {
+ if (input.recordsToRoundRobin?.isEmpty() != false && input.recordToRoundRobin != null) {
+ input.recordsToRoundRobin = new List{ input.recordToRoundRobin };
+ }
+ if (input.recordsToRoundRobin.isEmpty() == false) {
+ SELF.trackAssignedIds(input);
+ SELF.roundRobin(input);
+ }
}
}
}
diff --git a/core/classes/FlowRoundRobinAssigner.cls-meta.xml b/core/classes/FlowRoundRobinAssigner.cls-meta.xml
index 40d6793..754ecb1 100644
--- a/core/classes/FlowRoundRobinAssigner.cls-meta.xml
+++ b/core/classes/FlowRoundRobinAssigner.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
diff --git a/core/classes/FlowRoundRobinAssignerTests.cls b/core/classes/FlowRoundRobinAssignerTests.cls
index 6e3691a..3c41b53 100644
--- a/core/classes/FlowRoundRobinAssignerTests.cls
+++ b/core/classes/FlowRoundRobinAssignerTests.cls
@@ -1,6 +1,8 @@
@IsTest
private class FlowRoundRobinAssignerTests {
- static final User FAKE_USER = new User(Id = User.SObjectType.getDescribe().getKeyPrefix() + '0'.repeat(12));
+ static final User FAKE_USER = new User(
+ Id = User.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED).getKeyPrefix() + '0'.repeat(12)
+ );
@IsTest
static void defaultAssignmentToOwnerId() {
diff --git a/core/classes/FlowRoundRobinAssignerTests.cls-meta.xml b/core/classes/FlowRoundRobinAssignerTests.cls-meta.xml
index 40d6793..754ecb1 100644
--- a/core/classes/FlowRoundRobinAssignerTests.cls-meta.xml
+++ b/core/classes/FlowRoundRobinAssignerTests.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
diff --git a/core/classes/IThreadSafeCacheVisitor.cls-meta.xml b/core/classes/IThreadSafeCacheVisitor.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/IThreadSafeCacheVisitor.cls-meta.xml
+++ b/core/classes/IThreadSafeCacheVisitor.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/core/classes/MockUserAssignmentRepo.cls-meta.xml b/core/classes/MockUserAssignmentRepo.cls-meta.xml
index 40d6793..754ecb1 100644
--- a/core/classes/MockUserAssignmentRepo.cls-meta.xml
+++ b/core/classes/MockUserAssignmentRepo.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
diff --git a/core/classes/QueryAssigner.cls-meta.xml b/core/classes/QueryAssigner.cls-meta.xml
index 40d6793..754ecb1 100644
--- a/core/classes/QueryAssigner.cls-meta.xml
+++ b/core/classes/QueryAssigner.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
diff --git a/core/classes/RoundRobinAssigner.cls-meta.xml b/core/classes/RoundRobinAssigner.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/RoundRobinAssigner.cls-meta.xml
+++ b/core/classes/RoundRobinAssigner.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/core/classes/RoundRobinAssignerTests.cls b/core/classes/RoundRobinAssignerTests.cls
index e4b719e..dc21aeb 100644
--- a/core/classes/RoundRobinAssignerTests.cls
+++ b/core/classes/RoundRobinAssignerTests.cls
@@ -63,7 +63,7 @@ private class RoundRobinAssignerTests {
static List createUsersForAssignment(String assignmentType) {
List users = new List();
- String baseIdString = Schema.User.SObjectType.getDescribe().getKeyPrefix() + '0'.repeat(11);
+ String baseIdString = Schema.User.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED).getKeyPrefix() + '0'.repeat(11);
for (Integer index = 0; index < 2; index++) {
users.add(new User(Department = assignmentType, Id = baseIdString + (index + 1)));
}
diff --git a/core/classes/RoundRobinAssignerTests.cls-meta.xml b/core/classes/RoundRobinAssignerTests.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/RoundRobinAssignerTests.cls-meta.xml
+++ b/core/classes/RoundRobinAssignerTests.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/core/classes/RoundRobinRepository.cls b/core/classes/RoundRobinRepository.cls
index 81b5680..75d54e2 100644
--- a/core/classes/RoundRobinRepository.cls
+++ b/core/classes/RoundRobinRepository.cls
@@ -1,6 +1,7 @@
public without sharing class RoundRobinRepository extends AbstractCacheRepo {
private static Map CACHED_ASSIGNMENTS;
+ @SuppressWarnings('PMD.ApexCRUDViolation')
public void accept(IThreadSafeCacheVisitor visitor, List records) {
RoundRobin__c currentAssignment = this.getCurrentAssignment(visitor.getVisitKey());
visitor.visitRecords(records, currentAssignment);
@@ -46,56 +47,39 @@ public without sharing class RoundRobinRepository extends AbstractCacheRepo {
@SuppressWarnings('PMD.ApexCRUDViolation')
private Boolean commitUpdatedAssignment(RoundRobin__c assignment) {
+ Boolean wasCommitSuccessful = true;
Map currentCache = this.getCachedAssignments();
if (
currentCache.containsKey(assignment.Name) &&
currentCache.get(assignment.Name).LastUpdated__c > CACHED_ASSIGNMENTS.get(assignment.Name).LastUpdated__c
) {
- return false;
- }
- assignment.LastUpdated__c = System.now();
- /**
- * integration tests with after save Flows have shown something unfortunate:
- * though the second (recursive) call to the assigner is spawned in a second transaction
- * the RoundRobin__c.getAll() call still doesn't contain the Id of the inserted record (for the times where the assignment
- * is being run for the first time).
- * That means that we can't just call "upsert", and instead have to do this goofy
- * song and dance to ensure the Id is appended correctly
- */
- if (assignment.Id == null) {
- List existingAssignments = [SELECT Id FROM RoundRobin__c WHERE Name = :assignment.Name];
- if (existingAssignments.isEmpty() == false) {
- assignment.Id = existingAssignments[0].Id;
- }
- }
- if (assignment.Id != null) {
- try {
- /**
- * if two separate threads are trying to round robin at the same time, the LastUpdated__c check above
- * isn't enough to ensure write-safety, and unfortunately FOR UPDATE is the only mutex Apex offers
- * as a write-safe guarantee. One downside (among many) is that FOR UPDATE frequently throws; another is
- * that another locking thread can release early - let's protect against both those eventualities
- */
- RoundRobin__c lockedAssignment = [SELECT Id, Name, LastUpdated__c FROM RoundRobin__c WHERE Id = :assignment.Id FOR UPDATE];
- if (lockedAssignment.LastUpdated__c >= assignment.LastUpdated__c) {
- // lock was released early, but the existing Index__c now almost certainly has stale values in it
- // re-round robin to get the now-correct values
- return false;
+ assignment = currentCache.get(assignment.Name);
+ wasCommitSuccessful = false;
+ } else {
+ assignment.LastUpdated__c = System.now();
+ /**
+ * integration tests with after save Flows have shown something unfortunate:
+ * though the second (recursive) call to the assigner is spawned in a second transaction
+ * the RoundRobin__c.getAll() still doesn't contain the Id of the inserted record (for the times where the assignment
+ * is being run for the first time).
+ * That means that we can't just call "upsert", and instead have to do this goofy
+ * song and dance to ensure the Id is appended correctly
+ */
+ if (assignment.Id == null) {
+ List existingAssignments = [SELECT Id FROM RoundRobin__c WHERE Name = :assignment.Name];
+ if (existingAssignments.isEmpty() == false) {
+ assignment.Id = existingAssignments[0].Id;
}
- lockedAssignment.Index__c = assignment.Index__c;
- lockedAssignment.LastUpdated__c = assignment.LastUpdated__c;
- update lockedAssignment;
- // purely for the map assignment, below
- assignment = lockedAssignment;
- } catch (DmlException ex) {
- return false;
}
- } else {
- insert assignment;
+ if (assignment.Id != null) {
+ update assignment;
+ } else {
+ insert assignment;
+ }
}
CACHED_ASSIGNMENTS.put(assignment.Name, assignment);
- return true;
+ return wasCommitSuccessful;
}
private Map getCachedAssignments() {
@@ -108,8 +92,7 @@ public without sharing class RoundRobinRepository extends AbstractCacheRepo {
new RoundRobin__c(
Name = assignmentType,
// some sentinel value
- LastUpdated__c = Datetime.newInstanceGmt(1970, 1, 1),
- Index__c = null
+ LastUpdated__c = Datetime.newInstanceGmt(1970, 1, 1)
)
);
}
diff --git a/core/classes/RoundRobinRepository.cls-meta.xml b/core/classes/RoundRobinRepository.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/RoundRobinRepository.cls-meta.xml
+++ b/core/classes/RoundRobinRepository.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/core/classes/RoundRobinRepositoryTests.cls-meta.xml b/core/classes/RoundRobinRepositoryTests.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/core/classes/RoundRobinRepositoryTests.cls-meta.xml
+++ b/core/classes/RoundRobinRepositoryTests.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/integration-tests/classes/RoundRobinFlowIntegrationTests.cls-meta.xml b/integration-tests/classes/RoundRobinFlowIntegrationTests.cls-meta.xml
index e7009f9..45cccbd 100644
--- a/integration-tests/classes/RoundRobinFlowIntegrationTests.cls-meta.xml
+++ b/integration-tests/classes/RoundRobinFlowIntegrationTests.cls-meta.xml
@@ -1,5 +1,5 @@
- 54.0
+ 57.0Active
\ No newline at end of file
diff --git a/integration-tests/flows/Round_Robin_Assign_Leads.flow-meta.xml b/integration-tests/flows/Round_Robin_Assign_Leads.flow-meta.xml
index 623d8e0..73bf624 100644
--- a/integration-tests/flows/Round_Robin_Assign_Leads.flow-meta.xml
+++ b/integration-tests/flows/Round_Robin_Assign_Leads.flow-meta.xml
@@ -35,7 +35,7 @@
- 54.0
+ 57.0Add_Current_Record_to_Collection
diff --git a/package.json b/package.json
index 763f66a..e7182bc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salesforce-round-robin",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "Round robin records in Salesforce (SFDC) using Flow or Apex. Performant, fair, fast assignment with configurable user pools",
"repository": {
"type": "git",
diff --git a/sfdx-project.json b/sfdx-project.json
index 1556026..0195c0f 100644
--- a/sfdx-project.json
+++ b/sfdx-project.json
@@ -5,8 +5,8 @@
"definitionFile": "config/project-scratch-def.json",
"package": "salesforce-round-robin",
"path": "core",
- "versionName": "Guaranteed write-safety for dueling transactions",
- "versionNumber": "0.1.0.0",
+ "versionName": "Reverts mutex due to bulkification issues with Flow, adds more safety for recursive Flow transactions",
+ "versionNumber": "0.1.1.0",
"versionDescription": "Invocable & Apex-ready Round Robin Assigner for Salesforce Flow, Process Builder, Apex and more",
"releaseNotesUrl": "https://github.com/jamessimone/salesforce-round-robin/releases/latest"
},
@@ -16,13 +16,11 @@
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
- "sourceApiVersion": "54.0",
+ "sourceApiVersion": "57.0",
"packageAliases": {
"salesforce-round-robin": "0Ho6g000000GnClCAK",
- "salesforce-round-robin@0.0.1-0": "04t6g000008SjNvAAK",
- "salesforce-round-robin@0.0.2-0": "04t6g000008SjOFAA0",
- "salesforce-round-robin@0.0.3-0": "04t6g000008SjREAA0",
"salesforce-round-robin@0.0.4-0": "04t6g000008SjZEAA0",
- "salesforce-round-robin@0.1.0-0": "04t6g000008SjpyAAC"
+ "salesforce-round-robin@0.1.0-0": "04t6g000008SjpyAAC",
+ "salesforce-round-robin@0.1.1-0": "04t6g000008fjhFAAQ"
}
}
\ No newline at end of file