Nest Action Mailbox classes in the API docs

This commit is contained in:
George Claghorn 2018-12-26 16:18:42 -05:00
parent 11a8ba1272
commit 6c168aaffb
19 changed files with 559 additions and 521 deletions

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true
module ActionMailbox
# The base class for all Active Mailbox ingress controllers.
class ActionMailbox::BaseController < ActionController::Base
class BaseController < ActionController::Base
skip_forgery_protection
before_action :ensure_configured
@ -34,3 +35,4 @@ class ActionMailbox::BaseController < ActionController::Base
Rails.application.credentials.dig(:action_mailbox, :ingress_password) || ENV["RAILS_INBOUND_EMAIL_PASSWORD"]
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
module ActionMailbox
# Ingests inbound emails from Amazon's Simple Email Service (SES).
#
# Requires the full RFC 822 message in the +content+ parameter. Authenticates requests by validating their signatures.
@ -29,7 +30,7 @@
# to deliver emails to your application via POST requests to +/rails/action_mailbox/amazon/inbound_emails+.
# If your application lived at <tt>https://example.com</tt>, you would specify the fully-qualified URL
# <tt>https://example.com/rails/action_mailbox/amazon/inbound_emails</tt>.
class ActionMailbox::Ingresses::Amazon::InboundEmailsController < ActionMailbox::BaseController
class Ingresses::Amazon::InboundEmailsController < BaseController
before_action :authenticate
cattr_accessor :verifier
@ -50,3 +51,4 @@ class ActionMailbox::Ingresses::Amazon::InboundEmailsController < ActionMailbox:
head :unauthorized unless verifier.authentic?(request.body)
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
module ActionMailbox
# Ingests inbound emails from Mailgun. Requires the following parameters:
#
# - +body-mime+: The full RFC 822 message
@ -41,7 +42,7 @@
#
# If your application lived at <tt>https://example.com</tt>, you would specify the fully-qualified URL
# <tt>https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime</tt>.
class ActionMailbox::Ingresses::Mailgun::InboundEmailsController < ActionMailbox::BaseController
class Ingresses::Mailgun::InboundEmailsController < ActionMailbox::BaseController
before_action :authenticate
def create
@ -99,3 +100,4 @@ class ActionMailbox::Ingresses::Mailgun::InboundEmailsController < ActionMailbox
end
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
module ActionMailbox
# Ingests inbound emails from Mandrill.
#
# Requires a +mandrill_events+ parameter containing a JSON array of Mandrill inbound email event objects.
@ -13,7 +14,7 @@
# - <tt>422 Unprocessable Entity</tt> if the request is missing required parameters
# - <tt>500 Server Error</tt> if the Mandrill API key is missing, or one of the Active Record database,
# the Active Storage service, or the Active Job backend is misconfigured or unavailable
class ActionMailbox::Ingresses::Mandrill::InboundEmailsController < ActionMailbox::BaseController
class Ingresses::Mandrill::InboundEmailsController < ActionMailbox::BaseController
before_action :authenticate
def create
@ -78,3 +79,4 @@ class ActionMailbox::Ingresses::Mandrill::InboundEmailsController < ActionMailbo
end
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
module ActionMailbox
# Ingests inbound emails relayed from Postfix.
#
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
@ -41,7 +42,7 @@
# If your application lived at <tt>https://example.com</tt>, the full command would look like this:
#
# URL=https://example.com/rails/action_mailbox/postfix/inbound_emails INGRESS_PASSWORD=... bin/rails action_mailbox:ingress:postfix
class ActionMailbox::Ingresses::Postfix::InboundEmailsController < ActionMailbox::BaseController
class Ingresses::Postfix::InboundEmailsController < ActionMailbox::BaseController
before_action :authenticate_by_password, :require_valid_rfc822_message
def create
@ -55,3 +56,4 @@ class ActionMailbox::Ingresses::Postfix::InboundEmailsController < ActionMailbox
end
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
module ActionMailbox
# Ingests inbound emails from SendGrid. Requires an +email+ parameter containing a full RFC 822 message.
#
# Authenticates requests using HTTP basic access authentication. The username is always +actionmailbox+, and the
@ -43,10 +44,11 @@
#
# *NOTE:* When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled *"Post the raw,
# full MIME message."* Action Mailbox needs the raw MIME message to work.
class ActionMailbox::Ingresses::Sendgrid::InboundEmailsController < ActionMailbox::BaseController
class Ingresses::Sendgrid::InboundEmailsController < ActionMailbox::BaseController
before_action :authenticate_by_password
def create
ActionMailbox::InboundEmail.create_and_extract_message_id! params.require(:email)
end
end
end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
class Rails::Conductor::ActionMailbox::InboundEmailsController < Rails::Conductor::BaseController
module Rails
class Conductor::ActionMailbox::InboundEmailsController < Rails::Conductor::BaseController
def index
@inbound_emails = ActionMailbox::InboundEmail.order(created_at: :desc)
end
@ -27,3 +28,4 @@ class Rails::Conductor::ActionMailbox::InboundEmailsController < Rails::Conducto
{ io: StringIO.new(mail.to_s), filename: "inbound.eml", content_type: "message/rfc822" }
end
end
end

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true
module Rails
# Rerouting will run routing and processing on an email that has already been, or attempted to be, processed.
class Rails::Conductor::ActionMailbox::ReroutesController < Rails::Conductor::BaseController
class Conductor::ActionMailbox::ReroutesController < Rails::Conductor::BaseController
def create
inbound_email = ActionMailbox::InboundEmail.find(params[:inbound_email_id])
reroute inbound_email
@ -15,3 +16,4 @@ class Rails::Conductor::ActionMailbox::ReroutesController < Rails::Conductor::Ba
inbound_email.route_later
end
end
end

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true
module Rails
# TODO: Move this to Rails::Conductor gem
class Rails::Conductor::BaseController < ActionController::Base
class Conductor::BaseController < ActionController::Base
layout "rails/conductor"
before_action :ensure_development_env
@ -10,3 +11,4 @@ class Rails::Conductor::BaseController < ActionController::Base
head :forbidden unless Rails.env.development?
end
end
end

View File

@ -1,11 +1,12 @@
# frozen_string_literal: true
module ActionMailbox
# You can configure when this `IncinerationJob` will be run as a time-after-processing using the
# `config.action_mailbox.incinerate_after` or `ActionMailbox.incinerate_after` setting.
#
# Since this incineration is set for the future, it'll automatically ignore any `InboundEmail`s
# that have already been deleted and discard itself if so.
class ActionMailbox::IncinerationJob < ActiveJob::Base
class IncinerationJob < ActiveJob::Base
queue_as { ActionMailbox.queues[:incineration] }
discard_on ActiveRecord::RecordNotFound
@ -18,3 +19,4 @@ class ActionMailbox::IncinerationJob < ActiveJob::Base
inbound_email.incinerate
end
end
end

View File

@ -1,11 +1,13 @@
# frozen_string_literal: true
module ActionMailbox
# Routing a new InboundEmail is an asynchronous operation, which allows the ingress controllers to quickly
# accept new incoming emails without being burdened to hang while they're actually being processed.
class ActionMailbox::RoutingJob < ActiveJob::Base
class RoutingJob < ActiveJob::Base
queue_as { ActionMailbox.queues[:routing] }
def perform(inbound_email)
inbound_email.route
end
end
end

View File

@ -2,6 +2,7 @@
require "mail"
module ActionMailbox
# The `InboundEmail` is an Active Record that keeps a reference to the raw email stored in Active Storage
# and tracks the status of processing. By default, incoming emails will go through the following lifecycle:
#
@ -23,7 +24,7 @@ require "mail"
#
# inbound_email.mail.from # => 'david@loudthinking.com'
# inbound_email.source # Returns the full rfc822 source of the email as text
class ActionMailbox::InboundEmail < ActiveRecord::Base
class InboundEmail < ActiveRecord::Base
self.table_name = "action_mailbox_inbound_emails"
include Incineratable, MessageId, Routable
@ -43,3 +44,4 @@ class ActionMailbox::InboundEmail < ActiveRecord::Base
delivered? || failed? || bounced?
end
end
end

View File

@ -1,10 +1,11 @@
# frozen_string_literal: true
module ActionMailbox
# Command class for carrying out the actual incineration of the `InboundMail` that's been scheduled
# for removal. Before the incineration which really is just a call to `#destroy!` is run, we verify
# that it's both eligible (by virtue of having already been processed) and time to do so (that is,
# the `InboundEmail` was processed after the `incinerate_after` time).
class ActionMailbox::InboundEmail::Incineratable::Incineration
class InboundEmail::Incineratable::Incineration
def initialize(inbound_email)
@inbound_email = inbound_email
end
@ -22,3 +23,4 @@ class ActionMailbox::InboundEmail::Incineratable::Incineration
@inbound_email.processed?
end
end
end

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
class Mail::Address
module Mail
class Address
def ==(other_address)
other_address.is_a?(Mail::Address) && to_s == other_address.to_s
end
end
end

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
class Mail::Address
module Mail
class Address
def self.wrap(address)
address.is_a?(Mail::Address) ? address : Mail::Address.new(address)
end
end
end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
class Mail::Message
module Mail
class Message
def from_address
header[:from]&.address_list&.addresses&.first
end
@ -25,3 +26,4 @@ class Mail::Message
Array(header[:x_original_to]).collect { |header| Mail::Address.new header.to_s }
end
end
end

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
class Mail::Message
module Mail
class Message
def recipients
Array(to) + Array(cc) + Array(bcc) + Array(header[:x_original_to]).map(&:to_s)
end
end
end

View File

@ -1,8 +1,9 @@
# frozen_string_literal: true
module ActionMailbox
# Encapsulates the routes that live on the ApplicationMailbox and performs the actual routing when
# an inbound_email is received.
class ActionMailbox::Router
class Router
class RoutingError < StandardError; end
def initialize
@ -36,5 +37,6 @@ class ActionMailbox::Router
routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class)
end
end
end
require "action_mailbox/router/route"

View File

@ -1,9 +1,10 @@
# frozen_string_literal: true
module ActionMailbox
# Encapsulates a route, which can then be matched against an inbound_email and provide a lookup of the matching
# mailbox class. See examples for the different route addresses and how to use them in the `ActionMailbox::Base`
# documentation.
class ActionMailbox::Router::Route
class Router::Route
attr_reader :address, :mailbox_name
def initialize(address, to:)
@ -38,3 +39,4 @@ class ActionMailbox::Router::Route
end
end
end
end