Giter Site home page Giter Site logo

sendgrid-actionmailer's Introduction

SendGrid ActionMailer

An ActionMailer adapter to send email using SendGrid's HTTPS Web API (instead of SMTP). Compatible with Rails 5 and Sendgrid API v3.

Installation

Add this line to your application's Gemfile:

gem 'sendgrid-actionmailer'

Usage

Create a SendGrid API Key for your application. Then edit config/application.rb or config/environments/$ENVIRONMENT.rb and add/change the following to the ActionMailer configuration:

config.action_mailer.delivery_method = :sendgrid_actionmailer
config.action_mailer.sendgrid_actionmailer_settings = {
  api_key: ENV['SENDGRID_API_KEY'],
  raise_delivery_errors: true
}

Normal ActionMailer usage will now transparently be sent using SendGrid's Web API.

mail(to: '[email protected]', subject: 'email subject', body: 'email body')

Mail Settings

Mail settings, such as sandbox_mode, may be applied globally through the sendgrid_actionmailer_settings configuration.

config.action_mailer.delivery_method = :sendgrid_actionmailer
config.action_mailer.sendgrid_actionmailer_settings = {
  api_key: ENV['SENDGRID_API_KEY'],
  mail_settings: { sandbox_mode: { enable: true }}
}

Dynamic API Key

If you need to send mail for a number of Sendgrid accounts, you can set the API key for these as follows:

mail(to: '[email protected]',
  subject: 'email subject',
  body: 'email body',
  delivery_method_options: {
    api_key: 'SENDGRID_API_KEY'
  }
)

SendGrid Mail Extensions

The Mail functionality is extended to include additional attributes provided by the Sendgrid API.

Sendgrid v3 API Documentation

template_id (string)

The id of a template that you would like to use. If you use a template that contains a subject, you do not need to specify a subject at the personalizations nor message level. However, because of the way ActionMailer works, a body is required, even if the template contains one. If all your emails use templates with a body, you can add default body: "not used" to the top of your mailer.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', template_id: 'template_1')

sections (object)

An object of key/value pairs that define block sections of code to be used as substitutions.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', sections: {'%header%' => "<h1>Header</h1>"})

headers (object)

An object containing key/value pairs of header names and the value to substitute for them. You must ensure these are properly encoded if they contain unicode characters. Must not be one of the reserved headers.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', headers: {'X-CUSTOM-HEADER' => "foo"})

categories (array)

An array of category names for this message. Each category name may not exceed 255 characters.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', categories: ['marketing', 'sales'])

custom_args (object)

Values that are specific to the entire send that will be carried along with the email and its activity data. Substitutions will not be made on custom arguments, so any string that is entered into this parameter will be assumed to be the custom argument that you would like to be used. This parameter is overridden by personalizations[x].custom_args if that parameter has been defined. Total custom args size may not exceed 10,000 bytes.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', custom_args: {campaign: 'welcome'})

send_at (integer)

A unix timestamp allowing you to specify when you want your email to be delivered. This may be overridden by the personalizations[x].send_at parameter. You can't schedule more than 72 hours in advance. If you have the flexibility, it's better to schedule mail for off-peak times. Most emails are scheduled and sent at the top of the hour or half hour. Scheduling email to avoid those times (for example, scheduling at 10:53) can result in lower deferral rates because it won't be going through our servers at the same times as everyone else's mail.

batch_id (string)

This ID represents a batch of emails to be sent at the same time. Including a batch_id in your request allows you include this email in that batch, and also enables you to cancel or pause the delivery of that batch. For more information, see cancel_schedule_send

mail(to: '[email protected]', subject: 'email subject', body: 'email body', send_at: 1443636842, batch_id: 'batch1')

asm (object)

An object allowing you to specify how to handle unsubscribes.

group_id (integer) *required

The unsubscribe group to associate with this email.

groups_to_display (array[integer])

An array containing the unsubscribe groups that you would like to be displayed on the unsubscribe preferences page.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', asm: { group_id: 99, groups_to_display: [4,5,6,7,8] })

ip_pool_name (string)

The IP Pool that you would like to send this email from.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', ip_pool_name: 'marketing_ips')

mail_settings (object)

A collection of different mail settings that you can use to specify how you would like this email to be handled.

bcc (object)

This allows you to have a blind carbon copy automatically sent to the specified email address for every email that is sent.

enable (boolean)

Indicates if this setting is enabled.

email (string)

The email address that you would like to receive the BCC.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { bcc: { enable: true, email: '[email protected] }})

bypass_list_management (object)

Allows you to bypass all unsubscribe groups and suppressions to ensure that the email is delivered to every single recipient. This should only be used in emergencies when it is absolutely necessary that every recipient receives your email.

enable (boolean)

Indicates if this setting is enabled.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { bypass_list_management: { enable: true }})

footer (object)

The default footer that you would like included on every email.

enable (boolean)

Indicates if this setting is enabled.

text (string)

The plain text content of your footer.

html (string)

The HTML content of your footer.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { footer: { enable: true, text: 'FOOTER', html: '<h1>FOOTER</h1>' }})

sandbox_mode (object)

This allows you to send a test email to ensure that your request body is valid and formatted correctly.

enable (boolean)

Indicates if this setting is enabled.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { sandbox_mode: { enable: true }})

spam_check (object)

This allows you to test the content of your email for spam.

enable (boolean)

Indicates if this setting is enabled.

threshold (integer)

The threshold used to determine if your content qualifies as spam on a scale from 1 to 10, with 10 being most strict, or most likely to be considered as spam.

post_to_url (string)

An Inbound Parse URL that you would like a copy of your email along with the spam report to be sent to.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { spam_check: { enable: true, threshold: 1, post_to_url: 'https://spamcatcher.sendgrid.com' }})

tracking_settings(json)

Settings to determine how you would like to track the metrics of how your recipients interact with your email.

click_tracking(object)

Allows you to track whether a recipient clicked a link in your email.

enable (boolean)

Indicates if this setting is enabled.

enable_text (boolean)

Indicates if this setting should be included in the text/plain portion of your email.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', tracking_settings: { click_tracking: { enable: false, enable_text: false }})

open_tracking (object)

Allows you to track whether the email was opened or not, but including a single pixel image in the body of the content. When the pixel is loaded, we can log that the email was opened.

enable (boolean)

Indicates if this setting is enabled.

substitution_tag (string)

Allows you to specify a substitution tag that you can insert in the body of your email at a location that you desire. This tag will be replaced by the open tracking pixel.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', tracking_settings: { open_tracking: { enable: true, substitution_tag: 'Optional tag to replace with the open image in the body of the message' }})

subscription_tracking (object)

Allows you to insert a subscription management link at the bottom of the text and html bodies of your email. If you would like to specify the location of the link within your email, you may use the substitution_tag.

enable (boolean)

Indicates if this setting is enabled.

text (string)

Text to be appended to the email, with the subscription tracking link. You may control where the link is by using the tag <% %>

