Unsubscribe Apex Email Service

Apex email services. The short story here is that you can send email to your Salesforce Org which is then processed by some Apex logic in order to support the specific needs of your business.

One of our clients uses an Apex email service to automate Case creation for customer support issues. We have another client that has an email service for parsing CSV attachments in order to update their Org exchange rates. Just two ideas for how real-world Salesforce customers use Apex email services for automation.

Anyway, maybe you've seen that Apex code sample that uses an Apex email service for handling email opt outs? Here's a reimagined take on that logic.

/*
	Created by: Greg Hacic
    Last Update: 25 October 2019 by Greg Hacic
	Questions?: greg@interactiveties.com
	
	Notes:
        - InboundEmailHandler interface to set hasOptedOutOfEmail to TRUE for Contact and Lead records
        - tests located at unsubscribeTest.cls
*/
public class unsubscribe implements Messaging.InboundEmailHandler {
    
    //access an InboundEmail object to retrieve the contents, headers and attachments of inbound emails
    public Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,  Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailResult(); //InboundEmailResult object for returning the result of the Apex Email Service
        if (String.isNotBlank(email.subject) && email.subject.containsIgnoreCase('unsubscribe')) { //if the subject is not null, blank or empaty '' AND the subject contains the string 'unsubscribe' (regardless of case)
            if (!System.isFuture() && !System.isBatch()) { //if not currently in future or batch context
                handleUnsubscribeFuture(envelope.fromAddress); //process the email address asynchronously
            } else { //otherwise
                handleUnsubscribe(envelope.fromAddress); //process the email address synchronously
            }
        }
        result.success = true; //set the InboundEmailResult.Success boolean to true
        return result; //return the result
    }
    
    //updates the hasOptedOutOfEmail value to false for a passed email address and object name
    public static void hasOptedOutOfEmail(String emailAddress, String objectName) {
        List<SObject> updatedRecords = new List<SObject>(); //list of SObjects
        String soql = 'SELECT Id FROM '+objectName+' WHERE Email = \''+emailAddress+'\' AND hasOptedOutOfEmail = FALSE'; //construct the query statement
        List<SObject> objectList = Database.query(soql); //execute the query > populate the list
        Schema.SObjectType t = Schema.getGlobalDescribe().get(objectName); //grab the sObject token for the object name passed to the method
        for (SObject r : objectList) { //for each object in the list
            SObject o = t.newSObject(r.Id); //construct a new sObject of this type, with the specified Id
            o.put('hasOptedOutOfEmail', TRUE); //set the hasOptedOutOfEmail value to TRUE
            updatedRecords.add(o); //add the SObject to the list
        }
        if (!updatedRecords.isEmpty()) { //if the list is not empty
            List<Database.SaveResult> updateResults = Database.update(updatedRecords, false); //update SObject records allow for partial fail
        }
    }
    
    //execute the handleUnsubscribe method asynchronously
    public static void handleUnsubscribe(String s) {
        hasOptedOutOfEmail(s, 'Lead'); //handle Lead Opt Outs
        hasOptedOutOfEmail(s, 'Contact'); //handle Contact Opt Outs
    }
    
    //execute the handleUnsubscribe method asynchronously
    @future //future annotation indicates asynchronous execution
    public static void handleUnsubscribeFuture(String s) {
        handleUnsubscribe(s); //pass the string to the handleUnsubscribe method
    }

}

Removing the tests from the Apex class itself is a best practice. That test coverage is done in the class below:

/*
	Created by: Greg Hacic
    Last Update: 25 October 2019 by Greg Hacic
	Questions?: greg@interactiveties.com
	
	Notes:
        - tests unsubscribe.cls (95.83% coverage)
*/
@isTest
private class unsubscribeTest {
    
