strongqa/howitzer

View on GitHub
lib/howitzer/mail_adapters/mailgun.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'howitzer/mailgun_api/connector'
require 'howitzer/exceptions'
require 'howitzer/mail_adapters/abstract'

module Howitzer
  module MailAdapters
    # This class represents Mailgun mail adapter
    class Mailgun < Abstract
      # Finds an email in storage
      # @note emails are stored for 3 days only!
      # @param recipient [String] an email
      # @param subject [String]
      # @param wait [Integer] how much time is required to wait an email
      # @raise [EmailNotFoundError] if blank message

      def self.find(recipient, subject, wait:)
        message = {}
        retryable(find_retry_params(wait)) { message = retrieve_message(recipient, subject) }
        return new(message) if message.present?

        raise Howitzer::EmailNotFoundError,
              "Message with subject '#{subject}' for recipient '#{recipient}' was not found."
      end

      # @return [String] plain text body of the email message

      def plain_text_body
        message['body-plain']
      end

      # @return [String] html body of the email message

      def html_body
        message['stripped-html']
      end

      # @return [String] stripped text

      def text
        message['stripped-text']
      end

      # @return [String] an email address specified in `From` field

      def mail_from
        message['From']
      end

      # @return [String] recipient emails separated with `, `

      def recipients
        message['To'].split ', '
      end

      # @return [String] when email was received

      def received_time
        message['Received'][/\w+, \d+ \w+ \d+ \d+:\d+:\d+ -\d+ \(\w+\)$/]
      end

      # @return [String] a real sender email address

      def sender_email
        message['sender']
      end

      # @return [Array] attachments

      def mime_part
        message['attachments']
      end

      # @raise [NoAttachmentsError] if no attachments present
      # @return [Array] attachments

      def mime_part!
        files = mime_part
        return files if files.present?

        raise Howitzer::NoAttachmentsError, 'No attachments were found.'
      end

      def self.events
        MailgunApi::Connector.instance.client.get(
          "#{MailgunApi::Connector.instance.domain}/events", params: { event: 'stored' }
        )
      end
      private_class_method :events

      def self.event_by(recipient, subject)
        events.to_h['items'].find do |hash|
          hash['message']['recipients'].first == recipient && subject === hash['message']['headers']['subject']
        end
      end
      private_class_method :event_by

      def self.find_retry_params(wait)
        {
          timeout: wait || Howitzer.try(:mailgun_idle_timeout),
          sleep: Howitzer.mail_sleep_time || Howitzer.mailgun_sleep_time,
          silent: true,
          logger: Howitzer::Log,
          on: Howitzer::EmailNotFoundError
        }
      end
      private_class_method :find_retry_params

      def self.retrieve_message(recipient, subject)
        event = event_by(recipient, subject)
        raise Howitzer::EmailNotFoundError, 'Message not received yet, retry...' unless event

        message_url = event['storage']['url']
        MailgunApi::Connector.instance.client.get_url(message_url).to_h
      end
      private_class_method :retrieve_message
    end
  end
end