html (string)

HTML to be appended to the email, with the subscription tracking link. You may control where the link is by using the tag <% %>

substitution_tag (string)

A tag that will be replaced with the unsubscribe URL. for example: [unsubscribe_url]. If this parameter is used, it will override both the text and html parameters. The URL of the link will be placed at the substitution tag’s location, with no additional formatting.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', tracking_settings: { subscription_tracking: { enable: true, text: 'text to insert into the text/plain portion of the message', html: 'html to insert into the text/html portion of the message', substitution_tag: 'Optional tag to replace with the open image in the body of the message' }})

ganalytics (object)

Allows you to enable tracking provided by Google Analytics.

enable (boolean)

Indicates if this setting is enabled.

utm_source (string)

Name of the referrer source. (e.g. Google, SomeDomain.com, or Marketing Email)

utm_medium (string)

Name of the marketing medium. (e.g. Email)

utm_term (string)

Used to identify any paid keywords.

utm_content (string)

Used to differentiate your campaign from advertisements.

utm_campaign (string)

The name of the campaign.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', tracking_settings: { ganalytics: { enable: true, utm_source: 'some source', utm_medium: 'some medium', utm_term: 'some term', utm_content: 'some content', utm_campaign: 'some campaign' }})

dynamic_template_data (json)

Data to provide for feeding the new dynamic templates in Sendgrid with valueable data. This also disables the following Unsubscribe links because of deprecation of substitutions in the new template implementaiton. Variables are available within templates using {{handlebar syntax}}.

mail(to: '[email protected]', subject: 'email subject', body: 'email body', dynamic_template_data: { variable_1: 'foo', variable_2: 'bar' })

personalizations (json)

Allows providing a customized personalizations array for the v3 Mail Send endpoint. This allows customizing how an email is sent and also allows sending multiple different emails to different recipients with a single API call.

The personalizations object supports:

  • "to", "cc", "bcc" - The recipients of your email.
  • "subject" - The subject of your email.
  • "headers" - Any headers you would like to include in your email.
  • "substitutions" - Any substitutions you would like to be made for your email.
  • "custom_args" - Any custom arguments you would like to include in your email.
  • "send_at" - A specific time that you would like your email to be sent.
  • "dynamic_template_data" - data for dynamic templates.

The following should be noted about these personalization attributes:

  • to, cc, or bcc: if either to, cc, or bcc is also set when calling mail, those addresses provided to mail will be inserted as a separate personalization from the ones you provide. However, when using personalizations, you are not required to specify to when calling the mail function.
  • dynamic_template_data specified in the mail function will be merged with any dynamic_template_data specified in the personalizations object (with the personalizations object keys having priority).
  • Other fields set in the personalizations object will override any global parameters defined outside of personalizations.

Also note that substitutions will not work with dynamic templates.

Example usage:

mail(subject: 'default subject', 'email body', personalizations: [
  { to: [{ email: '[email protected]' }]},
  { to: [{ email: '[email protected]' }]}
])

Unsubscribe Links

Sendgrid unfortunately uses <% %> for their default substitution syntax, which makes it incompatible with Rails templates. Their proposed solution is to use Personalization Substitutions with the v3 Mail Send Endpoint. This gem makes that modification to make the following Rails friendly unsubscribe urls.

  • <a href="%asm_group_unsubscribe_raw_url%">Unsubscribe</a>
  • <a href="%asm_global_unsubscribe_raw_url%">Unsubscribe from List</a>
  • <a href="%asm_preferences_raw_url%">Manage Email Preferences</a>

Note: This feature, and substitutions in general, do not work in combination with dynamic templates.

Testing

The setting perform_send_request is available to disable sending for testing purposes. Setting perform_send_request false and return_response true enables the testing of the JSON API payload.

sendgrid-actionmailer's People

Contributors

davebream avatar dikond avatar eddiezane avatar gui avatar h6ah4i avatar hoangtuanictvn avatar hshimoyama avatar jadehopepunk avatar jjohnson avatar koppen avatar kristjan avatar michaelshowoff avatar radiantnode avatar ricklesgibson avatar rono23 avatar searls avatar shipstar avatar thomassnielsen avatar travisp avatar tricknotes avatar tyrauber avatar viniciusp avatar we5 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sendgrid-actionmailer's Issues

Optional fields

The readme states that

If you use a template that contains a subject and content (either text or html), you do not need to specify those at the personalizations nor message level.

This seems to be borrowed from the Sendgrid docs. It's not actually the case with ActionMailer (5.2.1) though. Trying to send an email without a subject causes NoMethodError: undefined method humanize' for nil:NilClassingems/actionmailer-5.2.1/lib/action_mailer/base.rb:880`.

Trying to send an email without a body causes NoMethodError: undefined method empty?' for nil:NilClassinactionview-5.2.1/lib/action_view/lookup_context.rb:206`.

In our case we set the subject and body in the Sendgrid template builder, leaving us with code like this:

def welcome_email
    user = params[:user]
    mail(
      to: user.email,
      subject: 'not-used',
      body: 'not-used',
      template_id: welcome_template_id,
      dynamic_template_data: {name: user.name},
      mail_settings: mail_settings
    )
  end

I think that either sendgrid-actionmailer should be updated to allow skipping subject and body (if possible), or the readme should be updated to not suggest skipping these. If the latter is chosen, I can draft a PR with the required changes.

Does not load when Rails inflection has been set for 'SendGrid'

Setting an inflection for SendGrid means we can make an exception to Rails' filenaming schemes. We can name models SendGrid... and not have to name files send_grid_..... However, it's introduced an issue here relating to sendgrid-actionmailer. The gem registers an ingress using the module name ActionMailbox::Ingresses::Sendgrid, but the previous acronym definition means the lookup expects SendGrid.

config/initializers/inflections.rb:

# frozen_string_literal: true

ActiveSupport::Inflector.inflections(:en) do |inflect|
  ...
  inflect.acronym 'SendGrid'
end

When running in production, where I use sendgrid-actionmailer:

