Permalink
Browse files

Accepts incoming email requests to the API and queues them up for del…

…ivery
  • Loading branch information...
1 parent b01e9d6 commit 156330e62a9aaf8530e1098d339b06bc9a0151e3 @revans committed Apr 3, 2012
@@ -0,0 +1,2 @@
+class Api::ApplicationController < ActionController::Base
+end
@@ -0,0 +1,14 @@
+class Api::V1::MailRoomController < Api::ApplicationController
+
+ def create
+ @mail = ValidateMail.new(params[:mail]).validate_it!
+
+ if @mail.has_errors?
+ render(json: @mail, status: 422)
+ else
+ ::MailRoom.prepare_for_delivery(params[:mail])
+ render(json: {errors: [], success: true}, status: 202)
+ end
+ end
+
+end
@@ -0,0 +1,4 @@
+module Mailman
+ class PostEmail
+ end
+end
@@ -0,0 +1,31 @@
+class ValidateMail
+ VALIDATION_ATTRIBUTES = %w(recipient sender subject body)
+
+ def initialize(mail)
+ @mail = mail
+ @response = {errors: []}
+ end
+
+ def validate_it!
+ validate_mail_attributes
+ self
+ end
+
+ def has_errors?
+ !@response[:errors].empty?
+ end
+
+ def errors
+ @response[:errors]
+ end
+
+ private
+
+ def validate_mail_attributes
+ VALIDATION_ATTRIBUTES.each do |attr|
+ if @mail[attr.to_sym] == '' || @mail[attr.to_sym].nil?
+ @response[:errors] << "#{attr.capitalize} is missing and is required."
+ end
+ end
+ end
+end
View
@@ -0,0 +1,55 @@
+class MailRoom < ActiveRecord::Base
+ attr_accessible :recipient, :sender, :subject, :body
+
+ validates_presence_of :sender, :subject, :body
+ validates_presence_of :recipient, message: 'You must specify a recipient'
+
+ def recipient=(values)
+ unless values.nil?
+ values = values.split if values.is_a?(String)
+ write_attribute(:recipient, values.join(','))
+ end
+ end
+
+ # NOTE: Not using 'update_attributes' because I don't want
+ # to expose the sent_at attribute
+ def mark_as_sent!
+ self.sent_at = Time.now.utc
+ self.status = 'sent'
+ self.save!
+ end
+
+ # NOTE: Not using create! because I do not want to expose
+ # the status attribute
+ def self.prepare_for_delivery(mail)
+ queue = new(
+ recipient: mail[:recipient],
+ sender: mail[:sender],
+ subject: mail[:subject],
+ body: mail[:body])
+ queue.status = 'pending'
+ queue.save!
+ queue
+ end
+
+ def self.queued_for_delivery
+ where(status: 'pending')
+ end
+
+ def is_pending?
+ status == 'pending'
+ end
+
+ def was_sent?
+ status == 'sent'
+ end
+
+ def was_sent_on
+ sent_at
+ end
+
+ # NOTE: Required because of validation - move to a presenter?
+ def has_errors?
+ false
+ end
+end
View
@@ -1,4 +1,10 @@
Mailman::Application.routes.draw do
+ namespace :api do
+ namespace :v1 do
+ post 'send_email' => 'mail_room#create', as: :send_email
+ end
+ end
+
# The priority is based upon order of creation:
# first created -> highest priority.
@@ -0,0 +1,14 @@
+class CreateMailRoom < ActiveRecord::Migration
+ def up
+ create_table :mail_rooms do |t|
+ t.string :recipient, :sender, :subject
+ t.string :status, default: 'pending'
+ t.text :body
+ t.datetime :sent_at, default: nil
+ end
+ end
+
+ def down
+ drop_table :mail_room
+ end
+end
View
@@ -11,6 +11,15 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 0) do
+ActiveRecord::Schema.define(:version => 20120402232031) do
+
+ create_table "mail_rooms", :force => true do |t|
+ t.string "recipient"
+ t.string "sender"
+ t.string "subject"
+ t.string "status", :default => "pending"
+ t.text "body"
+ t.datetime "sent_at"
+ end
end
View
@@ -6,6 +6,5 @@ task default: :spec
desc 'Run Specs'
Rspec::Core::RakeTask.new do |t|
t.rspec_opts = ['--color', '--format documentation', '--order rand']
- t.pattern = ['spec/**/*_spec.rb']
- t.pattern << 'spec/**/*_acceptance.rb'
+ t.pattern = 'spec/**/*_spec.rb'
end
@@ -0,0 +1,17 @@
+require_relative '../../app/mailman/post_email'
+
+describe Mailman::PostEmail do
+ context "posting an email" do
+ it 'will have a recipient email address'
+ it 'can have an array of recipient email addresses'
+ it 'will have a senders email address'
+ it 'will have subject line'
+ it 'will have a body'
+
+
+ it 'will error if no recipients are found'
+ it 'will error if no sender email address is given'
+ it 'will error if no subject is given'
+ it 'will error if no body is given'
+ end
+end
@@ -0,0 +1,49 @@
+require_relative '../../app/mailman/validate_mail'
+
+describe ValidateMail do
+ before do
+ @mail = {recipient: 'me@email.com', sender: 'you@email.com', subject: 'Hey', body: 'come home'}
+ end
+
+ context 'when the email is valid' do
+ it 'will return true' do
+ ValidateMail.new(@mail).validate_it!.should be_true
+ end
+
+ it '.has_errors?' do
+ mail = ValidateMail.new(@mail).validate_it!
+ mail.has_errors?.should be_false
+ end
+
+ end
+
+ context 'when the email is invalid' do
+ it 'will return an error if there is no recipient' do
+ mail = mail_setup(recipient: '')
+ mail.errors.should_not be_empty
+ mail.errors.should include 'Recipient is missing and is required.'
+ end
+
+ it 'will return an error if there is no sender' do
+ mail = mail_setup(sender: '')
+ mail.errors.should_not be_empty
+ mail.errors.should include 'Sender is missing and is required.'
+ end
+
+ it 'will return an error if there is no subject' do
+ mail = mail_setup(subject: '')
+ mail.errors.should_not be_empty
+ mail.errors.should include 'Subject is missing and is required.'
+ end
+
+ it 'will return an error if there is no body' do
+ mail = mail_setup(body: '')
+ mail.errors.should_not be_empty
+ mail.errors.should include 'Body is missing and is required.'
+ end
+
+ def mail_setup(options={})
+ ValidateMail.new(@mail.merge(options)).validate_it!
+ end
+ end
+end
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe MailRoom do
+ before do
+ @hash = {recipient: 'bob@example.com', sender: 'mary@example.com', subject: 'Hey There', body: 'long time no see'}
+ end
+
+ context "when validating the mail" do
+ it 'passes with all parameters' do
+ mail = MailRoom.new(@hash)
+ mail.should be_valid
+ end
+
+ it '.has_errors?' do
+ mail = MailRoom.new(@hash)
+ mail.has_errors?.should be_false
+ end
+
+
+ it 'will not pass when missing a recipient' do
+ mail = MailRoom.new(@hash.merge(recipient: ''))
+ mail.should_not be_valid
+ mail.should have(1).error_on(:recipient)
+ end
+
+ it 'will not pass when missing a sender' do
+ mail = MailRoom.new(@hash.merge(sender: ''))
+ mail.should_not be_valid
+ mail.should have(1).error_on(:sender)
+ end
+
+ it 'will not pass when missing the subject' do
+ mail = MailRoom.new(@hash.merge(subject: ''))
+ mail.should_not be_valid
+ mail.should have(1).error_on(:subject)
+ end
+
+ it 'will not pass when missing the body' do
+ mail = MailRoom.new(@hash.merge(body: ''))
+ mail.should_not be_valid
+ mail.should have(1).error_on(:body)
+ end
+
+ it 'will be valid when the recipients is an array' do
+ mail = MailRoom.create!(@hash.merge(recipient: ['bob@example.com', 'mary@example.com']))
+ mail.recipient.should == 'bob@example.com,mary@example.com'
+ end
+
+ end
+
+ context "when queuing up the mail" do
+ let(:mail) { MailRoom.prepare_for_delivery(@hash) }
+
+ it 'will set the status to "pending"' do
+ mail.is_pending?.should be_true
+ mail.was_sent_on.should be_nil
+ end
+
+ it 'can search for mail pending to be sent' do
+ MailRoom.queued_for_delivery.should include mail
+ end
+
+ it 'after processing status will be set to "sent" with a datetime stamp' do
+ mail.mark_as_sent!
+ mail.was_sent?.should be_true
+ mail.was_sent_on.should_not be_nil
+ end
+ end
+end
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe "Given there is an email post request" do
+ context "when that request is sent" do
+ it 'sends off the email when the parameters are valid' do
+ send_email_to('ernie@bert.com')
+ response.status.should eq(202)
+ end
+
+ context 'sends back an error if the parameters are not valid' do
+ it 'will error on no recipients' do
+ send_email_to('')
+ response.status.should eq(422)
+ response.body.should include 'Recipient is missing and is required.'
+ end
+
+ it 'will error on no sender' do
+ send_email_to('you@example.com', {sender: ''})
+ response.status.should eq(422)
+ response.body.should include 'Sender is missing and is required.'
+ end
+
+ it 'will error on no subject' do
+ send_email_to('you@example.com', {subject: ''})
+ response.status.should eq(422)
+ response.body.should include 'Subject is missing and is required.'
+ end
+
+ it 'will error on no body' do
+ send_email_to('you@example.com', {body: ''})
+ response.status.should eq(422)
+ response.body.should include 'Body is missing and is required.'
+ end
+
+ end
+ end
+
+ def send_email_to(recipients, options={})
+ to = {recipient: recipients}
+ mail = mail_options.merge(to).merge(options)
+ post '/api/v1/send_email', mail: mail, format: :json
+ end
+
+ def mail_options(options={})
+ {
+ sender: 'me@example.com',
+ subject: 'Hey There',
+ body: "Sgt. Pepper's lonely heard club band"
+ }
+ end
+end

0 comments on commit 156330e

Please sign in to comment.