Repository: swoosh/swoosh
Pull request #846 (2024-01-17 15:03:14)

AmazonSES: add :ses_source option to set Source API parameter
The SES API includes a [`Source` parameter](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html) than can be used to specify an email address that SES will use for bounce reports (equivalent to the `MAIL FROM` address when using SMTP).

This PR adds an optional `:ses_source` config option to the `AmazonSES` adapter (and by extension to the the `ExAwsAmazonSES` adapter). Providing the option sets the `Source` field in the API requests to the specified value.

---

Side note: I think it makes sense to have the adapter-specific API fields as options on the adapter, rather than adding them to the email. Even the existing `put_provider_option`s might be better as a adapter options, which can either be configured globally or passed in to `deliver`. Anyway, I need to set `Source`, so that's what this PR does 🙂

princemaple: Looks reasonable. Thanks 👍

Patch:
diff --git a/lib/swoosh/adapters/amazon_ses.ex b/lib/swoosh/adapters/amazon_ses.ex
index 32e5a9a..63fad02 100644
--- a/lib/swoosh/adapters/amazon_ses.ex
+++ b/lib/swoosh/adapters/amazon_ses.ex
@@ -29,6 +29,22 @@ defmodule Swoosh.Adapters.AmazonSES do
 
   [Amazon SES SendRawEmail Documentation](http://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html)
 
+  ## Configuration options
+
+  ### Required
+
+  Note that these are handled automatically if using `Swoosh.Adapters.ExAwsAmazonSES`.
+
+  * `:region` - the AWS region
+  * `:access_key` - the IAM access key
+  * `:secret` - the IAM secret
+
+  ### Optional
+
+  * `:ses_source` - An email address used for bounce reports. Will be set as the
+    `Source` [request parameter](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html#API_SendRawEmail_RequestParameters)
+     in the SES API. Note that this must correspond to a verified identity.
+
   ## Examples
 
       # config/config.exs
@@ -138,6 +154,7 @@ defmodule Swoosh.Adapters.AmazonSES do
     |> Map.put("Action", @action)
     |> Map.put("Version", Keyword.get(config, :version, @version))
     |> Map.put("RawMessage.Data", generate_raw_message_data(email, config))
+    |> prepare_source(config)
     |> prepare_configuration_set_name(email)
     |> prepare_tags(email)
   end
@@ -152,6 +169,13 @@ defmodule Swoosh.Adapters.AmazonSES do
 
   defp prepare_configuration_set_name(body, _email), do: body
 
+  defp prepare_source(body, config) do
+    case Keyword.fetch(config, :ses_source) do
+      {:ok, source} -> Map.put(body, "Source", source)
+      :error -> body
+    end
+  end
+
   defp prepare_tags(body, %{provider_options: %{tags: tags}}) do
     Map.merge(
       body,


Test patch:
diff --git a/test/swoosh/adapters/amazonses_test.exs b/test/swoosh/adapters/amazonses_test.exs
index da1b8f6..d1ac14c 100644
--- a/test/swoosh/adapters/amazonses_test.exs
+++ b/test/swoosh/adapters/amazonses_test.exs
@@ -163,6 +163,40 @@ defmodule Swoosh.Adapters.AmazonSESTest do
     assert AmazonSES.deliver(email, config) == {:ok, %{id: "messageId", request_id: "requestId"}}
   end
 
+  test ":ses_source set in config sets the Source API parameter", %{
+    bypass: bypass,
+    config: config,
+    valid_email: email
+  } do
+    config = Keyword.put(config, :ses_source, "aaa@bbb.com")
+
+    Bypass.expect(bypass, fn conn ->
+      conn = parse(conn)
+
+      assert {:ok, "aaa@bbb.com"} = Map.fetch(conn.body_params, "Source")
+
+      Plug.Conn.resp(conn, 200, @success_response)
+    end)
+
+    assert AmazonSES.deliver(email, config) == {:ok, %{id: "messageId", request_id: "requestId"}}
+  end
+
+  test ":ses_source not set in config does not set the Source API parameter", %{
+    bypass: bypass,
+    config: config,
+    valid_email: email
+  } do
+    Bypass.expect(bypass, fn conn ->
+      conn = parse(conn)
+
+      assert :error = Map.fetch(conn.body_params, "Source")
+
+      Plug.Conn.resp(conn, 200, @success_response)
+    end)
+
+    assert AmazonSES.deliver(email, config) == {:ok, %{id: "messageId", request_id: "requestId"}}
+  end
+
   test "a sent email that returns a api error parses correctly", %{
     bypass: bypass,
     config: config,