/app/vendor/bundle/ruby/2.7.0/gems/actionmailbox-6.0.3.2/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb:47:in `<module:ActionMailbox>': uninitialized constant ActionMailbox::Ingresses::Sendgrid (NameError)
Did you mean?  ActionMailbox::Ingresses::SendGrid
SendGrid

There are a couple of questions I have as I write this:

  • Is this issue better suited to rails/rails? Potentially, it might concern the inflector more than the gem, but I'm not sure.
  • Is there anything that can be done to prevent or fix this from within the gem, temporarily or otherwise?

0.2.6 release

Hello. I've just started using this gem and noticed that the latest release here on GH is quite out of date relative to what's on RubyGems.

It'd be helpful to keep them in-sync.

Sending email with unsubscribe link

I can successfully send email but the "Unsubscribe" link it's not linked. It's only text. I read the Readme but it's not clear how to have it. Is there a way to have them by default instead of change it everywhere?

 mail(to: @user.email, template_id: template_id, asm: { group_id: 14375}, personalizations: [
            {substitutions: {
                "%Unsubscribe%": ["<%asm_group_unsubscribe_url%>"],
                "%rawUnsubscribe%": ["<%asm_group_unsubscribe_raw_url%>"]
           }}
        ])

Cannot parse variable with `=>`

We've encountered an issue when trying to send an email using a dynamic template, where one o the dynamic template variables contain a =>.

How to reproduce

The following test case demonstrates the issue:

      it "should not mess up contents" do
        custom_args = {"text" => "line with a => in it"}
        mail["custom_args"] = custom_args
        mailer.deliver!(mail)
        expect(client.sent_mail['custom_args']).to eq({"text" => "line with a => in it"})
      end

The test case ends up failing with a

Failure/Error: JSON.parse(text, symbolize_names: symbolize)

 JSON::ParserError:
   767: unexpected token at '{"text":"line with "a":in it"}'
 # ./lib/sendgrid_actionmailer.rb:142:in `json_parse'
 # ./lib/sendgrid_actionmailer.rb:165:in `add_send_options'
 # ./lib/sendgrid_actionmailer.rb:33:in `deliver!'
 # ./spec/lib/sendgrid_actionmailer_spec.rb:323:in `block (5 levels) in <module:SendGridActionMailer>'

See 2df06f5 to for detailed changes.

Debugging

The immediate issue seems to be that SendGridActionMailer::DeliveryMethod#json_parse tries to convert the text representation of a Hash to JSON in order to parse it back to a Hash. In doing so, it changes all occurrences of "=>" to ":".

The issue seems to have been introduced in 413db56.

Bcc extension for multiple addresses

Hello, I’m using sendgrid-actionmailer gem and I need to use the bcc extension to send copies of an email to multiple recipients; however I'm not sure of how to do it since the documentation only shows the object for a single recipient:
mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { bcc: { enable: true, email: '[email protected]' }})

So, for multiple recipients should I use an array of strings, something like:
mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { bcc: { enable: true, email: ['[email protected]', '[email protected]', '[email protected]'] }})

Or maybe just a string made of comma-separated addresses, something like:
mail(to: '[email protected]', subject: 'email subject', body: 'email body', mail_settings: { bcc: { enable: true, email: '[email protected], [email protected], [email protected]' }})

Or maybe an array of objects. Well, I hope you can help me, thanks.

Not all error messages have a body

Hi all

Unlike what Sendgrid says

"Failed requests will always return an error response, including a response code, a message explaining the reason for the error, and a link to any relevant documentation that may help you troubleshoot the problem."

We're been getting a bodyless 401 - our free dev account has run out of email deliveries. This is fine except that it's blowing up inside the sendgrid-actionmailer gem , namely here

def perform_send_request(email)
      result = client.mail._('send').post(request_body: email.to_json) # ლ(ಠ益ಠლ) that API
      if result.status_code && result.status_code.start_with?('4')
        message = JSON.parse(result.body).fetch('errors').pop.fetch('message')

with

JSON::ParserError: 767: unexpected token at ''

I guess testing the body before trying to parse JSON would be ideal

Here's a response object example

#<SendGrid::Response:0x000055fdf6b29e00
 @body="",
 @headers=
  {"server"=>["nginx"],
   "date"=>["Fri, 17 Jan 2020 09:58:24 GMT"],
   "content-type"=>["application/json"],
   "content-length"=>["76"],
   "connection"=>["close"],
   "access-control-allow-origin"=>["https://sendgrid.api-docs.io"],
   "access-control-allow-methods"=>["POST"],
   "access-control-allow-headers"=>
    ["Authorization, Content-Type, On-behalf-of, x-sg-elas-acl"],
   "access-control-max-age"=>["600"],
   "x-no-cors-reason"=>
    ["https://sendgrid.com/docs/Classroom/Basics/API/cors.html"]},
 @status_code="401">

Cannot send variables to a template

Hi there,

I'm struggling to pass variables from my rails app to my Sendgrid template.
Everything else works fine.

Here is the code I'm using:

    mail(
      to: @user,
      subject: 'email subject',
      body: 'email body',
      template_id: 'xxx',
      dynamic_template_data:{
        variable_1: 'foo',
        variable_2: 'bar'
      }
    )

and I tried all of the following in my template:

:variable_1
-variable_1-
%variable_1%
[variable_1]
variable1
:variable1
-variable1-
%variable1%
[variable1]
Variable1
:Variable1
-Variable1-
%Variable1%
[Variable1]

Any idea?

I also tried unique_args && sub

to field issue Sendgrid delivery failed with 400 The template_id must be a valid GUID

Seems that the to: field has an issue. When using a .each loop, running into issues, however if it's mapped seems to work fine. The issue with going the mapped route, is all users show up on the to line in the email:

Works:

class LessonMailer < ApplicationMailer
  default from: '[email protected]
  default body: 'not used'
 
  def new_lesson
    @users = User.all
    mail(to: @users.map(&:email).uniq, template_id: 'xxxx-xxxx-xxxxx-xxxxx-xxxxx')
  end
end

Does not work:

class LessonMailer < ApplicationMailer
  default from: '[email protected]
  default body: 'not used'
 
  def new_lesson
    @users = User.all
    @users.each do |u|
      mail(to: u.email, template_id: 'xxxx-xxxx-xxxxx-xxxxx-xxxxx')
    end
  end
end

The error:

[ActiveJob] [ActionMailer::Parameterized::DeliveryJob] [209ea2f1-0cab-4e6a-a4c1-7fd0a4bdfbb3] Error performing ActionMailer::Parameterized::DeliveryJob (Job ID: 209ea2f1-0cab-4e6a-a4c1-7fd0a4bdfbb3) from Async(mailers) in 507.38ms: SendGridActionMailer::DeliveryMethod::SendgridDeliveryError (Sendgrid delivery failed with 400 The template_id must be a valid GUID, you provided '[#<Mail::Field 0x7fd0ebc8ff78 @name="template-id" @unparsed_value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx" @charset="UTF-8" @field_order_id=100 @field=#<Mail::OptionalField:0x00007fd0ec4cb750 @errors=[], @charset="UTF-8", @name="template-id", @length=nil, @element=nil, @value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx">>, #<Mail::Field 0x7fd0eb7aaa08 @name="template-id" @unparsed_value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx" @charset="UTF-8" @field_order_id=100 @field=#<Mail::OptionalField:0x00007fd0ec4bd010 @errors=[], @charset="UTF-8", @name="template-id", @length=nil, @element=nil, @value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx">>, #<Mail::Field 0x7fd0f186a618 @name="template-id" @unparsed_value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx" @charset="UTF-8" @field_order_id=100 @field=#<Mail::OptionalField:0x00007fd0ec4c2718 @errors=[], @charset="UTF-8", @name="template-id", @length=nil, @element=nil, @value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx">>, #<Mail::Field 0x7fd0ec491230 @name="template-id" @unparsed_value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx" @charset="UTF-8" @field_order_id=100 @field=#<Mail::OptionalField:0x00007fd0ec4d34a0 @errors=[], @charset="UTF-8", @name="template-id", @length=nil, @element=nil, @value="xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx">>]'.):
/Users/me/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/sendgrid-actionmailer-2.2.1/lib/sendgrid_actionmailer.rb:232:in `perform_send_request'
/Users/me/.rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/sendgrid-actionmailer-2.2.1/lib/sendgrid_actionmailer.rb:38:in `deliver!'

