375 lines
12 KiB
OpenEdge ABL
375 lines
12 KiB
OpenEdge ABL
public inherited sharing class CreateCaseAutoResponseRule {
|
|
private static final String SENDER_NAME = 'Columbus McKinnon Support';
|
|
private static final String SENDER_EMAIL = 'cmcoautoreply@columbusmckinnon.com';
|
|
private static final Integer MAX_POLLS = 20;
|
|
|
|
public class Request {
|
|
@InvocableVariable(label='Auto Response Rule Id')
|
|
public Id autoResponseRuleId;
|
|
|
|
@InvocableVariable(label='Auto Response Rule Name')
|
|
public String autoResponseRuleName;
|
|
|
|
@InvocableVariable(label='Sort Order')
|
|
public Integer sortOrder;
|
|
|
|
@InvocableVariable(required=true label='Case Origin')
|
|
public String caseOrigin;
|
|
|
|
@InvocableVariable(required=true label='Email Template Id')
|
|
public Id emailTemplateId;
|
|
}
|
|
|
|
public class Result {
|
|
@InvocableVariable(label='Async Apex Job Id')
|
|
public Id asyncJobId;
|
|
|
|
@InvocableVariable(label='Status')
|
|
public String status;
|
|
|
|
@InvocableVariable(label='Metadata Full Name')
|
|
public String metadataFullName;
|
|
}
|
|
|
|
public class Configuration {
|
|
public Id autoResponseRuleId;
|
|
public String autoResponseRuleName;
|
|
public Integer sortOrder;
|
|
public String caseOrigin;
|
|
public String templateFullName;
|
|
}
|
|
|
|
@InvocableMethod(
|
|
label='Create Case Auto-Response Rule Entry'
|
|
description='Queues a file-based Metadata API deployment that appends an entry to a Case auto-response rule.'
|
|
category='Queue Configuration'
|
|
callout=true
|
|
)
|
|
public static List<Result> createRuleEntry(List<Request> requests) {
|
|
MetadataXml.require(
|
|
requests != null && requests.size() == 1,
|
|
'Create Case Auto-Response Rule Entry accepts exactly one request per invocation.'
|
|
);
|
|
Request input = requests[0];
|
|
input.autoResponseRuleName = input.autoResponseRuleName == null
|
|
? null
|
|
: input.autoResponseRuleName.trim();
|
|
input.caseOrigin = input.caseOrigin == null
|
|
? null
|
|
: input.caseOrigin.trim();
|
|
MetadataXml.require(
|
|
input.autoResponseRuleId != null ||
|
|
!String.isBlank(input.autoResponseRuleName),
|
|
'Auto Response Rule Id or Auto Response Rule Name is required.'
|
|
);
|
|
MetadataXml.require(
|
|
!String.isBlank(input.caseOrigin),
|
|
'Case Origin is required.'
|
|
);
|
|
MetadataXml.require(
|
|
input.emailTemplateId != null,
|
|
'Email Template Id is required.'
|
|
);
|
|
MetadataXml.require(
|
|
input.sortOrder == null || input.sortOrder > 0,
|
|
'Sort Order must be greater than zero.'
|
|
);
|
|
|
|
List<EmailTemplate> templates = [
|
|
SELECT Id, DeveloperName, FolderId
|
|
FROM EmailTemplate
|
|
WHERE Id = :input.emailTemplateId
|
|
WITH USER_MODE
|
|
LIMIT 1
|
|
];
|
|
MetadataXml.require(
|
|
!templates.isEmpty(),
|
|
'Email Template Id was not found.'
|
|
);
|
|
List<Folder> folders = [
|
|
SELECT Id, DeveloperName
|
|
FROM Folder
|
|
WHERE Id = :templates[0].FolderId AND Type = 'Email'
|
|
WITH USER_MODE
|
|
LIMIT 1
|
|
];
|
|
MetadataXml.require(
|
|
!folders.isEmpty(),
|
|
'The email template must be stored in a shared Classic Email Template folder.'
|
|
);
|
|
|
|
Configuration configuration = new Configuration();
|
|
configuration.autoResponseRuleId = input.autoResponseRuleId;
|
|
configuration.autoResponseRuleName = String.isBlank(
|
|
input.autoResponseRuleName
|
|
)
|
|
? MetadataApiClient.toolingAutoResponseRuleName(input.autoResponseRuleId)
|
|
: input.autoResponseRuleName;
|
|
configuration.sortOrder = input.sortOrder;
|
|
configuration.caseOrigin = input.caseOrigin;
|
|
configuration.templateFullName =
|
|
folders[0].DeveloperName +
|
|
'/' +
|
|
templates[0].DeveloperName;
|
|
|
|
Id jobId = System.enqueueJob(new StartRetrieveJob(configuration));
|
|
Result result = new Result();
|
|
result.asyncJobId = jobId;
|
|
result.status = 'Queued';
|
|
result.metadataFullName = 'Case.' + configuration.autoResponseRuleName;
|
|
return new List<Result>{ result };
|
|
}
|
|
|
|
private class StartRetrieveJob implements Queueable, Database.AllowsCallouts {
|
|
private Configuration configuration;
|
|
|
|
private StartRetrieveJob(Configuration configuration) {
|
|
this.configuration = configuration;
|
|
}
|
|
|
|
public void execute(QueueableContext context) {
|
|
String retrieveId = MetadataApiClient.startRetrieve(
|
|
'Case.' + configuration.autoResponseRuleName,
|
|
'AutoResponseRule'
|
|
);
|
|
System.enqueueJob(new RetrievePollJob(configuration, retrieveId, 0), 1);
|
|
}
|
|
}
|
|
|
|
private class RetrievePollJob implements Queueable, Database.AllowsCallouts {
|
|
private Configuration configuration;
|
|
private String retrieveId;
|
|
private Integer pollCount;
|
|
|
|
private RetrievePollJob(
|
|
Configuration configuration,
|
|
String retrieveId,
|
|
Integer pollCount
|
|
) {
|
|
this.configuration = configuration;
|
|
this.retrieveId = retrieveId;
|
|
this.pollCount = pollCount;
|
|
}
|
|
|
|
public void execute(QueueableContext context) {
|
|
MetadataApiClient.RetrieveResult retrieveResult = MetadataApiClient.checkRetrieve(
|
|
retrieveId
|
|
);
|
|
if (!retrieveResult.done) {
|
|
if (pollCount >= MAX_POLLS) {
|
|
throw new MetadataApiClient.MetadataApiException(
|
|
'Timed out waiting for the auto-response rule metadata retrieve.'
|
|
);
|
|
}
|
|
System.enqueueJob(
|
|
new RetrievePollJob(configuration, retrieveId, pollCount + 1),
|
|
1
|
|
);
|
|
return;
|
|
}
|
|
if (
|
|
retrieveResult.status != 'Succeeded' ||
|
|
retrieveResult.zipFile == null
|
|
) {
|
|
throw new MetadataApiClient.MetadataApiException(
|
|
'Auto-response rule retrieve failed: ' +
|
|
(String.isBlank(retrieveResult.errorMessage)
|
|
? retrieveResult.status
|
|
: retrieveResult.errorMessage)
|
|
);
|
|
}
|
|
|
|
Blob deploymentZip = buildDeploymentZip(
|
|
retrieveResult.zipFile,
|
|
configuration
|
|
);
|
|
String deployId = MetadataApiClient.startDeploy(deploymentZip);
|
|
System.enqueueJob(new DeployPollJob(deployId, 0), 1);
|
|
}
|
|
}
|
|
|
|
private class DeployPollJob implements Queueable, Database.AllowsCallouts {
|
|
private String deployId;
|
|
private Integer pollCount;
|
|
|
|
private DeployPollJob(String deployId, Integer pollCount) {
|
|
this.deployId = deployId;
|
|
this.pollCount = pollCount;
|
|
}
|
|
|
|
public void execute(QueueableContext context) {
|
|
MetadataApiClient.DeployResult deployResult = MetadataApiClient.checkDeploy(
|
|
deployId
|
|
);
|
|
if (!deployResult.done) {
|
|
if (pollCount >= MAX_POLLS) {
|
|
throw new MetadataApiClient.MetadataApiException(
|
|
'Timed out waiting for the auto-response rule metadata deployment.'
|
|
);
|
|
}
|
|
System.enqueueJob(new DeployPollJob(deployId, pollCount + 1), 1);
|
|
return;
|
|
}
|
|
if (!deployResult.success) {
|
|
throw new MetadataApiClient.MetadataApiException(
|
|
'Auto-response rule deployment failed: ' +
|
|
(String.isBlank(deployResult.errorMessage)
|
|
? deployResult.status
|
|
: deployResult.errorMessage)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@TestVisible
|
|
private static Blob buildDeploymentZip(
|
|
Blob retrievedZip,
|
|
Configuration configuration
|
|
) {
|
|
Compression.ZipReader reader = new Compression.ZipReader(retrievedZip);
|
|
Compression.ZipEntry ruleEntry;
|
|
for (String entryName : reader.getEntriesMap().keySet()) {
|
|
if (entryName.endsWith('Case.autoResponseRules')) {
|
|
ruleEntry = reader.getEntriesMap().get(entryName);
|
|
break;
|
|
}
|
|
}
|
|
MetadataXml.require(
|
|
ruleEntry != null,
|
|
'The retrieved Case auto-response rule file is missing.'
|
|
);
|
|
String sourceXml = reader.extract(ruleEntry).toString();
|
|
String updatedXml = appendRuleEntry(sourceXml, configuration);
|
|
|
|
String packageXml =
|
|
'<?xml version="1.0" encoding="UTF-8"?>' +
|
|
'<Package xmlns="http://soap.sforce.com/2006/04/metadata">' +
|
|
'<types><members>Case.' +
|
|
MetadataXml.escape(configuration.autoResponseRuleName) +
|
|
'</members><name>AutoResponseRule</name></types>' +
|
|
'<version>' +
|
|
MetadataApiClient.API_VERSION +
|
|
'</version></Package>';
|
|
Compression.ZipWriter writer = new Compression.ZipWriter();
|
|
writer.addEntry('package.xml', Blob.valueOf(packageXml));
|
|
writer.addEntry(
|
|
'autoResponseRules/Case.autoResponseRules',
|
|
Blob.valueOf(updatedXml)
|
|
);
|
|
return writer.getArchive();
|
|
}
|
|
|
|
@TestVisible
|
|
private static String appendRuleEntry(
|
|
String sourceXml,
|
|
Configuration configuration
|
|
) {
|
|
Dom.Document document = new Dom.Document();
|
|
document.load(sourceXml);
|
|
Dom.XmlNode root = document.getRootElement();
|
|
Dom.XmlNode targetRule;
|
|
for (Dom.XmlNode rule : MetadataXml.children(root, 'autoResponseRule')) {
|
|
if (
|
|
configuration.autoResponseRuleName == MetadataXml.text(rule, 'fullName')
|
|
) {
|
|
targetRule = rule;
|
|
break;
|
|
}
|
|
}
|
|
MetadataXml.require(
|
|
targetRule != null,
|
|
'Auto-response rule "' +
|
|
configuration.autoResponseRuleName +
|
|
'" was not found.'
|
|
);
|
|
|
|
List<Dom.XmlNode> existingEntries = MetadataXml.children(
|
|
targetRule,
|
|
'ruleEntry'
|
|
);
|
|
for (Dom.XmlNode existingEntry : existingEntries) {
|
|
if (
|
|
configuration.caseOrigin ==
|
|
criterionValue(existingEntry, 'Case.Origin') &&
|
|
configuration.templateFullName ==
|
|
MetadataXml.text(existingEntry, 'template')
|
|
) {
|
|
return sourceXml;
|
|
}
|
|
}
|
|
Integer insertionPosition = configuration.sortOrder == null
|
|
? existingEntries.size() + 1
|
|
: Math.min(configuration.sortOrder, existingEntries.size() + 1);
|
|
|
|
String newEntry =
|
|
'<ruleEntry><booleanFilter>1 AND 2 AND 3 AND 4</booleanFilter>' +
|
|
criterion('Case.Origin', 'equals', configuration.caseOrigin) +
|
|
criterion('Case.ContactEmail', 'notEqual', '') +
|
|
criterion('Case.Status', 'equals', 'Active') +
|
|
criterion('Case.RecordTypeId', 'equals', 'Customer Service') +
|
|
'<senderEmail>' +
|
|
MetadataXml.escape(SENDER_EMAIL) +
|
|
'</senderEmail><senderName>' +
|
|
MetadataXml.escape(SENDER_NAME) +
|
|
'</senderName><template>' +
|
|
MetadataXml.escape(configuration.templateFullName) +
|
|
'</template></ruleEntry>';
|
|
|
|
String targetRuleXml = '<autoResponseRule>';
|
|
Integer currentEntryPosition = 0;
|
|
Boolean inserted = false;
|
|
for (Dom.XmlNode child : targetRule.getChildElements()) {
|
|
if (child.getName() == 'ruleEntry') {
|
|
currentEntryPosition++;
|
|
if (!inserted && currentEntryPosition == insertionPosition) {
|
|
targetRuleXml += newEntry;
|
|
inserted = true;
|
|
}
|
|
}
|
|
targetRuleXml += MetadataXml.serialize(child);
|
|
}
|
|
if (!inserted) {
|
|
targetRuleXml += newEntry;
|
|
}
|
|
targetRuleXml += '</autoResponseRule>';
|
|
|
|
String output =
|
|
'<?xml version="1.0" encoding="UTF-8"?>' +
|
|
'<AutoResponseRules xmlns="http://soap.sforce.com/2006/04/metadata">';
|
|
for (Dom.XmlNode rule : root.getChildElements()) {
|
|
output += rule == targetRule
|
|
? targetRuleXml
|
|
: MetadataXml.serialize(rule);
|
|
}
|
|
return output + '</AutoResponseRules>';
|
|
}
|
|
|
|
private static String criterion(
|
|
String field,
|
|
String operation,
|
|
String value
|
|
) {
|
|
return '<criteriaItems><field>' +
|
|
MetadataXml.escape(field) +
|
|
'</field><operation>' +
|
|
MetadataXml.escape(operation) +
|
|
'</operation><value>' +
|
|
MetadataXml.escape(value) +
|
|
'</value></criteriaItems>';
|
|
}
|
|
|
|
private static String criterionValue(Dom.XmlNode ruleEntry, String field) {
|
|
for (
|
|
Dom.XmlNode criterionNode : MetadataXml.children(
|
|
ruleEntry,
|
|
'criteriaItems'
|
|
)
|
|
) {
|
|
if (field == MetadataXml.text(criterionNode, 'field')) {
|
|
return MetadataXml.text(criterionNode, 'value');
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|