    //tests unsubscribe
	@isTest //defines method for use during testing only
	static void validSubject() {
        //BEGIN: Some Setup Items... 
        //insert an account
        List<Account> accounts = new List<Account>();
        accounts.add(new Account(BillingCity = 'Denver', BillingPostalCode = '80202', BillingStreet = '201 W Colfax Ave, First Floor', BillingState = 'CO', BillingCountry = 'US', Name = 'Tess Trucking Co.', ShippingCity = 'Denver', ShippingPostalCode = '80202', ShippingStreet = '201 W Colfax Ave, First Floor', ShippingState = 'CO', ShippingCountry = 'US'));
        accounts.add(new Account(BillingCity = 'Buffalo', BillingPostalCode = '14202', BillingStreet = '65 Niagara Square', BillingState = 'NY', BillingCountry = 'US', Name = 'Tess Financial Corp', ShippingCity = 'Buffalo', ShippingPostalCode = '14202', ShippingStreet = '65 Niagara Square', ShippingState = 'NY', ShippingCountry = 'US'));
        accounts.add(new Account(BillingCity = 'San Francisco', BillingPostalCode = '94110', BillingStreet = '3180 18th St', BillingState = 'CA', BillingCountry = 'US', Name = 'Tess Health LLC'));
        insert accounts;
        //insert Contacts
        List<Contact> contacts = new List<Contact>();
        contacts.add(new Contact(AccountId = accounts[0].Id, Email = 'tess@example.com', FirstName = 'Tess', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[0].Id, Email = 'fake.name@example.com', FirstName = 'Grachus', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[1].Id, Email = 'fake.name@example.com', FirstName = 'Priscus', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[1].Id, Email = 'spartacus@example.com', FirstName = 'Spartacus', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[2].Id, Email = 'crixus@example.com', FirstName = 'Crixus', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[2].Id, Email = 'fake.name@example.com', FirstName = 'Oenomaus', LastName = 'Dachshund'));
        contacts.add(new Contact(AccountId = accounts[2].Id, Email = 'gannicus@example.com', FirstName = 'Gannicus', LastName = 'Dachshund'));
        insert contacts;
        //insert Leads
        List<Lead> leads = new List<Lead>();
        for (Integer i = 0; i < 100; i++) {
            leads.add(new Lead(Company = 'Example Corp', Email = 'fake.name@example.com', FirstName = 'Fake', LastName = 'Person'));
        }
        insert leads;
        //END: Some Setup Items... 
        
        Test.startTest(); //denote testing context
        
        //email
        Messaging.InboundEmail email = new Messaging.InboundEmail(); //construct a new inbound email object
        email.fromAddress = 'fake.name@example.com';
        email.fromName = 'Fake Name';
        email.htmlBody = '<p>I would really like to be removed from your lists.</p>';
        email.plainTextBody = 'I would really like to be removed from your lists.';
        email.subject = 'Remove Me From Your List Unsubscribe';
        //envelope
        Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope(); //construct a new inbound envelope object that stores the envelope information associated with the inbound email
        envelope.fromAddress = 'fake.name@example.com';
        
        unsubscribe handler = new unsubscribe();
        handler.handleInboundEmail(email, envelope);
        
        Test.stopTest(); //revert from testing context
        
        //validate the logic
        for (Lead l : [SELECT Email, hasOptedOutOfEmail, Id FROM Lead]) { //for all leads
            System.assertEquals(true, l.hasOptedOutOfEmail); //hasOptedOutOfEmail = true
        }
        for (Contact c : [SELECT Email, hasOptedOutOfEmail, Id FROM Contact]) { //for all contacts
            if (c.Email == 'fake.name@example.com') {
                System.assertEquals(true, c.hasOptedOutOfEmail); //hasOptedOutOfEmail = true
            } else {
                System.assertEquals(false, c.hasOptedOutOfEmail); //hasOptedOutOfEmail = false
            }
        }
    }
    
    //tests unsubscribe
	@isTest //defines method for use during testing only
	static void invalidSubject() {
        //BEGIN: Some Setup Items... 
        //insert an account
        List<Account> accounts = new List<Account>();
        accounts.add(new Account(BillingCity = 'Denver', BillingPostalCode = '80202', BillingStreet = '201 W Colfax Ave', BillingState = 'CO', BillingCountry = 'US', Name = 'Tess Corp'));
        insert accounts;
        //insert Contacts
        List<Contact> contacts = new List<Contact>();
        for (Integer i = 0; i < 100; i++) {
            contacts.add(new Contact(AccountId = accounts[0].Id, Email = 'fake.name@example.com', FirstName = 'Tess', LastName = 'Dachshund'+i));
        }
        insert contacts;
        //insert Leads
        List<Lead> leads = new List<Lead>();
        for (Integer i = 0; i < 100; i++) {
            leads.add(new Lead(Company = 'Example Corp', Email = 'fake.name@example.com', FirstName = 'Fake', LastName = 'Person'));
        }
        insert leads;
        //END: Some Setup Items... 
        
        Test.startTest(); //denote testing context
        
        //email
        Messaging.InboundEmail email = new Messaging.InboundEmail(); //construct a new inbound email object
        email.fromAddress = 'fake.name@example.com';
        email.fromName = 'Fake Name';
        email.htmlBody = '<p>I would really like to be removed from your lists.</p>';
        email.plainTextBody = 'I would really like to be removed from your lists.';
        email.subject = 'Remove Me From Your List';
        //envelope
        Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope(); //construct a new inbound envelope object that stores the envelope information associated with the inbound email
        envelope.fromAddress = 'fake.name@example.com';
        
        unsubscribe handler = new unsubscribe();
        handler.handleInboundEmail(email, envelope);
        
        Test.stopTest(); //revert from testing context
        
        //validate the logic
        for (Lead l : [SELECT Email, hasOptedOutOfEmail, Id FROM Lead]) { //for all leads
            System.assertEquals(false, l.hasOptedOutOfEmail); //hasOptedOutOfEmail = false
        }
        for (Contact c : [SELECT Email, hasOptedOutOfEmail, Id FROM Contact]) { //for all contacts
            System.assertEquals(false, c.hasOptedOutOfEmail); //hasOptedOutOfEmail = false
        }
    }

}

Automated Exchange Rates in Salesforce.com

Reduce Repetitive Tasks, Eliminate Errors & Free Up Your Administrators.

Birthday Reminders for Salesforce.com

It might lead to a sale. Or it might make you feel good.