mail_settings cannot be empty hash

Passing mail_settings: {} causes a crash when sending emails.

The error is caused by the usage of json_parse to parse mail settings. The regex used in json_parse is confused by blank strings, which is what you get calling mail['mail_settings'].value when mail_settings is empty.

The code I had when discovering this:

  def welcome_email
    user = params[:user]
    mail(
      to: user.email,
      template_id: WELCOME_TEMPLATE_ID,
      dynamic_template_data: { name: user.first_name },
      mail_settings: mail_settings
    )
  end


  private

  def mail_settings
    if Rails.env.production?
      {}
    else
      {
        sandbox_mode: {
          enable: true
        }
      }
    end
  end

I'm changing my code to this workaround for now:

  def mail_settings
    {
      sandbox_mode: {
        enable: !Rails.env.production?
      }
    }
  end

I'm not sure what the intention of the regex in the json_parse method is, so unfortunately I can't attempt a PR at this time.

Can't send any emails

I've created an API key in my sendgrid dashboard and have added:

config.action_mailer.delivery_method = :sendgrid_actionmailer
  config.action_mailer.sendgrid_actionmailer_settings = {
    api_key: ENV['SENDGRID_API_KEY']
  }

to production.rb. Sending an email with a UserMailer class in console generates:

UserMailer#launch_email: processed outbound mail in 7.7ms
=> #<Mail::Message:70117088042780, Multipart: false, Headers: <From: [email protected]>, <To: [email protected]>, <Subject: test!>, <Mime-Version: 1.0>, <Content-Type: text/html>>

However nothing ever arrives in my inbox, and sendgrid dashboard doesn't show any activity for an outbound email. Any other settings I'm forgetting to include?

Setup global retry using mail settings

We're seeing timeouts in calling Sendgrid API and has set the following settings in action mailer -
Net::OpenTimeout Failed to open TCP connection to api.sendgrid.com:443 (execution expired)

Is there a way to setup retries globally or any other approach to reduce timeouts ?

config.action_mailer.sendgrid_actionmailer_settings = {
    api_key: <>,
    raise_delivery_errors: true,
    http_options: { open_timeout: 3 }
}

Substitutions may not be used with dynamic templating

gem version: 2.1.0

I'm trying to switch to using the new dynamic templates, but I keep getting Sendgrid delivery failed with 400 Substitutions may not be used with dynamic templating when sending emails. I've tried different options based on the readme (with and without a dynamic_template_data object, etc), but I'm basically stuck.

What I've been trying so far:

mailer.mail(to: email, from: sender_email, template_id: template_id, subject: "test", body: "test").deliver!

The docs says that subject and body is optional if it's provided in the template, but that doesn't seem to be the case by default (Unless the docs means providing an ActionMailer template, in which case that should be specified):

NoMethodError: undefined method `humanize' for nil:NilClass
from /Users/thomas/.rvm/gems/ruby-2.5.1/gems/actionmailer-5.2.1/lib/action_mailer/base.rb:880:in `default_i18n_subject'

It seems to me that either it doesn't work yet (maybe v3 support isn't fully baked?), or there is some config required that isn't in the readme. I've tried reading the issues for sendgrid-ruby as well, and I understand that the status of their SDK and documentation is less than ideal as well.

If anyone could point me in the right direction that would be great. If we figure it out I can make a PR to update the readme with the parts I'm missing.

Using a dynamic template only sends the text/plain part

We use the dynamic template feature of SendGrid quite heavily. All of our templates contain both a HTML version and a Plain Text version (both auto generated and manually created). This has worked fine until august 13th or so, after which our emails stopped containing the text/html part.

How to reproduce

Given a Dynamic Template with id d-8bc581e6c95d4a9a8f47ee85bcda656a containing both a HTML and a Plain Text version, we set up a mailer with the following method:

class SendgridMailer < ApplicationMailer
  def test
    mail(
      :to => "[email protected]",
      :body => "Test email body",
      :template_id => "d-8bc581e6c95d4a9a8f47ee85bcda656a"
    )
  end
end

SendgridMailer.test.deliver_now

Actual result

  • I receive an email with only a text/plain part.

Expected

  • I receive an email with both a text/html and a text/plain part.

Details

After august 13th our emails started containing only a text/plain part, ie the expected text/html part was nowhere to be seen. After talking to SendGrid support it was determined that we should stop sending a content_type in the request to them and SendGrid would figure out what parts to include.

However, that is proving tricky using sendgrid-actionmailer.

Thanks to ActionMailer a :body key is required, and following the instructions for using template_id it is suggested to just set it even though it isn't used.

Sending an email using the following arguments:

mail(
  :to => "[email protected]",
  :body => "Test email body",
  :template_id => "d-8bc581e6c95d4a9a8f47ee85bcda656a"
)

This does deliver an email, but the email contains only a text/plain part. The JSON generated and submitted to the backend API is:

{
  "from":{
    "email":"[email protected]",
  },
  "subject":"Test",
  "personalizations":
    [
      {
        "to":[
          {
            "email":"[email protected]"
          }
        ]
      }
    ],
    "content":[
      {
        "type":"text/plain",
        "value":"Test email body"
      }
    ],
    "template_id":"d-8bc581e6c95d4a9a8f47ee85bcda656a"
  }

As it can be seen, the unused "Test email body" is submitted under the key content.value and a content.type key is added with the value text/plain.

If we remove the content key and its children and submit the following JSON to the API manually, the email arrives just fine with both a text/plain part and text/html part as expected:

{
  "from":{
    "email":"[email protected]",
  },
  "subject":"Test",
  "personalizations":[
    {
      "to":[
        {"email":"[email protected]"}
      ]
    }
  ],
  "template_id":"d-8bc581e6c95d4a9a8f47ee85bcda656a"
}

Interestingly we get the same effect if we add both wanted MIME types to the content key:

{
  "from":{
    "email":"[email protected]",
  },
  "subject":"Test",
  "personalizations":[
    {
      "to":[
        {"email":"[email protected]"}
      ]
    }
  ],
  "content":[
    {
      "type":"text/plain",
      "value":"Test email body"
    },
    {
      "type":"text/html",
      "value":"Test email body"
    }
  ],
  "template_id":"d-8bc581e6c95d4a9a8f47ee85bcda656a"
}

Suggestions

  1. We could stop sending the content elements if the email also contains template_id?
  2. We could start including the unused body value in both text/plain and text/html content parts when the email contains template_id.

