Unable to parse Sendgrid parse api raw callback into Griddler email object - ruby-on-rails

I am trying to accomplish the following:
Have all emails send to my domain parsed for the content of their to, body, subject line and ... in order for me to construct a fax object from it and send it as a fax
So far I have been able to setup my MX record to point to Sendgrid and from Sendgrid's Parse API using Ngrok I get the callback in my localhost server. The next step is to get the raw object into an object I could work with. This lead me to Thoughbot's Griddler Gem.
This is how I have setup my app so far:
Sendgrid Setup:
URL: https://8c00fab0.ngrok.io/email_processor | Spam Check and Send Raw are both checked
As mentioned before this part works since I do receive this:
Parameters: {"dkim"=>"{#gmail.com : pass}", "email"=>"Received: by mx0034p1mdw1.sendgrid.net with SMTP id 253FfvZ40f Wed, 09 Sep 2015 22:31:48 +0000 (UTC)\nReceived: from mail-qg0-f54.google.com (mail-qg0-f54.google.com [209.85.192.54]) by mx0034p1mdw1.sendgrid.net (Postfix) with ESMTPS id 63848A83017 for <14085552422#faxbyemail.co>; Wed, 9 Sep 2015 22:31:48 +0000 (UTC)\nReceived: by qgx61 with SMTP id 61so21251135qgx.3 for <14085552422#faxbyemail.co>; Wed, 09 Sep 2015 15:31:48 -0700 (PDT)\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:from:date:message-id:subject:to:content-type; bh=Wo6p7CSlR30aGxf5SwjuZ2klt31URDqTZUHrKdmLz7s=; b=fWfFOQ4YOtijYvphYL9/1shyzK8CBTcHS37s89t9Qn4xK8OrXwVKtmGtNnwg7a1n48 JHkEeAdxVvRMpinxPllGiJoPNy0ivVN36uEYbsrLTcZVlx9RIIl8u5PjmrE1aqlnkjPy gOcbfJunVCFZLaLpv3RB/GiNYL1bodgW7nn8sMTmwN5EwoH7SncS7cle4ksPadgiGv9f DVoJniX2yuMqygSVLT4Qrk4JDzR6G2oj1Fz3QkcrQfNKxOS6iyBhpnh3cDuUi8GTSVYl Nqq2dtKlUNqI+U9ojtG1BjGVUKbP0v5apF3Wvmj72p8L0AB3FHqL8uvFnhi2dGTFVq+y 6nUQ==\nX-Received: by 10.140.233.137 with SMTP id e131mr48879201qhc.37.1441837907953; Wed, 09 Sep 2015 15:31:47 -0700 (PDT)\nMIME-Version: 1.0\nFrom: Sam Sedighian <samansb#gmail.com>\nDate: Wed, 09 Sep 2015 22:31:38 +0000\nMessage-ID: <CAEMkzYbhsLLB3NPLznG+-RibP5kzDG5yTT++mQL1ep-vH6_orA#mail.gmail.com>\nSubject: test 20\nTo: \"14085552422#faxbyemail.co\" <14085552422#faxbyemail.co>\nContent-Type: multipart/alternative; boundary=001a11354d8053d2d1051f580c22\n\n--001a11354d8053d2d1051f580c22\nContent-Type: text/plain; charset=UTF-8\n\nbody etxt\n\n--001a11354d8053d2d1051f580c22\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\">body etxt</div>\n\n--001a11354d8053d2d1051f580c22--\n", "to"=>"\"14085552422#faxbyemail.co\" <14085552422#faxbyemail.co>", "from"=>"Sam Sedighian <samansb#gmail.com>", "sender_ip"=>"209.85.192.54", "spam_report"=>"Spam detection software, running on the system \"mx0034p1mdw1.sendgrid.net\", has\nidentified this incoming email as possible spam. The original message\nhas been attached to this so you can view it (if it isn't spam) or label\nsimilar future email. If you have any questions, see\n##CONTACT_ADDRESS## for details.\n\nContent preview: body etxt body etxt [...] \n\nContent analysis details: (0.0 points, 5.0 required)\n\n pts rule name description\n---- ---------------------- --------------------------------------------------\n 0.0 FREEMAIL_FROM Sender email is freemail (samansb[at]gmail.com)\n 0.0 HTML_MESSAGE BODY: HTML included in message\n 0.0 T_MIME_NO_TEXT No text body parts\n\n", "envelope"=>"{\"to\":[\"14085552422#faxbyemail.co\"],\"from\":\"samansb#gmail.com\"}", "subject"=>"test 20", "spam_score"=>"0.012", "charsets"=>"{\"to\":\"UTF-8\",\"subject\":\"UTF-8\",\"from\":\"UTF-8\"}", "SPF"=>"pass"}
Gemfile
gem 'griddler'
gem 'griddler-sendgrid'
Gemfile.lock
griddler (1.2.1)
htmlentities
rails (>= 3.2.0)
griddler-sendgrid (0.0.1)
griddler
routes.rb
post '/email_processor' => 'fax#sendgrid'
# this corresponds to the following route when I rake route:
# email_processor POST /email_processor(.:format) fax#sendgrid
config/initializers/griddler.rb
Griddler.configure do |config|
config.processor_class = EmailProcessor
config.processor_method = :process
config.reply_delimiter = '-- REPLY ABOVE THIS LINE --'
config.email_service = :sendgrid
end
app/controllers/fax_controller.rb
def sendgrid
EmailProcessor.new(params).process
end
app/models/email_processor.rb
class EmailProcessor
def initialize(email)
#email = email
#from = email.from
#body = email.body
end
def self.process(email)
Fax.create({params go here})
end
end
I end up giving a Completed 500 Internal server error - Since I do not pass a 200 OK back to Sendgrid but the important part is that I get the following error in my log:
NoMethodError (undefined method `from' for #<ActionController::Parameters:0x007fc742a33070>):
app/models/email_processor.rb:5:in `initialize'
app/controllers/fax_controller.rb:31:in `new'
app/controllers/fax_controller.rb:31:in `sendgrid'

I think the issue is that you're bypassing the work Griddler does on the controller side. You're pointing Sendgrid at a controller and action you created yourself. Instead I think you want to point to Griddler's controller and action. See the README under "Installation".
Change your routes.rb to be:
# your own route
# post '/email_processor' => 'fax#sendgrid'
# griddler's
post '/email_processor' => 'griddler/emails#create'
If you take a look at Griddler's controller you'll see that there are a few things it does for you to get things into your email processor class.

The way I do this in Rails is to gather the email params in the Controller.
For instance, in a sample app I created, people email selfies to our Parse API. In my selfie_controller.rb, I have a route '/inbound' and I have the following:
def inbound
if !params['attachment1'].nil? and params['attachment1'].content_type == "image/jpeg"
Selfy.create!({ pic: params['attachment1'], email: params['from'] })
end
end
The call to Selfy.create! uses the selfy.rb model, but I gather the params from the email to send to that from the controller.
Is this something you could try?

Related

How do I find the payload of a Sendgrid email sent by ActionMailer?

I'm working with SendGrid support to determine why categories stopped working on my multipart email campaigns (the text-only one is fine). If I intentionally set the content-type of an HTML email as "text/plain" the email displays the header data, text and raw html all on a single email, but will get its category. Otherwise the email looks correct, but there's no category.
SendGrid has asked me to send them a copy of the payload and I'm not sure what that is or how to find it. They said "If you are familiar with running a telnet test then that is what we are looking for." I'm not familiar with telnet tests. This is the info from the screenshot they provided as an example of what they're looking for:
220 Hi! This is Rob's hMailServer!
ehlo panoply-tech.com
250-SAGE013963
250-SIZE 20480000
250 AUTH LOGIN PLAIN
AUTH LOGIN
334 VXN1ea5bbVUG
YT3TQBHbhM9WBHKTDGUjeD65WQ20=
235 authenticated.
MAIL FROM: mayes#panoply-tech.com
250 OK
RCPT TO: cstickings#demosagecrm.com
250 OK
DATA
354 OK, send.
Subject: This is a test email
Hi Clemence,
Just sending you a test email.
.
250 Queued <25.927 seconds>
I went to .rvm/gems/ruby-2.3.3/gems/actionmailer-4.2.8/lib/action_mailer/base.rb and found a method called "set_payload_for_mail" but what that produces does seem to be like their example:
{"mailer":"B2c::B2cSendGridMailer",
"message_id":"5d0b979767c26_16f2c3fc04043f9c84968e#Domain-Person.local.mail",
"subject":"TEST: 26_txt","to":["person#domain.com"],
"from":["info#another.com"],"date":"2019-06-20T09:26:31.000-05:00",
"mail":"Date: Thu, 20 Jun 2019 09:26:31 -0500\r\nFrom: info#another.com\r\nTo: person#domain.com\r\nMessage-ID: \u003c5d0b979767c26_16f2c3fc04043f9c84968e#Domain-Person.local.mail\u003e\r\nSubject: TEST: 26_txt\r\nMime-Version: 1.0\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n
X-SMTPAPI: {\"category\":[\"html_false\"]}\r\n
X-SMTPAPI: {\"filters\": {\"ganalytics\": {\"settings\": {\"enable\":1}}}}
\r\n\r\nHi there, but text\r\n"}
I know in the Google inbox, you can click "Show Original" for an email and see the header info, etc. I've sent that to them but that didn't have what they needed.
def b2c_tester(html=false, content)
e_domain = 'careinhomes.com'
#mailer_path = "app/views/b2c/b2c_send_grid_mailer"
#from = "info#careinhomes.com"
#recipients = ['gina#pipelinesuccess.com']
#subject = html ? "#{DateTime.now.minute.to_s}_html" :
"#{DateTime.now.minute.to_s}_txt"
header_category = {"category": ["html_#{html}"]}
headers['X-SMTPAPI'] = header_category.to_json
if html
msg = tester_mail_with_opts({domain: e_domain}, content)
else
msg = tester_mail_plain_text_with_opts(
"b2c_tester",{domain: e_domain})
end
msg
end
#content ex: 'text/plain', 'text/html', 'multipart/alternative', etc
def tester_mail_with_opts(delivery_options={}, content=nil)
mail_opts = set_mail_opts(delivery_options)
unless content.nil?
mail_opts[:content_type] = content
end
mail mail_opts
end
def set_mail_opts(delivery_options={})
#subject = "TEST: #{#subject}" unless Rails.env.production?
# Required
mail_opts = {
to: #recipients,
from: #from,
subject: #subject,
}
mail_opts[:template_path] = #template_path if #template_path
mail_opts[:content_type] = #content_type if #content_type
# Do delivery options
mail_opts[:delivery_method_options] = DELIVERY_OPTIONS
mail_opts[:delivery_method_options] =
mail_opts[:delivery_method_options].merge(delivery_options)
unless delivery_options.blank?
mail_opts
end
In ActionMailer's base model is a method called deliver_mail that extracts the payload and you can capture it that way. It appears that, for my problem, the payload is an empty hash.
This is what a healthy payload should look like from ActionMailer:
{"mailer":"B2c::B2cSendGridMailer","message_id":"5d10dacb26dc2_17fb93ff52483b9c8952bf#Domain-Person.local.mail","subject":"TEST: 14_txt","to":["person#domain.com"],"from":["info#another.com"],"date":"2019-06-24T09:14:35.000-05:00","mail":"Date: Mon, 24 Jun 2019 09:14:35 -0500\r\nFrom: info#another.com\r\nTo: person#domain.com\r\nMessage-ID: \u003c5d10dacb26dc2_17fb93ff52483b9c8952bf#Domain-Person.local.mail\u003e\r\nSubject: TEST: 14_txt\r\nMime-Version: 1.0\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\nX-SMTPAPI: {\"category\":[\"html_false\"]}\r\nX-SMTPAPI: {\"filters\": {\"ganalytics\": {\"settings\": {\"enable\":1}}}}\r\n\r\nHi there, but text\r\n"}

Attempt to send an email via Rails ActionMailer results in a failed call to `normalize_name` inside `lookup_context.rb`

I have a Mailer class which inherits from ApplicationMailer, which in turn inherits from ActionMailer::Base. Ruby version is ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin15].
The Mailer class looks like the following:
class PurchaseOrderStatusMailer < ApplicationMailer
CONTACTS = {
JC: ['me#example.com'],
RM: ['me#example.com']
}
def daily_report_email(facility)
#facility = facility
ingredient_items = LineItem.ingredient.by_facility(facility)
#delivered_count = ingredient_items.by_date([7.days.ago, 7.days.from_now]).delivered.count
#partial_count = ingredient_items.by_date([7.days.ago, 1.day.ago]).partial.count
#late_count = ingredient_items.by_date([7.days.ago, 1.day.ago]).late.count
#expected_count = ingredient_items.by_date([Date.today, 7.days.from_now]).expected.count
mail(to: CONTACTS[facility.to_sym], subject: "#{facility} Daily Receipt Status - #{Date.today}")
end
end
ApplicationMailer looks like the following:
# frozen_string_literal: true
class ApplicationMailer < ActionMailer::Base
default from: 'notify#example.com'
def facility_email(facility)
emails = Rails.application.config_for(:emails)
(emails[facility] + emails["DEFAULT"]).flatten
end
end
The view is located at app/views/purchase_order_status_mailer/daily_report_email.html.erb.
When I open my Rails console and type PurchaseOrderStatusMailer.new.daily_report_email('JC').deliver, I see the following error:
NoMethodError: undefined method `empty?' for nil:NilClass
from /Users/me/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionview-5.0.4/lib/action_view/lookup_context.rb:215:in `normalize_name'
I tried passing a format block to the call to the mail helper with the same call, like so:
mail(to: CONTACTS[facility.to_sym], subject: "#{facility} Daily Receipt Status - #{Date.today}") do |format|
format.text { render plain: "Hey!" }
end
The above produced the following response, which appears to represent a successful email send:
Rendering text template
Rendered text template (0.0ms)
Sent mail to me#example.com (8.8ms)
Date: Mon, 25 Sep 2017 12:55:11 -0400
From: notify#example.com
To: me#example.com
Message-ID: <59c934efec401_1115b3fd69cc3f840917be#me-MBP.mail>
Subject: JC Daily Receipt Status - 2017-09-25
Mime-Version: 1.0
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Hey!
=> #<Mail::Message:70191029273300, Multipart: false, Headers: <Date: Mon, 25 Sep 2017 12:55:11 -0400>, <From: notify#example.com>, <To: ["me#example.com"]>, <Message-ID: <59c934efec401_1115b3fd69cc3f840917be#me-MBP.mail>>, <Subject: JC Daily Receipt Status - 2017-09-25>, <Mime-Version: 1.0>, <Content-Type: text/plain>, <Content-Transfer-Encoding: 7bit>>
I didn't actually receive an email, and I assume that means I don't have SMTP set up on my local machine, but the above response is encouraging. There was no stack trace apart from the error I posted above, so I tried digging into the Rails source code, and I saw that normalize_name inside lookup_context.rb gets called from within the args_for_lookup protected method, which in turn gets called by the ViewPaths module's find_all method. But beyond that it was hard to trace the call stack, as I couldn't find who the caller of find_all is.
My question is: what is wrong with the first call to mail?
EDIT 1: I also tried format.html { render html: "<h1>Hello Mikel!</h1>".html_safe } instead of the format.text option, as per the example here, and I got a similar success message.
I then tried adding a byebug statement inside normalize_name, to try and identify what the values of the parameters were on the successful email sends, but it looks like this method isn't getting called when a block is passed. That makes me suspect even more strongly that the problem is somehow related to my views. But I can't confirm that yet.
The issue is here
PurchaseOrderStatusMailer.new.daily_report_email('JC').deliver
remove the new i.e
PurchaseOrderStatusMailer.daily_report_email('JC').deliver
Because of the wrong object of its not able to deduce the template path/file
I was able to generate a success response (including my mailer's rendered template file) by passing template_path and template_name options to the original call to the mail helper, like so:
mail(
to: CONTACTS[facility.to_sym],
subject: "#{facility} Daily Receipt Status - #{Date.today}",
template_path: 'purchase_order_status_mailer',
template_name: 'daily_report_email')
This generated the following success response:
Rendering purchase_order_status_mailer/daily_report_email.text.erb
Rendered purchase_order_status_mailer/daily_report_email.text.erb (0.3ms)
Sent mail to me#example.com (12.1ms)
Date: Mon, 25 Sep 2017 15:18:25 -0400
From: notify#example.com
To: me#example.com
Message-ID: <59c9568121cec_11e943fee8503f82823542#me-MBP.mail>
Subject: JC Daily Receipt Status - 2017-09-25
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="--==_mimepart_59c9568120033_11e943fee8503f8282341e";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_59c9568120033_11e943fee8503f8282341e
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Please find the receipt status for JC as of 09-25-2017:
=================================================================================================
...<lots more content, including ERB rendered into HTML>...
=> #<Mail::Message:70293683934580, Multipart: true, Headers: <Date: Mon, 25 Sep 2017 15:18:25 -0400>, <From: notify#example.com>, <To: ["me#example.com"]>, <Message-ID: <59c9568121cec_11e943fee8503f82823542#me-MBP.mail>>, <Subject: JC Daily Receipt Status - 2017-09-25>, <Mime-Version: 1.0>, <Content-Type: multipart/alternative; boundary="--==_mimepart_59c9568120033_11e943fee8503f8282341e"; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>
I'm a little confused about why I had to add these options, as the documentation doesn't make it clear that these are required and my templates and folders appear to be named correctly, but it worked and I have deadlines so I'm moving on. :-)

ruby on rails action mailer not receiving emails to arrays from a yaml file

I have an issue where i am able to send email via the mailer for a hardcoded array.
But the email does not go through for an array picked up from config.yml
Here is my config.yml
company:
email:
- user1#company.com
- User1.Lastname#company.com
- User2.Lastname#company.com
This is my mailer class:
class ReportMailer < ActionMailer::Base
default :from => "donotreply#company.com"
def send_report(company, file)
mail(:to=> "#{CONFIG[company]['email']}", :subject => "Daily Report")
end
end
end
when run it in my rails console & view the logs, seems like everything was executed fine but I did not receive my email:
[DEBUG] 2016-04-21 18:21:29 :: Date: Thu, 21 Apr 2016 18:21:29 -0400
From: donotreply#merchantlink.com
to: ["user1#company.com", "User1.Lastname#company.com","User2.Lastname#company.com"]
...
...
[INFO] 2016-04-21 18:21:29 ::
Sent mail to ["user1#company.com", "User1.Lastname#company.com", "User2.Lastname#company.com"]
If i change my code & replace it with hardcoded array instead of reading from config.yml it works fine.
Am i reading the yaml array wrong?
As correctly pointed out in the comment by #Alfie, you are passing a stringified array to to:.
CONFIG[company]['email'] returns an array, then string interpolation calls to_s on it and you end up with
"['user1#company.com', 'User1.Lastname#company.com','User2.Lastname#company.com']"
Just pass in the array without inserting it into a string:
mail(:to=> CONFIG[company]['email'], :subject => "Daily Report")

Rails + SendGrid Unauthenticated senders not allowed

I'm currently working on sending emails via sendgrid and currently sending emails to new users is working. ...But strangely, very similar emailing options aren't working - the full email including the recipient is displayed in the log but is never received.
Here's what I have:
In a mail.rb file in initializers:
ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
:user_name => configatron.sendgrid.username,
:password => configatron.sendgrid.password,
:domain => 'heroku.com'
}
ActionMailer::Base.delivery_method = :smtp
with the username and password being defined elsewhere - they are confirmed to have their correct values
When a new user registers, I call my UserMailer class and do the following:
def welcome_email(user)
#email = user.email
#name = user.full_name
mail(to: #email, subject: "Welcome!")
end
And the worker calls it by simply doing the following:
UserMailer.welcome_email(user).deliver
This much works; I receive the email and that's that.
But when I try to send an email from a different method, magically things break down.
In the SAME UserMailer class:
def request(org, recipient, item)
#org = org
#recipient = recipient
#item = item
mail(to: #org.email, subject: "A new request has been posted!")
end
Using the SAME function (although this time outside of a worker so I can debug easily):
UserMailer.request(org, recipient, item).deliver
The email appears in the terminal perfectly:
Sent mail to mypersonalemail#test.com (4717ms)
Date: Mon, 31 Dec 2012 01:03:35 -0800
From: mysendingemail#test.com
To: mypersonalemail#test.com
Message-ID: <[longhexstring]#localhost.localdomain.mail>
Subject: A new request has been posted!
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="--==_mimepart_50e154e769903_3c41bba7ec576d5";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_50e154e769903_3c41bba7ec576d5
Date: Mon, 31 Dec 2012 01:03:35 -0800
Mime-Version: 1.0
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Content-ID: <[longhex]#localhost.localdomain.mail>
Hello world!
----==_mimepart_50e154e769903_3c41bba7ec576d5
Date: Mon, 31 Dec 2012 01:03:35 -0800
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Content-ID: <[longhex]#localhost.localdomain.
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
</head>
<body>
<p>
Hello world!
</p>
</body>
</html>
But, I check my inbox on my personal email, the junk, the trash, and received nothing. Even sendgrid has no logs of me ever requesting to send request emails! But the welcome emails are working perfectly fine, and even are logged through sendgrid. I don't see what could cause one group of emails to send but not another and this is really baffling. No obvious errors in syntax jump out at me, and the values I use are of the correct datatype and not nil.
Any help with this strange issue would be extremely appreciated.
The solution ended up being remarkably simple, so for those who find this thread and need help I'll at least post what I did.
In development.rb I did the following to see the error being raised:
config.action_mailer.raise_delivery_errors = true
And got this message:
Net::SMTPFatalError - 550 Cannot receive from specified address <mypersonalemail#test.com>: Unauthenticated senders not allowed
Which is strange considering I thought I was authenticated with the info I put into .env Outputting my configatron.sendgrid.username sure enough is nil. Still trying to figure out why that would be and I'm still not sure how the welcome emails were sent.
Sure enough, changing my smtp settings to be hardcoded values for authentication worked like a charm. Now the question becomes how to get the ENV to not come out nil...
The end solution was simply me being on the wrong localhost. I was using rails's default rails server at port 3000, but foreman start (not sure if by default but in my project's case) was using port 5000 instead.
I had this issue when running code via rake. The problem was that the SENDGRID_USERNAME and SENDGRID_PASSWORD environment variables stored in my .env were not loaded automatically via rake. The solution was to run rake through foreman:
foreman run bundle exec rake namespace:task

How to test email headers using RSpec

I'm using SendGrid's SMTP API in my Rails application to send out emails. However, I'm running into troubles testing the email header ("X-SMTPAPI") using RSpec.
Here's what the email looks like (retrieving from ActionMailer::Base.deliveries):
#<Mail::Message:2189335760, Multipart: false, Headers:
<Date: Tue, 20 Dec 2011 16:14:25 +0800>,
<From: "Acme Inc" <contact#acmeinc.com>>,
<To: doesntmatter#nowhere.com>,
<Message-ID: <4ef043e1b9490_4e4800eb1b095f1#Macbook.local.mail>>,
<Subject: Your Acme order>, <Mime-Version: 1.0>,
<Content-Type: text/plain>, <Content-Transfer-Encoding: 7bit>,
<X-SMTPAPI: {"sub":{"|last_name|":[Foo],"|first_name|":[Bar]},"to":["foo#bar.com"]}>>
Here's my spec code (which failed):
ActionMailer::Base.deliveries.last.to.should include("foo#bar.com")
I've also tried various method to retrieve the header("X-SMTPAPI") and didn't work either:
mail = ActionMailer::Base.deliveries.last
mail.headers("X-SMTPAPI") #NoMethodError: undefined method `each_pair' for "X-SMTPAPI":String
Help?
Update (answer)
Turns out, I can do this to retrieve the value of the email header:
mail.header['X-SMTPAPI'].value
However, the returned value is in JSON format. Then, all I need to do is to decode it:
sendgrid_header = ActiveSupport::JSON.decode(mail.header['X-SMTPAPI'].value)
which returns a hash, where I can do this:
sendgrid_header["to"]
to retrieve the array of email addresses.
The email_spec gem has a bunch of matchers that make this easier, you can do stuff like
mail.should have_header('X-SMTPAPI', some_value)
mail.should deliver_to('foo#bar.com')
And perusing the source to that gem should point you in the right direction if you don't want to use it e.g.
mail.to.addrs
returns you the email addresses (as opposed to stuff like 'Bob ')
and
mail.header['foo']
gets you the field for the foo header (depending on what you're checking you may want to call to_s on it to get the actual field value)
Repeating some of the other advice here, in more modern rspec syntax:
RSpec.describe ImportFile::Mailer do
describe '.file_error' do
let(:mail) { described_class.file_error('daily.csv', 'missing header') }
it { expect(mail.subject).to eq("Import error: missing header in daily.csv") }
it { expect(mail.header['X-source-file'].to_s).to eq ('daily.csv') }
end
end

Resources