Actionmailer SMTP Server Response - ruby-on-rails

When sending mail through actionmailer, the actionmailer gets a response from the SMTP server, when its ok, or when its wrong. Is there a way to retrieve this response after sending a mail?
Also when no errors are thrown by the SMTP server?
Our qmail mail server throws a handler id which we want to use for tracing e-mails.
As an example, the server response is this :
250 ok 1308235825 qp 17832

Set return_response: true in the smtp settings and call message.deliver! instead of deliver. This returns the SMTP server response, a Net::SMTP::Response, which contains the server response you're looking for.
If you need a log of all responses from the connection with the server, not just the final result, you'll need to dig into Net::SMTP.

Looking at the the source you can define an observer:
in base.rb
# Register an Observer which will be notified when mail is delivered.
# Either a class or a string can be passed in as the Observer. If a string is passed in
# it will be <tt>constantize</tt>d.
def register_observer(observer)
delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
Mail.register_observer(delivery_observer)
end
So you could use some code like this in an initialization file:
class MailObserver
def self.delivered_email(message)
logger_info "Sent Message: #{message}"
end
end
ActionMailer::Base.register_observer(MailObserver)
That will log sent mail and you can see if you can get the headers or response from the sent mail object.

Related

Rails API 422 Unprocessable Entity: No verification key available, heroku

I created a Rails API with a JWT authentication system and deployed it to Heroku. When I request the endpoints locally, all seems to be working fine but when I make requests to the live endpoints (i.e the Heroku deployed app) I get a: 422 Unprocessable Entity server error and the response body looks like this:
{
"message": "No verification key available"
}
The class responsible for encoding and decoding the auth token is defined as follows:
class JsonWebToken
# secret to encode and decode token
HMAC_SECRET = Rails.application.secrets.secret_key_base
def self.encode(payload, exp = 24.hours.from_now)
# set expiry to 24 hours from the creation time.
payload[:exp] = exp.to_i
# sign token with application secret
JWT.encode(payload, HMAC_SECRET)
end
def self.decode(token)
# get payload, first index in decoded Array
body = JWT.decode(token, HMAC_SECRET)[0]
HashWithIndifferentAccess.new body
# rescue from all decode errors
rescue JWT::DecodeError => e
# raise custom error to be handled by custom handler
raise ExceptionHandler::InvalidToken, e.message
end
end
I have an endpoint /signup where I can make a POST request to register a new user and POST /todos which is accessible and available only to registered users. Making a registration request works perfectly fine, but when I try to make the POST request to the /todos endpoint it raises an error.
The association between user and suit is 1:m respectively.
Please if you have any idea on how I can fix this, I'll be very grateful, thanks : ).
I finally figured a way out by altering the Rails.application.secrets.secret_key_base to Rails.application.secret_key_base. For a more detailed review on this please check out this link. Hopefully, this will help someone facing a similar issue.
This was also my problem. After checking out my json_web_token.rb file, I figured out that I had written the following line:
HMAC_SECRET = Rails.application.secrets.secret_key_base
There is an extra secrets reference, which is causing the problem. It should be:
HMAC_SECRET = Rails.application.secret_key_base
But as far as I'm concerned, you managed to figure it out yourself!

Why is my Twilio status callback never called again even with a retry policy set to all?

I'm working with Twilio to send SMS messages and I'm trying to configure it so that it would retry the status callback request if the endpoint returns an error (500):
require 'twilio-ruby'
#client = Twilio::REST::Client.new(account_sid, auth_token)
message = #client.messages.create(
from: '+...',
body: 'Test message',
to: '+...
status_callback: 'https://public-url/ack#rc=2&ct=1000&rp=all'
)
The endpoint I exposed always returns a 500 error, and I'm expecting Twilio to retry the endpoint 2 times. But this doesn't happen, it is never retrying the request.
Connection overrides are supposed to work with the message resource: https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides
Why would this not work? Am I missing something or is this just a bug?
Try adding, &sni=y. I know with Ngrok, I need to do this, to see the retries.
Connection Overrides

HTTP status code when sending email failed

