lib/aws/rails/action_mailbox/sns_notification.rb



# frozen_string_literal: true

require 'aws-sdk-sns'
require 'aws/rails/action_mailbox/s3_client'
require 'aws/rails/action_mailbox/sns_message_verifier'

module Aws
  module Rails
    module ActionMailbox
      # @api private
      class SnsNotification
        class MessageContentError < StandardError; end

        def initialize(request_body)
          @request_body = request_body
        end

        def subscription_confirmed?
          (200..299).cover?(confirmation_response.code.to_i)
        end

        def verified?
          SnsMessageVerifier.verifier.authentic?(@request_body)
        end

        def topic
          notification.fetch(:TopicArn)
        end

        def type
          notification.fetch(:Type)
        end

        def message_content
          raise MessageContentError, 'Incoming emails must have notificationType `Received`' unless receipt?

          if content_in_s3?
            s3_content
          else
            return message[:content] unless destination

            "X-Original-To: #{destination}\n#{message[:content]}"
          end
        end

        private

        def notification
          @notification ||= JSON.parse(@request_body, symbolize_names: true)
        rescue JSON::ParserError => e
          Rails.logger.warn("Unable to parse SNS notification: #{e}")
          nil
        end

        def s3_content
          S3Client
            .client
            .get_object(key: key, bucket: bucket)
            .body
            .string
        end

        def message
          @message ||= JSON.parse(notification[:Message], symbolize_names: true)
        end

        def destination
          message.dig(:mail, :destination)&.first
        end

        def action
          return unless message[:receipt]

          message.fetch(:receipt).fetch(:action)
        end

        def bucket
          action.fetch(:bucketName)
        end

        def key
          action.fetch(:objectKey)
        end

        def content_in_s3?
          action&.fetch(:type) == 'S3'
        end

        def receipt?
          message.fetch(:notificationType) == 'Received'
        end

        def confirmation_response
          @confirmation_response ||= Net::HTTP.get_response(URI(notification[:SubscribeURL]))
        end
      end
    end
  end
end