Friday, April 29, 2016

How to use SendGrid templates from Ruby on Rails

I recently had to convert a Rails application that I work with from Mandrill to SendGrid.  The application was fully functional and live in production.  All of the messages sent by the system were using templates.

The steps that I took included:
1)      Create a new account at SendGrid
2)      Go to Templates -> Transactional and Create Template
3)      I then cut and pasted each template from Mandrill to SendGrid
a.       Mandrill was using *|variable_name|* syntax for replacement variables and I was able to make this work with SendGrid as well.
4)      The one quirk about SendGrid that is worth mentioning is that I had to add a <%body%> tag to each message even though I don’t have a payload for <%body%>. I found that the easiest way to deal with that was to delete it out, switch to code mode, paste in my complete html and then paste the <%body%> tag as the last thing in my template.  SendGrid then did some minor formatting but left it out of my way.
5)      Each template gets an ID shown at the top of its information on the Transactional Template Engine page in SendGrid. They look like: 45ef9876-4321-1cd1-ab1c-d12a123a1234
6)      Go to Settings, API Keys.
a.       Click “Create API Key”.
b.      Save the giant key string that they display. This is important because they really won’t ever show it to you again.
c.       Edit details for the key you just created and set security for the functionality you want that key to be able to access.  In my case I just set Mail Send = full access.
7)      Over on the Rails side I create a new class for SendGrid.  Fortunately I had isolate the email send functionality in my application to one routine so I only had to create a new sendtemplate method.
8)      I used the sendgrid-ruby gem that is officially supported by SendGrid. Available here: https://github.com/sendgrid/sendgrid-ruby

Here is the entire file with comments added. In general the documentation from the gem was very clear and I had only one minor problem leveraging the API which I’ve referenced in line.

# Begin Code
module RDSendGrid

  # I use this to return a standard result from the class no matter what I get back from the vendor
  class FakeRes
    attr_accessor :code
    attr_accessor :body
  end
 
  class RDSendGrid

    require "uri"
    require 'sendgrid-ruby'
  
    def initialize
    end # initialize
   
    ##########################################################################
    # audit_id: the id of my internal record for this email
    # template_id: the template id provided by SendGrid for the template I want to send
    # from_email: the address to use as the sender’s email
    # from_name:  name to show instead of the address in the recipient’s mail client
    # to_email: The address to send the message to
    # to_name: The name of the person to send the message to
    # subject: the subject of the email message  
    # merge_vars: an array of attribute value pairs of replacement variables
    #    these get sent en-mas to the template engine and are specific to each template
    #    the pairs are “name” and “content”
    # bcc: provide an email address here if I want to bcc someone on this message
    ##########################################################################

    def sendtemplate(audit_id, template_id, from_email, from_name, to_email, to_name, subject, merge_vars, bcc ="")
       # defined in my environment
      sendgridkey = Rails.configuration.sendgridkey
     
      # This flag allows me to do email testing without pushing the message to SendGrid
      if Rails.configuration. sendmail

        # This was the only part that wasn’t crystal clear to me in the gem documentation
        # See my note at the end
        # Create an SMTPAPI header and put settings into it
        header = Smtpapi::Header.new
        header.add_unique_arg("rd_audit_number", audit_id)

        # Create a SendGrid recipient to add to the list
        # At this time my sendtemplate function only sends a single message but SendGrid can
        # process a list of recipients so I’ve allowed for that expansion in the future

        # Create a sendgid recipient list
        recipients = []

        recipient = SendGrid::Recipient.new(to_email)
        merge_vars.each do |mv|
          # note that I used *||* as variable delimiters because they were that way in mandril
          recipient.add_substitution('*|' + mv["name"] + '|*', mv["content"])
        end
       
        # Put our one and only recipient into the array of recpients
        recipients << recipient
       
        # Create a sendgrid template
        puts(template_id)
        template = SendGrid::Template.new(template_id)
       
        # Create a client
        # Note that because I don’t have a body to send I’ve made html and text blank
        # However, an empty string wouldn’t work for me so I used one space
        # Adding the smtpapi header here, this was the second half of the unclear part for me
        client = SendGrid::Client.new(api_key:  sendgridkey)
        mail_defaults = {
          smtpapi: header,
          from: from_email,
          from_name: from_name,
          to: to_email,
          to_name: to_name,
          bcc: bcc,
          html: ' ',
          text: ' ',
          subject: subject
        }

        # Create a new TemplateMailer
        mailer = SendGrid::TemplateMailer.new(client, template, recipients)       
       
        # send it
        lres = mailer.mail(mail_defaults)

        # return the result
        res = FakeRes.new
        res.code = lres.code
        res.body = lres.body["message"]
       
      else
        # Rails.configuration.sendmail was set to false so fake a success return
        res = FakeRes.new
        res.code = "200"
        res.body = "success"
      end
       
      return res
    end #sendtemplate
  end   
end
# End code

Now about my notes on the SMTPAPI Header. In order to get email events back from SendGrid, and update my email history with the data, I needed to send a unique ID for each message to them. The documentation is actually quite clear on how to do that and how to create an api header for that purpose. The only thing that was unclear (to me personally) was how exactly to associate that header with the TemplateMailer call. In the end, it was just a question of creating the header -- header = Smtpapi::Header.new --, populating it -- header.add_unique_arg("rd_audit_number", audit_id) -- and then assigning the header to the mail_deafulats -- mail_defaults = { smtpapi: header … --


So, once again I’ve managed to do something that thousands have done before me and yet find the need to share it with others anyway.  Next time I’ll share how I processed the mail event updates from SendGrid.

2 comments: