The Force.com Messaging.InboundEmailHandler interface is cool. Read more about it here. It allows you to send email to a particular address, then take that email apart and use it’s pieces to interact with any of your objects in any way you can envision using Apex code. The InboundEmail service can create new records, which can fire triggers, or start trigger-ready flows. The InboundEmail service can also send API messages, making for quick points of integration. So why isn’t it more popular? Well, because the content of email messages are notoriously hard to predict.
Think of it this way: The InboundEmail service will always give you a Messaging.InboundEmail object (we will call that “email”) that you can you can always pull the subject into a field using email.subject & email.plainTextBody. But what if you had a template based email, and wanted a field called “Applications”, and you planned to get the names of the applications out of the string email.plainTextBody returned? What if you didn’t know what apps might be listed (and there was no predefined set of possibilities)? All you could do is look at what came right before the list of apps started in the email template, what came right after, and then use
.substringBetween() to get the application names (you might then use
.split() to return a list if the app names are separated by commas or spaces).
But what the string that came before the app names changed based on what day of the week it is? You’d have to write logic to look for several possible beginnings of the app names substring. And what if nothing came after the app names because they were at the end of the email? You’d have to use
.substringAfter(). And what if you didn’t just want app names? What if you wanted the day an event would happen, the contact information of a group listed in the email body, and whether or not the email subject indicated “high priority”? You’d have to write logic into your Apex code for each of those substrings. That’s a lot of custom logic, which you can do. But even after you’re done, what would it take to break it?
If one unexpected character shows up, your Apex code will not know where to begin or end gathering a substring, and will either return something ridiculous or break with null reference error.
You will have to figure out what changed, rewrite the logic for that substring in your class (probably with cascading effect to other substrings), and redeploy.
That’s why people don’t like Inbound Email Services. And that’s what I’m here to change.
The main problem with the model above is that it relies on hard coding values into code, which is a big no-no in terms of any development language best practices. What needs to be introduced is two Custom Settings: one to remove strings that are superfluous, and one to define the beginning and end of each sub string which will be assigned to a sobject field.
First, cast your emailbody (or subject if you want) to a string we are going to work with for the rest of the class (for example:
String message = email.plainTextBody.normalizeSpace();). Run regex on the “message” string to remove non-alphanumeric characters.
For the first Custom Setting, let’s call it “Substrings_To_Remove__c”. Simply call
Substrings_To_Remove__c.getAll().Values() to loop through a list of substrings where, if found, that substring will be removed from the subject or emailbody string. If the email’s sender add some characters that you don’t need to capture or characters that are breaking your code, just add a record on the Substrings_To_Remove__c custom setting. Then you’ll be working with a clean string to begin extracting values.
After that, we need a Custom Setting to populate the sObject fields. We’ll call it
Fields_to_fill__c. Name a
Fields_to_fill__c record for each of the sObject fields we will want to populate. Create a text field for Begining__c and End__c, and a checkbox if the substring are looking for is at the start of “message” or end of “message”. Place comma seperated lists in the Begining__c and End__c fields if the value may change (based on something like day or sender). Run a Schema describe on the objects we are interested in to get a list of the field names. Then iterate through the list of fields to see if you created a record of that name exists in
Fields_to_fill__c. If so, see if the
Fields_to_fill__c record checkboxes indicate us of
.substringBefore(), or .substringAfter(). Use
Fields_to_fill__c.getvalues(fieldResult.getName()).Start__c.split(',') (or End__c) to iterate through comma separated field values and see which is true for
message.contains(). You now have the necessary data to extract a substring from “message” assign that substring value to the sObject field. You can even use the substring value in Dynamic SOQL to populate a lookup field. Once the correct value is identified, assign it to the sObject & upsert (remember to bulkify). For example, for Account a with field Apps__c where fieldresult.getName() = Apps__c:
a.put(fieldResult.getName(),message.substringBetween(start,the_end).trim() (.trim() will remove proceeding or trailing whitespace).
And there you go! If your email sender changes the template, you can either pluck unwanted characters out of the “message”, or change what you are looking for in order to identify your substring values WITHOUT a redeployment!