Created
February 29, 2016 12:27
-
-
Save iolaru/05e115096f6e440e5698 to your computer and use it in GitHub Desktop.
Code example provided by user for sending emails with PDF attachments from Salesforce.com using Sendgrid API v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
global class SendGrid { | |
private static final String ENCODING = 'UTF-8'; | |
private static final String API_URL = 'https://api.sendgrid.com/api/mail.send.json'; | |
private transient String username; | |
private transient String password; | |
private transient String apikey; | |
private enum AUTHTYPE {KEY, USER_PASS} | |
private AUTHTYPE selAuthType; | |
private List<string> tos = new List<String>(); | |
public SendGrid(String username, String password) { | |
this.username = username; | |
this.password = password; | |
this.apikey = null; | |
selAuthType = AUTHTYPE.USER_PASS; | |
} | |
public SendGrid(String apiKey) { | |
this.apikey = apikey; | |
this.apikey = '12345766'; | |
selAuthType = AUTHTYPE.KEY; | |
} | |
public String credentialsToWebFormat() { | |
String output = ''; | |
if(selAuthType == AUTHTYPE.USER_PASS) { | |
output += 'api_user=' + this.username; | |
output += '&api_key=' + this.password; | |
} | |
return output; | |
} | |
public void sendTrigger(SendGrid.Email email) { | |
verifyAuthentication(); | |
String body = this.credentialsToWebFormat() + email.toWebFormat(); | |
Map<String, String> sgHeaders = new Map<String, String>{ | |
'Content-Type' => 'application/x-www-form-urlencoded', | |
'Content-Length' => String.valueof(body.length()) | |
}; | |
//HTTPCalloutHandler.HTTPCalloutFuture(API_URL, 'POST', body, sgHeaders, apiKey); | |
} | |
public SendGridResponse send(SendGrid.Email email) { | |
verifyAuthentication(); | |
String messageBoundry = generateMessageBoundry(); | |
String body = email.toMultipartFormat(messageBoundry); | |
Blob bodyBlob = EncodingUtil.base64Decode(body); | |
Map<String, String> sgHeaders = new Map<String, String>{ | |
'Content-Type' => 'multipart/form-data;boundary="'+messageBoundry+'"', | |
'Content-Length' => String.valueof(body.length()) | |
}; | |
//return null; | |
HTTPResult res = HTTPCalloutHandler.HTTPCallout(API_URL, 'POST', bodyBlob, sgHeaders, apiKey); | |
if(res.success) { | |
return new SendGridResponse(res.statusCode, res.body); | |
} else { | |
throw new SendGridException(res.status); | |
} | |
/*HttpRequest req = new HttpRequest(); | |
Http http = new Http(); | |
req.setEndpoint(API_URL); | |
req.setMethod('POST'); | |
req.setHeader('Content-Type',''); | |
req.setBody(body); | |
req.setHeader('Content-Length',String.valueof(body.length())); | |
try { | |
HttpResponse res = http.send(req); | |
return new SendGridResponse(res.getStatusCode(), res.getBody()); | |
} catch (Exception e) { | |
throw new SendGridException(e); | |
}*/ | |
} | |
private String generateMessageBoundry() { | |
String hashString = '1000' + String.valueOf(Datetime.now().formatGMT('yyyy-MM-dd HH:mm:ss.SSS')); | |
Blob hash = Crypto.generateDigest('MD5', Blob.valueOf(hashString)); | |
String hexString = EncodingUtil.convertToHex(hash); | |
return '----------------------------'+hexString; | |
} | |
global class Email { | |
public Smtpapi.Header smtpapi; | |
public String fromm; | |
public String fromname; | |
public String replyto; | |
public List<String> bcc = new List<String>(); | |
public String subject; | |
public String text; | |
public String html; | |
public Map<String, String> headers = new Map<String, String>(); | |
public Map<String, String> toEmailName = new Map<String, String>(); | |
public Map<String, Blob> files = new Map<String, Blob>(); | |
public Email() { | |
this.smtpapi = new Smtpapi.Header(); | |
} | |
public Email addTo(String toEmail) { | |
return addTo(toEmail, toEmail); | |
} | |
public Email addTo(String toEmail, String toName) { | |
this.smtpapi.addTo(toEmail); | |
this.toEmailName.put(toEmail, toName); | |
return this; | |
} | |
public Email setTos(List<String> tos) { | |
this.smtpapi.setTos(tos); | |
return this; | |
} | |
public Email setFrom(String email) { | |
this.fromm = email; | |
return this; | |
} | |
public Email setFromName(String name) { | |
this.fromname = name; | |
return this; | |
} | |
public Email setReplyTo(String email) { | |
this.replyto = email; | |
return this; | |
} | |
public Email addBcc(String email) { | |
this.bcc.add(email); | |
return this; | |
} | |
public Email setSubject(String subject) { | |
this.subject = subject; | |
return this; | |
} | |
public Email setText(String text) { | |
this.text = text; | |
return this; | |
} | |
public Email setHtml(String html) { | |
this.html = html; | |
return this; | |
} | |
public Email addSubstitution(String key, List<String> val) { | |
this.smtpapi.addSubstitution(key, val); | |
return this; | |
} | |
public Email addUniqueArg(String key, String val) { | |
this.smtpapi.addUniqueArg(key, val); | |
return this; | |
} | |
public Email addCategory(String category) { | |
this.smtpapi.addCategory(category); | |
return this; | |
} | |
public Email addSection(String key, String val) { | |
this.smtpapi.addSection(key, val); | |
return this; | |
} | |
public Email addFilter(String filter_name, String parameter_name, String parameter_value) { | |
this.smtpapi.addFilter(filter_name, parameter_name, parameter_value); | |
return this; | |
} | |
public Email addHeader(String key, String val) { | |
this.headers.put(key, val); | |
return this; | |
} | |
public Email addAttachmentStream(String filename, String content) { | |
this.files.put(filename, Blob.valueOf(content)); | |
return this; | |
} | |
public Email addAttachmentStream(String filename, Blob content) { | |
this.files.put(filename, content); | |
return this; | |
} | |
public String toWebFormat() { | |
String output = ''; | |
// updateMissingTo - There needs to be at least 1 to address, | |
// or else the mail won't send. | |
if (!this.smtpapi.to.isEmpty() && !String.isBlank(this.fromm)) { | |
for(String t :this.smtpapi.to) { | |
String encoded = EncodingUtil.urlEncode(t, ENCODING); | |
output += '&to[]=' + encoded; | |
} | |
} | |
if (!String.isBlank(this.fromm)) { | |
String encoded = EncodingUtil.urlEncode(this.fromm, ENCODING); | |
output += '&from=' + encoded; | |
} | |
if (!String.isBlank(this.fromname)) { | |
String encoded = EncodingUtil.urlEncode(this.fromname, ENCODING); | |
output += '&fromname=' + encoded; | |
} | |
if (!String.isBlank(this.replyto)) { | |
String encoded = EncodingUtil.urlEncode(this.replyto, ENCODING); | |
output += '&replyto=' + encoded; | |
} | |
if (!this.bcc.isEmpty()) { | |
for (String bcc_email : this.bcc) { | |
String encoded = EncodingUtil.urlEncode(bcc_email, ENCODING); | |
output += '&bcc[]=' + encoded; | |
} | |
} | |
if (!String.isBlank(this.subject)) { | |
String encoded = EncodingUtil.urlEncode(this.subject, ENCODING); | |
output += '&subject=' + encoded; | |
} | |
if (!String.isBlank(this.text)) { | |
String encoded = EncodingUtil.urlEncode(this.text, ENCODING); | |
output += '&text=' + encoded; | |
} | |
if (!String.isBlank(this.html)) { | |
String encoded = EncodingUtil.urlEncode(this.html, ENCODING); | |
output += '&html=' + encoded; | |
} | |
if (this.smtpapi.jsonString() != '{}') { | |
String encoded = EncodingUtil.urlEncode(this.smtpapi.jsonString(), ENCODING); | |
output += '&x-smtpapi=' + encoded; | |
} | |
if (!this.headers.isEmpty()) { | |
String serialized_headers = JSON.serialize(this.headers); | |
String encoded = EncodingUtil.urlEncode(serialized_headers, ENCODING); | |
output += '&headers=' + encoded; | |
} | |
/*if (!this.files.isEmpty()) { | |
for (String filename : this.files.keySet()){ | |
String value = this.files.get(filename); | |
String encoded = EncodingUtil.urlEncode(value, ENCODING); | |
output += '&files['+filename+']=' + encoded; | |
} | |
}*/ | |
return output; | |
} | |
//Allows attaching Binary files | |
private String toMultipartFormat(String boundry) { | |
String header = ''; | |
if (!this.smtpapi.to.isEmpty() && !String.isBlank(this.fromm)) { | |
for(String t :this.smtpapi.to) { | |
header += getPartString('to[]',t, boundry); | |
header += getPartString('toname[]',this.toEmailName.get(t), boundry); | |
} | |
} | |
if (!String.isBlank(this.fromm)) { | |
header += getPartString('from',this.fromm, boundry); | |
} | |
if (!String.isBlank(this.fromname)) { | |
header += getPartString('fromname',this.fromname, boundry); | |
} | |
if (!String.isBlank(this.replyto)) { | |
header += getPartString('replyto',this.replyto, boundry); | |
} | |
if (!this.bcc.isEmpty()) { | |
for (String bcc_email : this.bcc) { | |
header += getPartString('bcc[]',bcc_email, boundry); | |
} | |
} | |
if (!String.isBlank(this.subject)) { | |
header += getPartString('subject',this.subject, boundry); | |
} | |
if (!String.isBlank(this.text)) { | |
header += getPartString('text',this.text, boundry); | |
} | |
if (!String.isBlank(this.html)) { | |
header += getPartString('html',this.html, boundry); | |
} | |
if (this.smtpapi.jsonString() != '{}') { | |
//header += getPartString('x-smtpapi',this.smtpapi.jsonString(), boundry); | |
} | |
if (!this.headers.isEmpty()) { | |
String serialized_headers = JSON.serialize(this.headers); | |
header += getPartString('headers',serialized_headers, boundry); | |
} | |
String footer = '--'+boundry+'--'; | |
String encodedBody = ''; | |
String headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n')); | |
while(headerEncoded.endsWith('=')) { | |
header += ' '; | |
headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n')); | |
} | |
if (!this.files.isEmpty()) { | |
encodedBody = headerEncoded + encodeWithAttachments(footer, boundry, this.files); | |
} else { | |
encodedBody = headerEncoded + EncodingUtil.base64Encode(Blob.valueOf('\r\n' + footer)); | |
} | |
return encodedBody; | |
} | |
private String encodeWithAttachments(String footer, String boundry, Map<String, Blob> atts) { | |
String attachmentsStr = ''; | |
String lastPrepend = ''; | |
for(String fileName :atts.keySet()) { | |
Blob fileBlob = atts.get(fileName); | |
String fHeader = lastPrepend + '--'+boundry+'\r\n'; | |
fHeader += 'Content-Disposition: form-data; name="files['+fileName+']"; filename="'+fileName+'"\r\nContent-Type: application/octet-stream'; | |
String fHeaderEncoded = EncodingUtil.base64Encode(Blob.valueOf(fheader+'\r\n\r\n')); | |
while(fHeaderEncoded.endsWith('=')) { | |
fHeader += ' '; | |
fHeaderEncoded = EncodingUtil.base64Encode(Blob.valueOf(fHeader+'\r\n\r\n')); | |
} | |
String fbodyEncoded = EncodingUtil.base64Encode(fileBlob); | |
String last4Bytes = fbodyEncoded.substring(fbodyEncoded.length()-4,fbodyEncoded.length()); | |
if(last4Bytes.endsWith('==')) { | |
last4Bytes = last4Bytes.substring(0,2) + '0K'; | |
fBodyEncoded = fbodyEncoded.substring(0,fbodyEncoded.length()-4) + last4Bytes; | |
lastPrepend = ''; | |
} else if(last4Bytes.endsWith('=')) { | |
last4Bytes = last4Bytes.substring(0,3) + 'N'; | |
fBodyEncoded = fbodyEncoded.substring(0,fbodyEncoded.length()-4) + last4Bytes; | |
lastPrepend = '\n'; | |
} else { | |
lastPrepend = '\r\n'; | |
} | |
attachmentsStr += fHeaderEncoded + fBodyEncoded; | |
} | |
footer = lastPrepend + footer; | |
return attachmentsStr + EncodingUtil.base64Encode(Blob.valueOf(footer)); | |
} | |
private String getPartString(String valType, String val, String boundry) { | |
String part = ''; | |
part += '--'+boundry+'\r\n'; | |
part += 'Content-Disposition: form-data; name="'+valType+'"\r\n\r\n'+val+'\r\n'; | |
return part; | |
} | |
} | |
private void verifyAuthentication() { | |
if(this.selAuthType == AUTHTYPE.USER_PASS && (this.username == null || this.password == null)) { | |
throw new SendGridException('Username and Password Missing'); | |
} | |
if(this.selAuthType == AUTHTYPE.KEY && this.apikey == null) { | |
throw new SendGridException('API Key is missing'); | |
} | |
} | |
public class SendGridException extends Exception {} | |
public class SendGridResponse { | |
public Integer code { get; private set; } | |
public Boolean success { get; private set; } | |
public String message { get; private set; } | |
public SendGridResponse(Integer code, String body) { | |
this.code = code; | |
this.success = code == 200; | |
this.message = body; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment