Sendgrid v3 not working for me with rails my_mailer.rb

余生颓废 提交于 2019-12-24 21:13:37

问题


I want to send a transactional mail via Sendgrid when a user registers (I use devise for authentication). I had this working fine in my_mailer.rb using SMTP as follows:

  def confirmation_instructions(record, token, opts={})
    # SMTP header for Sendgrid - v2
    # headers["X-SMTPAPI"]= {
    #  "sub": {
    #    "-CONFIRM_TOKEN-": [
    #      token
    #    ]       
    #   },
    #  "filters": {
    #   "templates": {
    #     "settings": {
    #       "enable": 1,
    #       "template_id": "1111111-1111-1111-1111-111111111111"
    #     }
    #   }
    #  }   
    # }.to_json    

However, prompted by Sendgrid to use v3 syntax to support newer mail templates, I changed code to the following (from the sendgrid help docs, as opposed to a real understanding):

  def confirmation_instructions(record, token, opts={})

    require 'sendgrid-ruby'
    include SendGrid

    sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])

    data = JSON.parse('{
      "substitutions": {
        "-CONFIRM_TOKEN-": [
          token
        ],
      "template_id": "1111111-1111-1111-1111-111111111111"
    }')

    response = sg.client.mail._("send").post(request_body: data)
    puts response.status_code
    puts response.body
    puts response.parsed_body
    puts response.headers    

Now I get the error message:
'NoMethodError (undefined method `include' for #<MyMailer:0x0000000003cfa398>):'

If I comment out the 'include' line I get:
'TypeError (no implicit conversion of nil into String):' on the line: "sg = SendGrid..."

I use the Gem: sendgrid-ruby (5.3.0)

Any ideas would be appreciated - I've been trying to hit on the correct syntax by trial-and-error for a while now and finally admit I am stuck.

UPDATE#1:
The first issue was I was using the wrong API_KEY env. variable (copied from 2 different help docs): "SENDGRID_API_KEY" (in code) vs. SENDGRID_APIKEY_GENERAL (set in Heroku). Fixed.

UPDATE #2:
With the "include" line commented out I now seem to be getting a JSON parse error:

JSON::ParserError (416: unexpected token at 'token

So my 2 current issues are now:
(1) I would like 'token' to be the confirmation token variable but it is not being passed

(2) Sending the below simple (1 line) content of 'data' does not throw up an error, but the appropriate template within Sendgrid is not selected:

data = JSON.parse('{
  "template_id": "1111111-1111-1111-1111-111111111111"    
}')

UPDATE #3:
Here's an update on the status of my issue and exactly where I am now stuck:

This code works fine (using Sendgrid v2 which I am trying to upgrade from):

  def confirmation_instructions(record, token, opts={})

    #
    # SMTP header for Sendgrid     - v2
    #     This works fine
    #

    headers["X-SMTPAPI"]= {
     "sub": {
       "-CONFIRM_TOKEN-": [
         token
       ]
      },
     "filters": {
      "templates": {
        "settings": {
          "enable": 1,
          "template_id": "1111111-1111-1111-1111-111111111111"
        }
      }
     }
    }.to_json 

This Sendgrid v3 code does not work (the email does get sent via Sendgrid but it does not select the template within Sendgrid - it just uses whatever code is in app/views/my_mailer/confirmation_instructions.html.erb):

    #
    # Sendgrid API v3
    #   This sends an email alright but it takes content from app/views/my_mailer/confirmation_instructions.html.erb
    #   It DOES NOT select the template within Sendgrid
    #

    data = JSON.parse('{
      "template_id": "1111111-1111-1111-1111-111111111111",
      "personalizations": [
        {
          "substitutions": {
            "-CONFIRM_TOKEN-": "'+token+'"
          } 
        }
      ]
    }')

    sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL2'])
    response = sg.client.mail._("send").post(request_body: data)
    puts response.status_code
    puts response.body
    puts response.parsed_body
    puts response.headers

As always, any insight appreciated.


回答1:


For anyone trying to get SendGrid v3 API working with Ruby/Devise/Heroku and use SendGrid's dynamic transactional emails these tips may help you. I spent a week getting this to work and these steps (& mistakes I made) were not apparent in the various documentation:

  1. Generating the SendGrid API key (on SendGrid website): when generating the key, the API key only appears once allowing you to copy it, from then on it is invisible. As I could not see the key later I mistakenly used the "API Key ID" in my Heroku environment variables, rather than the true API Key.

  2. Ensure the name you give the key in Heroku (for me: "SENDGRID_APIKEY_GENERAL") matches the code you use to reference it i.e. sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL'])

  3. For sending variables to be substituted in the template use "dynamic_template_data" and not "substitutions". This should be within the "personalizations" section (see code example below).

  4. I found it useful to refer to the Sendgrid dynamic template ID by using an environment variable in Heroku (for me: 'SENDGRID_TRANS_EMAIL_CONFIRM_TEMPLATE_ID') as opposed to hard-coding in Ruby (just allowed me to experiment with different templates rather than changing code).

  5. The correct syntax for using a variable in the JSON string in Ruby is e.g. "CONFIRM_TOKEN": "'+token+'" (see code example below)

  6. Do not use other characters in the name: i.e. "CONFIRM_TOKEN" worked but "-CONFIRM_TOKEN-" did not work

  7. In the HTML of the transactional email template on SendGrid use this syntax for the substitution: {{CONFIRM_TOKEN}}

  8. When creating a transactional template on SendGrid you can only have a 'design' view or a 'code' view not both. You must select at the start when creating the template and cannot switch after.

  9. In the devise confirmations_instructions action refer to the user as a record (e.g. email) as record.email

  10. Gemfile: gem 'rails', '5.2.2' ruby '2.6.1' gem 'devise', '4.6.1' gem 'sendgrid-ruby', '6.0.0'

Here is my successful ruby code that I have in my_mailer.rb:

  def confirmation_instructions(record, token, opts={})

    data = JSON.parse('{
      "personalizations": [
        {
          "to": [
            {
              "email": "'+record.email+'"
            }
          ],
          "subject": "Some nice subject line content",
          "dynamic_template_data": {
            "CONFIRM_TOKEN": "'+token+'",
            "TEST_DATA": "hello"
          }
        }
      ],
      "from": {
        "email": "aaaa@aaaa.com"
      },
      "content": [
        {
          "type": "text/plain",
          "value": "and easy to do anywhere, even with Ruby"
        }
      ],
      "template_id": "'+ENV['SENDGRID_TRANS_EMAIL_CONFIRM_TEMPLATE_ID']+'"
    }')

    sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL'])
    response = sg.client.mail._("send").post(request_body: data)
    puts response.status_code
    puts response.body
    puts response.headers



回答2:


You cannot include a module in a method. You have to include it in your class, so outside of the methode, like

class SomethingMailer
  require 'sendgrid-ruby'
  include SendGrid

  def confirmation_instructions(record, token, opts={})
    ...
  end
end

For your third update problem:

You are not sending a JSON but instead you are creating a JSON, then parsing it into a hash, then sending that hash, instead of the JSON.

JSON.parse #parses a JSON into a Hash

You should do the opposite and have a hash that you transform into a JSON

Something like

data = {
  template_id: your_template_id # or pass a string
  personalizations: [
    ...
  ]
}

Then you call

data_json = data.to_json
response = sg.client.mail._("send").post(request_body: data_json)

However this does not explain why your template in app/views/my_mailer/confirmation_instructions.html.erb gets sent. So I think you are either calling a different mailer_method at the same time, or you are not calling your actual confirmation_instructions method at all. Try to confirm that you SendGrid Methods actually is called and see what it returns. It should have returned some kind of error before, when you were sending a hash instead of a string.



来源:https://stackoverflow.com/questions/56967786/sendgrid-v3-not-working-for-me-with-rails-my-mailer-rb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!