Skip to content

Instantly share code, notes, and snippets.

@AlainODea
Created December 18, 2020 05:29
Show Gist options
  • Save AlainODea/cbacf691863ba861bb382c8c87e49c02 to your computer and use it in GitHub Desktop.
Save AlainODea/cbacf691863ba861bb382c8c87e49c02 to your computer and use it in GitHub Desktop.
Salesforce Flow InvocableMethod plugin for sending Lightning Email Templates

Salesforce Flow InvocableMethod plugin for sending Lightning Email Templates

This is an example implementation of supporting sending Lightning Email Templates from a Flow using @InvocableMethod.

Notes

Example__c won't necessarily exist or have matching fields. It is in here to inspire what you might do.

If you want to try this unmodified, you'll need to create a custom Object with an API name of Example. It will need to have fields with API names Email__c (Lookup(Contact)) and ExampleEmail__c (String or Email).

The unit tests here are egregious. They don't test anything other than "does it blow to pieces when invoked." This warrants more significant, targeted tests with proper assertions.

Caveat Emptor

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

public with sharing class SendEmailTemplate {
@InvocableMethod(
Label='Send Email Template'
Description='Send an email to a set of recipients using a Lightning Email Template'
Category='Email'
// A Lightning Web Component can be used to provide a custom Flow editor experience
// ConfigurationEditor='c:SendEmailTemplateLwc'
)
public static void sendEmails(List<Request> requests) {
for (Request request : requests) {
sendEmails(request.emailTemplateId, request.orgWideEmailAddressId, request.relatedRecords);
}
}
public class Request {
@InvocableVariable(
Label='Email Template ID'
Description='The ID of the Email Template to use.'
Required=true
)
public Id emailTemplateId;
@InvocableVariable(
Label='Org-Wide Address ID'
Description='The ID of the organization-wide email address associated with the outgoing email.'
Required=true
)
public Id orgWideEmailAddressId;
@InvocableVariable(
Label='Related Records'
Description='Records that are of the Email Template\'s related entity type'
Required=true
)
public List<SObject> relatedRecords;
}
public class Response {
@InvocableVariable(
Label='Successful Records'
Description='Records that were successfully sent the Email Template'
Required=true
)
public List<SObject> successes;
@InvocableVariable(
Label='Failed Records'
Description='Records that failed to be sent the Email Template'
Required=true
)
public List<SObject> failures;
}
private static void sendEmails(Id emailTemplateId, Id orgWideEmailAddressId, List<SObject> relatedRecords) {
for (SObject relatedRecord : relatedRecords) {
sendEmail(emailTemplateId, orgWideEmailAddressId, relatedRecord);
}
}
private static void sendEmail(Id emailTemplateId, Id orgWideEmailAddressId, SObject relatedRecord) {
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setTargetObjectId(getTargetObjectId(relatedRecord));
if (getClassName(relatedRecord).endsWith('__c')) {
message.setWhatId(relatedRecord.Id);
}
message.setTemplateId(emailTemplateId);
message.setOrgWideEmailAddressId(orgWideEmailAddressId);
message.setOptOutPolicy('FILTER');
message.setUseSignature(false);
message.setBccSender(false);
message.setSaveAsActivity(isSaveAsActivity(relatedRecord));
message.setToAddresses(new List<String>{
getToAddress(relatedRecord)
});
Messaging.SingleEmailMessage[] messages = new List<Messaging.SingleEmailMessage>{
message
};
Messaging.SendEmailResult sendEmailResult = Messaging.sendEmail(messages)[0];
if (!sendEmailResult.isSuccess()) {
System.debug(String.format(
'ERROR: SendLightningEmail: failed to send templateId={0} orgWideEmailAddressId={1} relatedRecord.Id={2} error="{3}',
new List<Object> {emailTemplateId, orgWideEmailAddressId, relatedRecord.Id, sendEmailResult.errors[0].message}
));
}
}
private static Id getTargetObjectId(SObject relatedRecord) {
// There is *definitely* an Enterprise programming pattern to make this pluggable
if (relatedRecord instanceof Example__c) {
return ((Example__c) relatedRecord).Email__c;
} else {
return relatedRecord.Id;
}
}
private static String getToAddress(SObject relatedRecord) {
// There is *definitely* an Enterprise programming pattern to make this pluggable
if (relatedRecord instanceof Example__c) {
return ((Example__c) relatedRecord).ExampleEmail__c;
} else if (relatedRecord instanceof Contact) {
return ((Contact) relatedRecord).Email;
} else if (relatedRecord instanceof User) {
return ((User) relatedRecord).Email;
} else if (relatedRecord instanceof Lead) {
return ((Lead) relatedRecord).Email;
} else {
throw new IllegalArgumentException('Unsupported related record of type: ' + getClassName(relatedRecord));
}
}
private static String getClassName(Object theObject) {
return String.valueOf(theObject).split(':')[0];
}
private static Boolean isSaveAsActivity(Object theObject) {
return !getClassName(theObject).equals('User');
}
}
@IsTest
private class SendEmailTemplateTest {
@IsTest
static void testNewContactValidEmail() {
Contact contact = newContact();
insert contact;
assertDoesntCrash(contact);
}
@IsTest
static void testNewUserValidEmail() {
User user = [SELECT Id, Email FROM User LIMIT 1];
assertDoesntCrash(user);
}
@IsTest
static void testNewLeadValidEmail() {
Lead lead = newLead();
lead.Company = 'Acme Corp';
insert lead;
assertDoesntCrash(lead);
}
@IsTest
static void testNewExampleValidEmail() {
Contact contact = newContact();
insert contact;
Example__c example = newExample(contact);
insert example;
assertDoesntCrash(example);
}
static void assertDoesntCrash(SObject relatedRecord) {
String templateDeveloperName = SendEmailTemplateTest.class.getName();
User thisUser = [ SELECT Id FROM User WHERE Id = :UserInfo.getUserId() ];
System.runAs ( thisUser ) {
EmailTemplate emailTemplate = new EmailTemplate();
emailTemplate.Name = 'Hello World';
emailTemplate.DeveloperName = templateDeveloperName;
emailTemplate.Body = 'Hello, World!';
emailTemplate.TemplateType = 'custom';
emailTemplate.FolderId = UserInfo.getUserId();
emailTemplate.IsActive = true;
insert emailTemplate;
}
EmailTemplate emailTemplate = [
SELECT
Id
FROM
EmailTemplate
WHERE
DeveloperName = :templateDeveloperName
LIMIT 1
];
OrgWideEmailAddress orgWideEmailAddress = [
SELECT
Id
FROM
OrgWideEmailAddress
LIMIT 1
];
SendEmailTemplate.Request request = new SendEmailTemplate.Request();
request.emailTemplateId = emailTemplate.Id;
request.orgWideEmailAddressId = orgWideEmailAddress.Id;
request.relatedRecords = new List<SObject>{
relatedRecord
};
List<SendEmailTemplate.Request> requests = new List<SendEmailTemplate.Request>{
request
};
Test.startTest();
SendEmailTemplate.sendEmails(requests);
Test.stopTest();
}
private static Example__c newExample(Contact contact) {
Example__c example = new Example__c();
example.ExampleEmail__c = 'another.one-' + 0 + '@example.com';
example.Example__c = contact.Id;
return example;
}
private static Contact newContact() {
Id contactContactRecordType = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('contact').getRecordTypeId();
Contact contact = new Contact();
contact.Email = '[email protected]';
contact.LastName = 'Example';
contact.RecordTypeId = contactContactRecordType;
return contact;
}
private static Lead newLead() {
Lead lead = new Lead();
lead.Email = '[email protected]';
lead.LastName = 'Example';
return lead;
}
private static String getClassName(SObject theObject) {
return String.valueOf(theObject).split(':')[0];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment