bulk email using send grid in rails - ruby-on-rails

Context:
I need to send bulk-email using send grid in a rails app.
I will be sending emails to maybe around 300 subscribers.
I have read that it can be accomplished using
headers["X-SMTPAPI"] = { :to => array_of_recipients }.to_json
I have tried following that.
The following is my ActionMailer:
class NewJobMailer < ActionMailer::Base
default from: "from#example.com"
def new_job_post(subscribers)
#greeting = "Hi"
headers['X-SMTPAPI'] = { :to => subscribers.to_a }.to_json
mail(
:to => "this.will#be.ignored.com",
:subject => "New Job Posted!"
)
end
end
I call this mailer method from a controller
..
#subscribers = Subscriber.where(activated: true)
NewJobMailer.new_job_post(#subscribers).deliver
..
The config for send-grid is specified in the config/production.rb file and is correct, since I am able to send out account activation emails.
Problem:
The app works fine without crashing anywhere, but the emails are not being sent out.
I am guessing the headers config is not being passed along ?
How can I correct this ?
UPDATE:
I checked for email activity in the send grid dashboard.
Here is a snapshot of one of the dropped emails:

You are grabbing an array of ActiveRecord objects with
#subscribers = Subscriber.where(activated: true)
and passing that into the smtpapi header. You need to pull out the email addresses of those ActiveRecord objects.
Depending on what you called the email field, this can be done with
headers['X-SMTPAPI'] = { :to => subscribers.map(&:email) }.to_json

Related

Sending emails in Rails using HTTP protocol(MailGun API)

I am trying to sending emails using MailGun's batch sending API using MailGun ruby sdk(https://github.com/mailgun/mailgun-ruby/blob/master/docs/MessageBuilder.md). As of now I have this method inside a class which inherits from ActionMailer.
class BatchMailer < ApplicationMailer
def send_batch_email(mail, recipients)
# First, instantiate the Mailgun Client with your API key
mg_client = Mailgun::Client.new("your-api-key")
# Create a Batch Message object, pass in the client and your domain.
mb_obj = Mailgun::BatchMessage.new(mg_client, "example.com")
# Define the from address.
mb_obj.from("me#example.com", {"first" => "Ruby", "last" => "SDK"});
# Define the subject.
mb_obj.subject("A message from the Ruby SDK using Message Builder!");
# Define the body of the message.
mb_obj.body_text("This is the text body of the message!");
# Loop through all of your recipients
mb_obj.add_recipient(:to, "john.doe#example.com", {"first" => "John", "last" => "Doe"});
mb_obj.add_recipient(:to, "jane.doe#example.com", {"first" => "Jane", "last" => "Doe"});
mb_obj.add_recipient(:to, "bob.doe#example.com", {"first" => "Bob", "last" => "Doe"});
...
mb_obj.add_recipient(:to, "sally.doe#example.com", {"first" => "Sally", "last" => "Doe"});
# Call finalize to get a list of message ids and totals.
message_ids = mb_obj.finalize
# {'id1234#example.com' => 1000, 'id5678#example.com' => 15}
end
end
Is is a correct way to keep the method that doesn't use actionmailer to send emails inside mailer?
ActionMailer method returns mail object but when trying to write spec for the method that uses API to send emails I can't able to get response as there won't be a mail object(ActionMailer message object). Where to keep this method and how it can be tested?
Is this a correct way to keep the method that doesn't use actionmailer to send emails inside mailer?
There is no reason to use a Mailer in this case. Simply use a service object (a plain-old ruby object or PORO). It might look something like:
class BatchMailerService
attr_accessor *%w(
mail
recipients
recipient
).freeze
delegate *%w(
from
subject
body_text
add_recipient
finalize
), to: :mb_obj
delegate *%w(
address
first_name
last_name
), to: :recipient, prefix: true
class << self
def call(mail, recipients)
new(mail, recipients).call
end
end # Class Methods
#==============================================================================================
# Instance Methods
#==============================================================================================
def initialize(mail, recipients)
#mail, #recipients = mail, recipients
end
def call
setup_mail
add_recipients
message_ids = finalize
end
private
def mg_client
#mg_client ||= Mailgun::Client.new(ENV["your-api-key"])
end
def mb_obj
#mb_obj ||= Mailgun::BatchMessage.new(mg_client, "example.com")
end
def setup_mail
from("me#example.com", {"first" => "Ruby", "last" => "SDK"})
subject("A message from the Ruby SDK using Message Builder!")
body_text("This is the text body of the message!")
end
def add_recipients
recipients.each do |recipient|
#recipient = recipient
add_recipient(
:to,
recipient_address,
{
first: recipient_first_name,
last: recipient_last_name
}
)
end
end
end
Which you would use something like:
BatchMailerService.call(mail, recipients)
(assuming, naturally, that you have variables called mail and recipients).
Where to keep this method?
You might place that file in app/services/batch_mailer_service.rb.
How can it be tested?
What do you mean? How you test the service depends on what your criteria for success are. You could test that mb_obj receives the finalize call (maybe using something like expect().to receive). You could test message_ids contains the correct information (maybe using something like expect().to include). It sort of depends.

How do I insert X-PAYPAL headers with Rails Invoice SDK?

I'm using the paypal SDK for invoicing located here:
https://github.com/paypal/invoice-sdk-ruby
This works great.
I integrated the paypal permissions SDK for rails:
https://github.com/paypal/permissions-sdk-ruby
The authorization workflow is working great.
So now I need to put them together. The documentation for the permissions sdk leaves off after you get your token. It doesn't explain how to use it with the other paypal SDKs (at least not so I could understand :D ) The invoice sdk tells you to see the Auth sdk.
Paypal tells me:
# Third-party Auth headers
-H "X-PAYPAL-SECURITY-SUBJECT:<receiverEdress>" # Merchant's PayPal e-mail
-H "X-PAYPAL-AUTHENTICATION:<OAuthSig>" # Generated OAuth Signature
Don't know how to insert that. The request is generated here in my model:
#create_and_send_invoice = api.build_create_and_send_invoice(paypalized || default_api_value)
The data itself is assembled in the invoice model like so:
paypalized = {
:access_token => self.user.paypal_token,
:invoice => {
:merchantEmail => self.user.paypal_email || self.user.email,
:payerEmail => self.client.email,
:itemList => #itemlist,
:currencyCode => "USD",
:paymentTerms => "DueOnReceipt",
:invoiceDate => self.updated_at,
:number => self.name,
:note => self.description,
:merchantInfo => #businessinfo
# project / Invoice title?
} # end invoice
} # end paypalized
return paypalized
This implementation is not working and the access_token field is being rejected. I looked through the gems associated with the sdks but can't see where the headers themselves are built or how to interact with that.
UPDATE: Found this which gives me a clue...
INVOICE_HTTP_HEADER = { "X-PAYPAL-REQUEST-SOURCE" => "invoice-ruby-sdk-#{VERSION}" }
This seems to be used here during calls in the paypal-sdk-invoice gem:
# Service Call: CreateAndSendInvoice
# #param CreateAndSendInvoiceRequest
# #return CreateAndSendInvoiceResponse
def CreateAndSendInvoice(options = {} , http_header = {})
request_object = BuildCreateAndSendInvoice(options)
request_hash = request_object.to_hash
...
I notice that there's two arguments: options and http_header. It's possible I can modify the http_header argument and pass it this way in my controller:
#create_and_send_invoice_response = api.create_and_send_invoice(#create_and_send_invoice, #cutsom_header)
or maybe
#create_and_send_invoice = api.build_create_and_send_invoice(data, custom_header)
I'll keep this updated since I googled around a lot and couldn't find any clear answers on how to do this...
You have to pass the token and token_secret while creating API object for third-party authentication.
#api = PayPal::SDK::Invoice::API.new({
:token => "replace with token",
:token_secret => "replace with token-secret" })