Personally, I am leaning towards the first option, as that is the simpler payload transmitted over the wire, it makes no assumptions about what the sender wants, and it's what SendGrid explained to us should happen.

However, option 2 is closer to what the payload is likely to look like when not using template_id and is more explicit.

Actionmailer test helper not working with sandbox

I am using the actionmailer test helper assert_emails method in various tests; I have added { sandbox_mode: { enable: true }} in the sendgrid_actionmailer_settings configuration but the tests arent passing because the emails arent being sent. Thanks in advance for any help with this!

Failure:
1 emails expected, but 0 were sent.
Expected: 1
  Actual: 0

Error message: A JSON text must at least contain two octets!

Hello, I'm trying to use this gem but I get this error message A JSON text must at least contain two octets! when I use the Action Mailer deliver_now method. I'm using a mailer, a template and calling it from a controller as usual, testing in development environment. I have no idea about the possible causes, any idea?

More details:
`Completed 500 Internal Server Error in 990ms (ActiveRecord: 4.4ms)

JSON::ParserError - A JSON text must at least contain two octets!:
json (1.8.6) lib/json/common.rb:155:in initialize' json (1.8.6) lib/json/common.rb:155:in new'
json (1.8.6) lib/json/common.rb:155:in parse' sendgrid-actionmailer (2.4.2) lib/sendgrid_actionmailer.rb:231:in perform_send_request'
sendgrid-actionmailer (2.4.2) lib/sendgrid_actionmailer.rb:38:in deliver!' mail (2.6.3) lib/mail/message.rb:252:in deliver!'
actionmailer (4.2.1) lib/action_mailer/message_delivery.rb:77:in deliver_now!' app/controllers/prospects_controller.rb:605:in test_mailing'
actionpack (4.2.1) lib/action_controller/metal/implicit_render.rb:4:in send_action' actionpack (4.2.1) lib/abstract_controller/base.rb:198:in process_action'
actionpack (4.2.1) lib/action_controller/metal/rendering.rb:10:in process_action' actionpack (4.2.1) lib/abstract_controller/callbacks.rb:20:in block in process_action'
activesupport (4.2.1) lib/active_support/callbacks.rb:117:in call' activesupport (4.2.1) lib/active_support/callbacks.rb:555:in block (2 levels) in compile'
activesupport (4.2.1) lib/active_support/callbacks.rb:505:in call' activesupport (4.2.1) lib/active_support/callbacks.rb:498:in block (2 levels) in around'
activesupport (4.2.1) lib/active_support/callbacks.rb:313:in block (2 levels) in halting' sentry-raven (2.7.4) lib/raven/integrations/rails/controller_transaction.rb:7:in block in included'
activesupport (4.2.1) lib/active_support/callbacks.rb:441:in instance_exec' activesupport (4.2.1) lib/active_support/callbacks.rb:441:in block in make_lambda'
activesupport (4.2.1) lib/active_support/callbacks.rb:312:in block in halting' activesupport (4.2.1) lib/active_support/callbacks.rb:497:in block in around'
activesupport (4.2.1) lib/active_support/callbacks.rb:505:in call' activesupport (4.2.1) lib/active_support/callbacks.rb:92:in _run_callbacks'
activesupport (4.2.1) lib/active_support/callbacks.rb:776:in _run_process_action_callbacks' activesupport (4.2.1) lib/active_support/callbacks.rb:81:in run_callbacks'
actionpack (4.2.1) lib/abstract_controller/callbacks.rb:19:in process_action' actionpack (4.2.1) lib/action_controller/metal/rescue.rb:29:in process_action'
actionpack (4.2.1) lib/action_controller/metal/instrumentation.rb:32:in block in process_action' activesupport (4.2.1) lib/active_support/notifications.rb:164:in block in instrument'
activesupport (4.2.1) lib/active_support/notifications/instrumenter.rb:20:in instrument' activesupport (4.2.1) lib/active_support/notifications.rb:164:in instrument'
actionpack (4.2.1) lib/action_controller/metal/instrumentation.rb:30:in process_action' actionpack (4.2.1) lib/action_controller/metal/params_wrapper.rb:250:in process_action'
activerecord (4.2.1) lib/active_record/railties/controller_runtime.rb:18:in process_action' actionpack (4.2.1) lib/abstract_controller/base.rb:137:in process'
actionview (4.2.1) lib/action_view/rendering.rb:30:in process' rack-mini-profiler (1.0.0) lib/mini_profiler/profiling_methods.rb:104:in block in profile_method'
actionpack (4.2.1) lib/action_controller/metal.rb:196:in dispatch' actionpack (4.2.1) lib/action_controller/metal/rack_delegation.rb:13:in dispatch'
actionpack (4.2.1) lib/action_controller/metal.rb:237:in block in action' actionpack (4.2.1) lib/action_dispatch/routing/route_set.rb:74:in dispatch'
actionpack (4.2.1) lib/action_dispatch/routing/route_set.rb:43:in serve' actionpack (4.2.1) lib/action_dispatch/journey/router.rb:43:in block in serve'
actionpack (4.2.1) lib/action_dispatch/journey/router.rb:30:in each' actionpack (4.2.1) lib/action_dispatch/journey/router.rb:30:in serve'
actionpack (4.2.1) lib/action_dispatch/routing/route_set.rb:819:in call'

emails not delivered - 400 bad request

I’m using sendgrid-actionmailer gem with those dependencies (also tested without)

gem 'sendgrid', '1.2.4'
gem 'sendgrid-ruby', '6.1.2'
gem 'sendgrid-actionmailer', '3.0.0'

I'm struggling with using SendGrid v3; my emails keep not being delivered, I keep receiving a 400 bad request, especially when there are special characters inside it (there are two special characters in the footer. When I remove them, the email is delivered successfully)

Do you have any what I could be doing wrong?

Appreciate the help

Testability of Template Based Email

Great gem.

We are migrating a lot of mails from conventional rails mailers to Sendgrid templates.

One thing we are missing is testability. We use personalizations with dynamic templates and there is no clear way to get the final list of recipients.

The gem does the bulk of its work in deliver!. Is there a way the final sendgrid_mail.to_json can be exposed in the delivery? Maybe as additional mail header?

Or may be the preparation of request can be extracted into its own method that an email Observer can use to verify?

def prepare_request(mail)
  sendgrid_mail = Mail.new.tap do |m|
    m.from = to_email(mail.from)
    m.reply_to = to_email(mail.reply_to)
    m.subject = mail.subject || ""
  end

  add_personalizations(sendgrid_mail, mail)
  add_api_key(sendgrid_mail, mail)
  add_content(sendgrid_mail, mail)
  add_send_options(sendgrid_mail, mail)
  add_mail_settings(sendgrid_mail, mail)
  add_tracking_settings(sendgrid_mail, mail)

  sendgrid_mail
end

def deliver!(mail)
  response = perform_send_request(prepare_request(mail))

  settings[:return_response] ? response : self
end

Release tags after v2.6.0 are missing or are inconsistent

Please add tags for releases v3.0.1, v3.0.2 and v3.1.0, and normalize the 3.0.0 tag to v3.0.0. This will make it easier to determine why I'm having problems with a particular version of sendgrid-actionmailer as I can use the tag to diff between various versions to see where or if a bug I may encounter is fixed.

The latest release of sendgrid-actionmailer on RubyGems is 3.1.0 and it is missing a tag. There are also the 3.0.1, 3.0.2 releases that are missing tags.

Version 2.6.0 has a v2.6.0 tag and earlier have tags starting with v. Version 3.0.0 has a 3.0.0 tag which doesn't match the leading v for prior releases.

There are no tags for versions after 3.0.0.

Feature request: Make it possible to config request timeout

Hello, I'm using your sendgrid-actionmailer gem now. And I make this request to hope that you can add the feature to pass the http_options like timeout from config of actionmailer to sendgrid-ruby to set the timeout for sendgrid API request as below.

config.action_mailer.sendgrid_actionmailer_settings = {
  api_options: {
    http_options: {
      open_timeout: x,
      write_timeout: x
      ...
    }
  }
  ...
}

I had read some issues of the sendgrid/ruby-http-client gem. In sendgrid/ruby-http-client#20 PR, http_options were added, so I hope it is possible to config in your gem.

Header handling doesn't match Mail gem's delivery methods

When using any of the built-in Mail delivery methods, this works as expected to send a custom header.

class FooMailer < ApplicationMailer
  def foo_message
    headers['X-CUSTOM'] = "I am some header"
    mail(
      :from         => "[email protected]",
      :to           => "[email protected]",
      :subject      => "My Test message"
    ) do |format|
      format.text { "I am a message. Wheeeeeee" }
    end
  end
end

This also works.

class FooMailer < ApplicationMailer
  def foo_message
    mail(
      :from         => "[email protected]",
      :to           => "[email protected]",
      :subject      => "My Test message",
      "X-CUSTOM"    => "I am some header"
    ) do |format|
      format.text { "I am a message. Wheeeeeee" }
    end
  end
end

This gem is instead requiring this:

class FooMailer < ApplicationMailer
  def foo_message
    mail(
      :from         => "[email protected]",
      :to           => "[email protected]",
      :subject      => "My Test message",
      :headers      => {
        "X-CUSTOM"     => "I am some header"
      }
    ) do |format|
      format.text { "I am a message. Wheeeeeee" }
    end
  end
end

Clarify how to test

Using something like

self.sendgrid_actionmailer_settings = {
  api_key: ENV["SENDGRID_API_KEY"],
  perform_send_request: !Rails.env.test?,
  return_response: Rails.env.test?
}

should I be able to expect(ActionMailer::Base.deliveries.size).to eq(1)?

Git tags are missing or inconsistent

Please add tags for releases v3.0.1, v3.0.2 and v3.1.0, and normalize the 3.0.0 tag to v3.0.0. This will make it easier to determine why I'm having problems with a particular version of sendgrid-actionmailer as I can use the tag to diff between various versions to see where or if a bug I may encounter is fixed.

The latest release of sendgrid-actionmailer on RubyGems is 3.1.0 and it is missing a tag. There are also the 3.0.1, 3.0.2 releases that are missing tags.

Version 2.6.0 has a v2.6.0 tag and earlier have tags starting with v. Version 3.0.0 has a 3.0.0 tag which doesn't match the leading v for prior releases.

There are no tags for versions after 3.0.0.

Update changelog

The changelog is super useful when updating gems, but the one for this project is out of date. It would be great to have it updated for future versions, and even better to have it retroactively updated for previous versions.

Categories containing commas

I noticed that, if I send an email that looks like this:

      mail(
        to: email,
        subject: subject,
        categories: [{foo: 42, bar: 99}.to_json]
      )

This library ends up mangling the API request, because it splits the categories on commas.

I want to say right now, I would understand if you said "we dont support this, you are abusing the Categories field". I am unfortunately dealing with a legacy app that happens to be abusing the Categories field.

If this is something you do think we should support, I'm not sure exactly how it should work. The Mail library does some weird parsing on the email fields, which means you'd have to do something like mail['categories'].instance_variable_get(:@unparsed_value), which is pretty weird/bad.

And if this isn't something you want to support (I am open to this!) it might be worth adding some documentation to the readme saying "commas are illegal inside of individual categories".

Having troubles with attachments

Hi there,
I've troubles sending with attachments. The first run raises an error, when I do the exact same command again, it works:

Complete output

InvoiceMailer.with(user: User.last, invoice: Stripe::Event.construct_from(WebhookEvent.last.data['data']['object'])).order_failed_email.deliver_now
  User Load (1.7ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
  WebhookEvent Load (2.2ms)  SELECT "webhook_events".* FROM "webhook_events" ORDER BY "webhook_events"."id" DESC LIMIT $1  [["LIMIT", 1]]
InvoiceMailer#order_failed_email: processed outbound mail in 10943.9ms
Traceback (most recent call last):
        2: from (irb):1
        1: from app/mailers/invoice_mailer.rb:16:in `order_failed_email'