Note: I have read this but I still don't know how to go about building the sending email function correctly, so I ask this question. I need to know the HTTP status code to use when email sending succeed/failed, or if that's not the right thing to do, the right thing to do.
A POST request to my rails app will send an email.
If the email sending failed, what HTTP status code should I return to the person who send the POST request in my JSON response?
def inform
delivered = true
begin
UserMailer.new_comment(current_user, other_user, #note).deliver_now
rescue Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError
delivered = false
end
if delivered
# I use :created here because email is created
render json: { delivered: true }.to_json, status: :created
else
# I use :service_unavailable here because email sending failed
render json: { delivered: false }.to_json, status: :service_unavailable
end
end
502
bad_gateway
Typically used for upstream server failure.
Here's some more info: https://airbrake.io/blog/http-errors/502-bad-gateway-error
a 502 Bad Gateway Error means that a server that is upstream to one that you (the client) are connecting to has run into trouble. In this scenario, this means that the server providing the 502 Bad Gateway Error is acting as a gateway
I would rather use code 424 https://www.rfc-editor.org/rfc/rfc4918#section-11.4
The 424 (Failed Dependency) status code means that the method could
not be performed on the resource because the requested action
depended on another action and that action failed. For example, if a
command in a PROPPATCH method fails, then, at minimum, the rest of
the commands will also fail with 424 (Failed Dependency).

How to send the status of WebSocket request in ActionCable

Is there any way to send a status of request to client in ActionCable Channels?
For example, if sent data is invalid (or some unknown exception were raised) I want to error text in response message, otherwise send 'status: ok'. What I have on client side now is subscriber's perform method which returns true in almost any circumstances as we can see from source code (github):
send: (data) ->
if #isOpen()
#webSocket.send(JSON.stringify(data))
true
else
false
In websocket-rails gem there were methods for this: trigger_failure and trigger_success.

How to parse request body from Amazon SNS Notification in rails

I'm setting up an endpoint in my rails 3.0 app to receive pushed notifications from an Amazon SNS service.
The request that is posted by Amazon has a JSON payload, but they set content-type on the request as "text/plain", which results in Rails not parsing out the body.
Example post request from Amazon's docs:
POST / HTTP/1.1
x-amz-sns-message-type: Notification
x-amz-sns-message-id: 22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324
x-amz-sns-topic-arn: arn:aws:sns:us-west-2:123456789012:MyTopic
x-amz-sns-subscription-arn: arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96
Content-Length: 773
Content-Type: text/plain; charset=UTF-8
Host: myhost.example.com
Connection: Keep-Alive
User-Agent: Amazon Simple Notification Service Agent
{
"Type" : "Notification",
"MessageId" : "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324",
"TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
"Subject" : "My First Message",
"Message" : "Hello world!",
"Timestamp" : "2012-05-02T00:54:06.655Z",
"SignatureVersion" : "1",
"Signature" : "EXAMPLEw6JRNwm1LFQL4ICB0bnXrdB8ClRMTQFGBqwLpGbM78tJ4etTwC5zU7O3tS6tGpey3ejedNdOJ+1fkIp9F2/LmNVKb5aFlYq+9rk9ZiPph5YlLmWsDcyC5T+Sy9/umic5S0UQc2PEtgdpVBahwNOdMW4JPwk0kAJJztnc=",
"SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
"UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96"
}
So in my controller I can use the request object and read request.body and parse it out myself, like so:
def receive_notification
if request.content_type =~ /text\/plain/
body = request.body.read.force_encoding("UTF-8")
params.merge(JSON.parse(body))
request.body.rewind
end
# ... go on with rest of controller stuff
end
Anyone got a better way to do this? Can we move it back up the chain so my controller isn't worrying about the request object? Is it a bad idea to write a middleware that runs before the parameter parsing and recognizes that a request is from Amazon (by User-Agent, or those custom headers), and changes the content type to "application/json"? (And how do you do that?)
I recently came across this issue. Most of the approaches mentioned on the web didn't work for me so I created a middleware. The middleware detects if the message comes from SNS (by looking for the type header) and forces the content_type to application/json.
In my case the message parameter was also JSON passed as a string (SNS message from an S3 bucket notification). This doesn't handle that case but could be extended to do so pretty easily.
# app/middleware/sns_content_type.rb
class SnsContentType
def initialize(app, message = "Response Time")
#app = app
end
def call(env)
env['CONTENT_TYPE'] = 'application/json' if env['HTTP_X_AMZ_SNS_MESSAGE_TYPE'].present?
#app.call(env)
end
end
Once you've created the middleware you need to install it like so:
# config/application.rb
config.middleware.insert_before ActionDispatch::ParamsParser, "SnsContentType"
This inserts the middleware just before the parameters are parsed meaning that the params parser will see the JSON content type.
Maybe try this https://stackoverflow.com/a/14664355
before_filter :set_default_response_format
private
def set_default_response_format
request.format = :json
end

Resources