Make API Request, puts response in RAILS 4.0.0

I have been using ruby to make API calls and operating strictly in the terminal for some time. I am now in the process of learning more about rails and trying to get out of my terminal. How can I, using rails 4.0, put a variable to the screen from an already existing .rb file? I am confused as to where I should write the API request to get the variable- Is it a controller, can I write it directly in a view, etc.
Sample idea:
#test.rb
call= "/api/v2/surveys/"
auth = {:username => "test", :password => "password"}
url = HTTParty.get("https://surveys.com#{call}",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' } )
response = JSON.parse(url.body)
survey_ids = response["surveys"].map { |s| s["id"] }
survey_ids.each do |i|
puts i
end
That is a sample .rb script I already have. The difference is I would like for puts i to happen on a web app when a page is loaded instead of me running the script in my terminal. What would I use in rails to make that happen?
It depends entirely on how your application is going to be set up but here's a basic example:
Say you have a Survey model:
class Survey < ActiveRecord::Base
attr_accessible :survey_id
end
You can place your call for a list of surveys (I'm assuming that's what your code does) in the SurveysController:
class SurveysController < ApplicationController
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def pull_surveys
call= "/api/v2/surveys/"
auth = {:username => "test", :password => "password"}
url = HTTParty.get("https://surveys.com#{call}",
:basic_auth => auth,
:headers => { 'ContentType' => 'application/json' } )
response = JSON.parse(url.body)
survey_ids = response["surveys"].map { |s| s["id"] }
survey_ids.each do |i|
Survey.create(survey_id: i)
end
end
After calling the pull_surveys method, you'll actually have surveys your view can load so in your views for the Survey Model you can use #surveys or #survey (depending on which view you're in) and serve up whatever you want (e.g #survey.survey_id in show to show that specific survey's ID).
Note that you'll want to be careful about where you place your API call methods - I placed it in the controller for simplicity's sake but you may not want to do this.
There's lots of useful info in the rails guides to get you started: http://guides.rubyonrails.org/index.html

Rails delayed_job argument error with mailer

I'm getting a "wrong number of arguments (2 for 1)" error when I try send a mailer to delayed_job. Everything works perfect if I don't try to push it to the background. Below is my controller with delayed_job:
def export
#user = User.where("id = ?", params[:user_id])
#logs = VehicleMileage.export_logs(params, #user)
if #logs['log_count'] > 0
ExportLogsMailer.delay.email_logs(#user, #logs['sending_to'])
end
respond_to do |format|
formats # index.html.erb
format.json { render json: #logs }
end
end
When I ExportLogsMailer.email_logs(#user, #logs['sending_to']).deliver the mailer works fine. I'm using the gem 'delayed_job_active_record' and gem 'rails', '3.2.13'.
Here is what ExportLogsMailer looks like:
class ExportLogsMailer < ActionMailer::Base
default :from => "support#myconsultantapp.com"
def email_logs(user, emails)
# make sure emails are unique
emails.uniq
first_name = user[0].first_name
last_name = user[0].last_name
# encoded_content = Base64.strict_encode64(File.read("#{Rails.root}/tmp/test.xls"))
# puts encoded_content
# attachments['test.xls'] = { :content => encoded_content,
# :encoding => 'Base64'
# }
# Name of template in your Mandrill template area
headers['X-MC-Template'] = 'Export Logs'
# Tags help classify your messages
headers['X-MC-Tags'] = 'mileage logs'
# Enable open or click-tracking for the message.
# Only can track to at a time. Possibilies: opens, clicks, clicks_htmlonly, clicks_textonly
headers['X-MC-Track'] = 'opens, clicks'
# Automatically generate a plain-text version of the email from the HTML content.
headers['X-MC-Autotext'] = 'true'
# Add dynamic data to replace mergetags that appear in your message content. Should be a JSON-formatted
# object and flat, so if you more than one recipient then add another X-MC-MergeVars to the header. The
# below example will change anywhere where *|FNAME|* or *|LNAME|* to the respective value.
mergeVars =
{
"fname" => first_name,
"lname" => last_name
}
headers['X-MC-MergeVars'] = mergeVars.to_json
# Add Google Analytics tracking to links in your email for the specified domains.
headers['X-MC-GoogleAnalytics'] = 'http://www.myconsultantapp.com/'
# Add an optional value to be used for the utm_campaign parameter in Google Analytics tracked links.
# headers['X-MC-GoogleAnalyticsCampaign'] = ''
# Information about any custom fields or data you want to append to the message.
# Up to 200 bytes of JSON-encoded data as an object. The object should be flat; nested object structures are not supported.
# headers['X-MC-Metadata'] = ''
# Whether to strip querystrings from links for reporting. "true" or "false"
# headers['X-MC-URLStripQS'] = 'true'
# Whether to show recipients of the email other recipients, such as those in the "cc" field. "true" or "false"
headers['X-MC-PreserveRecipients'] = 'false'
message = prepare_message :subject => "1 myConsultant logs",
:to => emails,
:content_type => "multipart/mixed"
message.alternative_content_types_with_attachment(
:text => 'text',
:html => 'html'
) do |i|
i.inline['test.xls'] = File.read("#{Rails.root}/tmp/test.xls")
end
message
end
end
Any help would be greatly appreciated. Thanks you!
Are you trying to retrieve just one user? Then do:
#user = User.find(params[:user_id])
Otherwise, if you're trying to pass an array of objects to delayed_job, I think you need to add all at the end:
#objects = Model.where(...).all

Mr. Ruby, Ms Rails, my json created record is empty

this newbie here is smacking his head with webservices over Rails.
Perhaps someone could ease my pain?
I've created a simple rails app, and generated the scaffold MyRecords. Then I'm trying to create a record over irb with the code below :
testWS.rb
require 'HTTParty'
class MyRecordCreate
include HTTParty
base_uri 'localhost:3000'
def initialize(u, p)
#auth = {:username => u, :password => p}
end
def post(text)
options = { :body => { name:text} }
self.class.post('/my_records', options)
end
end
response = HTTParty.get("http://localhost:3000/my_records/new.json")
print response
record = MyRecordCreate.new("","").post("test remote record")
print record
With the code above, I managed to create a record. the thing is that my Record (which only has the column "name") is created with an empty name!
Any suggestions on this one?
I'm longing to slice this despair piece by piece.
Thank you for your contribute.
Try adding these two lines to your HTTParty class:
format :json
headers "Accept" => "application/json"
These tell httparty and the remote service to which it connects to send and receive JSON. For your example (with .json at the end of the URL) it isn't necessary to add the second line, but I find it is good practice and keep it anyway.
The next problem is that Rails expects your uploaded data to be inside the top level name of your object. So, for your example, the options line should look something like:
options = { :body => { :person => { :name => text } } }
Replace person with the name of the model that you are attempting to create.

Resources