RuntimeError (Can't add attachments after `mail` was called.)
Make sure to use `attachments[]=` before calling `mail`.
>> InvoiceMailer.with(user: User.last, invoice: Stripe::Event.construct_from(WebhookEvent.last.data['data']['object'])).order_failed_email.deliver_now
  User Load (2.6ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
  WebhookEvent Load (2.9ms)  SELECT "webhook_events".* FROM "webhook_events" ORDER BY "webhook_events"."id" DESC LIMIT $1  [["LIMIT", 1]]
InvoiceMailer#order_failed_email: processed outbound mail in 9208.0ms
Delivered mail 6284a23f4edf5_5b8924683512c@JJ-MacBook-Pro.local.mail (908.1ms)
Date: Wed, 18 May 2022 09:37:35 +0200
From: noreply@fromme.at
To: some@email.com
Message-ID: <6284a23f4edf5_5b8924683512c@JJ-MacBook-Pro.local.mail>
Subject: =?UTF-8?Q?money grabbing failed
Mime-Version: 1.0
Content-Type: multipart/mixed;
 boundary="--==_mimepart_6284a23f4d766_5b8924683502d";
 charset=UTF-8
Content-Transfer-Encoding: 7bit


----==_mimepart_6284a23f4d766_5b8924683502d
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Some comlainings....

----==_mimepart_blah_blah
Content-Type: application/pdf;
 filename=invoice.pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename=invoice.pdf
Content-ID: <blah_blah@JJ-MacBook-Pro.local.mail>

JVBERi0xLjQKMSovQ3JlYXRvciAo/v8A
dwBrAGgAdABtAGwAAuADEAMgAuADUpCi9Qcm9kdWNl
ciAo/v8AUQB0ACXRpb25EYXRlIChEOjIwMjIw
NTE4MDczNzMxWikKPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL0V4dEdT
....
...
..
.

Complete mailer class

class InvoiceMailer < ApplicationMailer
  def order_failed_email
    raise "InvoiceMailer.order_failed_email: no User found" if params[:user].blank?

    # grabbing remote PDF and attaching it
    attachments['invoice.pdf'] = URI.open(params[:invoice][:invoice_pdf]).read if params[:invoice][:invoice_pdf].present?
    sending mail
    mail(
      to: params[:user][:email],
      subject: 'money grabbing failed',
      body: "Some comlainings...."
    )

  end
end

# ApplicationMailer
class ApplicationMailer < ActionMailer::Base
  default from: ENV['EMAIL_MAILER']
  layout 'mailer'
end

Anything more I've to concern? Did I miss something?

Gemfile:

gem 'sendgrid-ruby'
gem 'sendgrid-actionmailer'

development.rb

  config.action_mailer.default_url_options = { host: 'localhost', port: ENV.fetch("APP_PORT") }
  # Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = true

  config.action_mailer.perform_caching = true
  config.action_mailer.delivery_method = :sendgrid_actionmailer
  config.action_mailer.sendgrid_actionmailer_settings = {
    api_key: ENV['SENDGRID_API_KEY'],
    raise_delivery_errors: true
  }

sendgrid_actionmailer_settings => options

What are the options you can set for sendgrid_actionmailer_settings? e.g. can you set the 'domain'?


config.action_mailer.sendgrid_actionmailer_settings = {
    api_key: '',
    raise_delivery_errors: true
  }

How do I send attachments?

Initially sent an array of hashes under :attachments with keys matching what the Sendgrid API might expect, but that did not work.

NoMethodError: undefined method `unparsed_value' for #<Mail::OptionalField:0x007fbc2ad7b318>

Hi I think this is related to issue #99

OP was using "personalizations" and I'm using "custom_args" (works if i remove custom_args)

Still having this error in Rails 5

Rails 5.1.6
ruby 2.3.1p112
sendgrid-actionmailer-3.2.0

	def sendgrid_test(to_email)
		mail(:to => to_email, :subject => "Test", custom_args: {campaign: 'welcome'}) do |format|
			format.text { render :plain => "Test message" }
		end
	end
irb(main):027:0> ApplicationMailer.sendgrid_test("[email protected]").deliver_now
  Rendering text template
  Rendered text template (0.1ms)
ApplicationMailer#sendgrid_test: processed outbound mail in 22.7ms
Sent mail to [email protected] (8.9ms)
Date: Wed, 16 Feb 2022 18:45:23 +0800
From: XXX <[email protected]>
To: [email protected]
Message-ID: <[email protected]>
Subject: Test
Mime-Version: 1.0
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: 7bit
custom-args: {:campaign=>"welcome"}
X-SMTPAPI: {"category":"dev","filters":{"clicktrack":{"settings":{"enable":0}}}}

Test message
NoMethodError: undefined method 'unparsed_value' for #<Mail::OptionalField:0x007fbc2ad7b318>
	from (irb):27

get Sendgrid message ID back from mail call

Is is possible to get the ID Sendgrid assigns the email when calling mail? I was looking to do something like:

response = mail(to: '[email protected]', subject: 'email subject', body: 'email body')
puts response.sendgrid_id

I see there is an option to pass in return_response, but it looks like I get the same data back when calling mail.

Thank you.

DeliveryMethod#settings method is not implemented properly

The DeliveryMethod#settings method should return a Hash object which is passed to the constructor. However, current implementation just returns an empty Hash.

Current implementation causes an issue when I use return_response option which is supported by the mikel/mail gem.

> mailer = SMTPActionMailer.new  # this mailer uses SMTP for delivery method
> mailer.mail(...).deliver!
=>  #<Mail::Message: ...
> mailer.mail(..., delivery_method_options: { return_response: true }).deliver!
=> #<Net::SMTP::Response: ...
> mailer = SendGridActionMailer.new  # this mailer uses sendgrid-actionmailer for delivery method
> mailer.mail(...).deliver!
=>  #<Mail::Message: ...
> mailer.mail(..., delivery_method_options: { return_response: true }).deliver!
=>  #<Mail::Message: ...

# ^^^ THIS SHOULD BE  #<SendGrid::Response: ...

In the mikel/mail gem's code, we can see the following code. It invokes delivery_method.settings method and checks whether the :return_response flag is present.

module Mail
  class Message
    ...

    def deliver!
      inform_interceptors
      response = delivery_method.deliver!(self)
      inform_observers
      delivery_method.settings[:return_response] ? response : self
    end

    ...
  end
end

source: https://github.com/mikel/mail/blob/6bc16b4bce4fe280b19523c939b14a30e32a8ba4/lib/mail/message.rb#L271

Reference implementation:
https://github.com/mikel/mail/blob/master/lib/mail/network/delivery_methods/smtp.rb

NoMethodError: undefined method `settings' for #<SendGridActionMailer::DeliveryMethod:0x0055cc36982d50>

I got this error, when I was just testing this Gem for the first time.
I made a new Mailer called SendgridMailer and defined a method in that as test_send_email and sent an email to myself. Then I opened rails console and tried sending an email and I got the following error.

Rails version is 4.1.14


SendgridMailer.test_send_email.deliver!
  Rendered sendgrid_mailer/test_send_email.html.erb (2.9ms)

SendgridMailer#test_send_email: processed outbound mail in 186.5ms
NoMethodError: undefined method `settings' for #<SendGridActionMailer::DeliveryMethod:0x0055cc36982d50>

Error parsing empty response body

Hi there !

I ran into this exception : A JSON text must at least contain two octets! which happens on this line in your code :
message = !!(result.body) ? JSON.parse(result.body).fetch('errors').pop.fetch('message') : 'Sendgrid API Error'

as apparently, some 401 responses from Sendgrid have an empty body...
Moreover, I ran into another 401 saying :
Sendgrid delivery failed with 401 Could not authenticate
but when I tried again the same operation with the same key it got through... does that ring any bell ?

Anyway thanks for the great work :)

SendGrid Web API port change

Capture d’écran 2020-07-14 à 16 03 28

Is there anything to do about it in your gem about it ? Hopefully it would only require upgrading the sendgrid-ruby gem, but I don't think it's been updated to handle this issue.

json_parse issue

Hi there ! I've read the other issues regarding the json_parse method, however I think my issue is different : I'm trying to upgrade our project from SendGrid v1 to v3 using your gem; all of our email templates contain fields to be substitued using the handlebar syntax.
I pass a personalization object looking like this :

personalizations: [{ to: [{ email: '[email protected]' }], substitutions: {'{{contact_first_name}}' => contact.first_name } }]

But I get this error :

JSON::ParserError - 419: unexpected token at '{"to":[{"email":"[email protected]"}], "substitutions":{"{{contact_first_name}}"":"Davy"}}]':
  json (1.8.6) lib/json/common.rb:155:in 'parse'
/Users/dav/Documents/plezi/vendor/gems/sendgrid-actionmailer-2.6.0/lib/sendgrid_actionmailer.rb:176:in 'json_parse'
  /Users/dav/Documents/plezi/vendor/gems/sendgrid-actionmailer-2.6.0/lib/sendgrid_actionmailer.rb:190:in 'add_personalizations'
  /Users/dav/Documents/plezi/vendor/gems/sendgrid-actionmailer-2.6.0/lib/sendgrid_actionmailer.rb:29:in 'deliver!'

I'm trying to understand where the extra double quote character comes from here :
{"{{contact_first_name}}"":"Davy"} but I'm having a little trouble debugging the regexp, the only fix I managed to come up with is this :

def json_parse(text, symbolize=true)
  JSON.parse(text.empty? ? '{}' : text.gsub(/:*\"*([\%a-zA-Z0-9_-]*)\"*(( *)=>\ *)/) { "\"#{$1}\":" }.gsub(/\"\":/, '":'), symbolize_names: symbolize)
end

If you have a cleaner suggestion that would allow me not to use a modified version of your code, it would be great !
Thank you for your great work :)

Incompatibility with new SendGrid API (v3)

Using this gem, I've been getting intermittent "Could not authenticate" errors from SendGrid when trying to send emails. As they're intermittent, I've been having trouble getting to the root cause, but I did run across this note in SendGrid's docs:

"API Keys work for v3 API calls and for sending email. API endpoints prior to v3 do not support API keys at this time." - https://sendgrid.com/docs/Classroom/Send/How_Emails_Are_Sent/api_keys.html

As this gem's gemspec limits the sendgrid-ruby version to < 2.0, it seems that we're not using SendGrid's v3 API, which leads me to wonder if this is a cause of the issue.

For the moment, given project time constraints, I'm planning to use a different approach to integrating with SendGrid, but wanted to file this issue in the hopes that it's helpful.

Thanks for your work in creating this!

Not attaching files

Rails 5, sendgrid-actionmailer 3.1.0

I'm working with activestorage with cloudinary, and sendgrid dynamic templates, so this is what I tried:

require 'open-uri'

def my_email
      @record.work_files.each do |file|
         temp_file = open(file.service_url).read
         attachments[file.blob[:filename]] = { mime_type: file.blob.content_type, content: temp_file }
       end
mail to: '[email protected]', template_id: template_id, dynamic_template_data: { first_name: 'dortin' }
end

this worked before I add this gem, I tried also with:

require 'open-uri'
def my_email
      @record.work_files.each do |file|
         temp_file = open(file.service_url).read
         attachment = SendGrid::Attachment.new
         attachment.content = temp_file
         attachment.filename = file.blob[:filename]
         mail.add_attachment(attachment)
       end
mail to: '[email protected]', template_id: template_id, dynamic_template_data: { first_name: 'dortin' }
end
require 'open-uri'
def my_email
@mail_attachments  = []
      @record.work_files.each do |file|
         temp_file = open(file.service_url).read

         @mail_attachments << {
           filename: file.blob[:filename],
           type: file.blob.content_type,
           content: temp_file
         }
       end
mail to: '[email protected]', template_id: template_id, dynamic_template_data: { first_name: 'dortin' }, attachments: @mail_attachments
end

but nothing worked

Devise

Can you add doc for the steps needed to make this work with Devise?

Email address with name is not handled properly

I noticed some issues with address like User Name <[email protected]> format.

Issue 1

If there are quotation marks around the user name, quotation marks are not removed.

"User Name" <[email protected]>

# Should be parsed as:
{ name: 'User Name', email: '[email protected]' }
# but current:
{ name: '"User Name"',  email: '[email protected]' }

Actually, this bug came from the split_email method of sendgrid/sendgrid-ruby gem. However, the syntax of valid address is so complicated, so we should rely on more reliable mikel/mail gems implementation.

Issue 2

The named format should be accepted in every E-mail field, such as From, To, CC, BCC, Reply-To. However, current implementation only support it for From field. If named address specified for other fields, names are completely ignored.

asm parameter does not work

mail to: to, subject: "NEW TEST EMAIL"

works

mail(to: to, subject: 'NEW TEST EMAIL WITH ASM SUPPORT', asm: { group_id: 1234, groups_to_display: [1234]} )

does not work

neither does any of the following:
mail(to: to, subject: 'NEW TEST EMAIL WITH ASM SUPPORT', asm: { group_id: 1234, groups_to_display: []} )
mail(to: to, subject: 'NEW TEST EMAIL WITH ASM SUPPORT', asm: { group_id: 1234, groups_to_display: [1234, 1235]} )
mail(to: to, subject: 'NEW TEST EMAIL WITH ASM SUPPORT', asm: { group_id: 1234} )

or any variants including body, i.e.:
mail(to: to, subject: 'NEW TEST EMAIL WITH ASM SUPPORT', body: 'THIS BE BODY', asm: { group_id: 1234} )

The groups ids I've used are valid. Failure to send email does not raise any errors despite raise_delivery_errors: true

Sendgrid Activity Feed does not display messages that don't get delivered (the ones that have asm set), but the emails without asm show and deliver fine.

NoMethodError: undefined method `any?' for email address

We're seeing an error from time to time whereby this library seems to think the value passed to the to: option of the mail method is an array. In this case, add_personalizations (sendgrid_actionmailer.rb:178) is calling any? on the to value. Since this is a string (only one recipient), it blows up.

I've also seen each called on the to: value in the past, again indicating that the library expects this value to be an array.

Has anyone else run into this? The short-term fix seems to be Array(EMAIL_ADDRESS) but I don't know if that will have different repercussions.

Support for bulk email personalizations?

One of the nice features of the sendgrid API is that it's possible to send multiple emails to different recipients using personalizations. For example, suppose you have a feature where a group of users want to be notified when something happens. Instead of looping through each user and making a separate API call for each one, personalizations can be used to make one API call that sends a separate email to each user (and substitutes in things like their first name).

Would there be interest in having this gem to support this?

I would do this by extending mail to take is_multiple similar to the sendgrid python library, e.g.

addresses = ['[email protected]', '[email protected]']
sections = [{'%fname%' => 'User 1'}, {'%fname%' => 'User 2'}]
mail(to: addresses, sections: sections, subject: 'email subject', body: 'email body', is_multiple: true)

and modifying the to_personalizations method of sendgrid-actionmailer

How do I enable sandbox mode in environment file?

We want to use SendGrid sandbox mode to test our email sending.
However, I'm unable to get it work. I've tried the code below, but the email is still sent:

config.action_mailer.delivery_method = :sendgrid_actionmailer
config.action_mailer.sendgrid_actionmailer_settings = {
	api_key: Rails.application.credentials.dig(:sendgrid, :rest_api_key),
	raise_delivery_errors: true,
	mail_settings: {
		sandbox_mode: {
			enable: true
		}
	}
}

What am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.