diff --git a/README.md b/README.md index 5af695d..eca45a4 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ Courrier supports these transactional email providers: - [Loops](https://loops.so) - [Mailgun](https://mailgun.com) - [MailPace](https://mailpace.com) +- [Mailtrap](https://mailtrap.io) - [Postmark](https://postmarkapp.com) - [Resend](https://resend.com) - [SendGrid](https://sendgrid.com) diff --git a/lib/courrier/email/provider.rb b/lib/courrier/email/provider.rb index 7de7e4d..ab53b7b 100644 --- a/lib/courrier/email/provider.rb +++ b/lib/courrier/email/provider.rb @@ -9,6 +9,7 @@ require "courrier/email/providers/mailgun" require "courrier/email/providers/mailjet" require "courrier/email/providers/mailpace" +require "courrier/email/providers/mailtrap" require "courrier/email/providers/postmark" require "courrier/email/providers/resend" require "courrier/email/providers/ses" @@ -29,6 +30,7 @@ class Provider mailgun: Courrier::Email::Providers::Mailgun, mailjet: Courrier::Email::Providers::Mailjet, mailpace: Courrier::Email::Providers::Mailpace, + mailtrap: Courrier::Email::Providers::Mailtrap, postmark: Courrier::Email::Providers::Postmark, resend: Courrier::Email::Providers::Resend, ses: Courrier::Email::Providers::Ses, diff --git a/lib/courrier/email/providers/mailtrap.rb b/lib/courrier/email/providers/mailtrap.rb new file mode 100644 index 0000000..d92b647 --- /dev/null +++ b/lib/courrier/email/providers/mailtrap.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Courrier + class Email + module Providers + class Mailtrap < Base + ENDPOINT_URL = "https://send.api.mailtrap.io/api/send" + + def body + { + "from" => email_address(@options.from), + "to" => email_addresses(@options.to), + "cc" => email_addresses(@options.cc), + "bcc" => email_addresses(@options.bcc), + "reply_to" => email_address(@options.reply_to), + "subject" => @options.subject, + "text" => @options.text, + "html" => @options.html + }.compact + end + + private + + def default_headers = {"Api-Token" => @api_key} + + def email_addresses(addresses) + addresses&.split(",")&.map { |address| email_address(address) } + end + + def email_address(address) + {"email" => address.strip} if address + end + end + end + end +end diff --git a/test/courrier/email/providers/mailtrap_test.rb b/test/courrier/email/providers/mailtrap_test.rb new file mode 100644 index 0000000..bb56fae --- /dev/null +++ b/test/courrier/email/providers/mailtrap_test.rb @@ -0,0 +1,61 @@ +require "test_helper" + +module Courrier::Email::Providers + class MailtrapTest < Minitest::Test + def setup + email = TestEmail.new( + from: "devs@railsdesigner.com", + to: "first@example.com, second@example.com", + reply_to: "support@railsdesigner.com", + cc: "copy@example.com", + bcc: "archive@example.com" + ) + + @provider = Mailtrap.new(api_key: "test_key", options: email.options) + end + + def test_formats_transactional_email + assert_equal( + { + "from" => {"email" => "devs@railsdesigner.com"}, + "to" => [{"email" => "first@example.com"}, {"email" => "second@example.com"}], + "cc" => [{"email" => "copy@example.com"}], + "bcc" => [{"email" => "archive@example.com"}], + "reply_to" => {"email" => "support@railsdesigner.com"}, + "subject" => "Test Subject", + "text" => "Test Body", + "html" => "

Test HTML Body

" + }, + @provider.body + ) + end + + def test_omits_optional_addresses_when_not_set + email = TestEmail.new(from: "devs@railsdesigner.com", to: "first@example.com") + body = Mailtrap.new(api_key: "test_key", options: email.options).body + + refute_includes body.keys, "cc" + refute_includes body.keys, "bcc" + refute_includes body.keys, "reply_to" + end + + def test_authenticates_with_api_key + assert_equal({"Api-Token" => "test_key"}, @provider.send(:default_headers)) + end + + def test_is_available_through_provider_registry + mock_provider = Minitest::Mock.new + mock_provider.expect(:deliver, nil) + + Mailtrap.stub :new, mock_provider do + Courrier::Email::Provider.new( + provider: "mailtrap", + api_key: "test_key", + options: @provider.instance_variable_get(:@options) + ).deliver + end + + mock_provider.verify + end + end +end