Repository: swoosh/swoosh
Pull request #897 (2024-04-29 06:31:35)

Add config options for AmazonSES adapter
Implements support for `SourceArn`, `FromArn` and `ReturnPathArn` [request parameters](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html#API_SendRawEmail_RequestParameters) in the AmazonSES adapter. 

These parameters are necessary for scenarios such as sending emails across different AWS accounts where the sender identity is managed in a separate account (which is the use case I'm facing now).

I used this PR as a reference: https://github.com/swoosh/swoosh/pull/846 where the `Source` parameter was set as a config option. Other SES API request parameters `ConfigurationSetName` and `Tags` are passed as provider options. For consistency it might be better to use provider options for all of the request parameters. Maybe that could be another PR if we decide so?  




Patch:
diff --git a/lib/swoosh/adapters/amazon_ses.ex b/lib/swoosh/adapters/amazon_ses.ex
index 6d3898c..743d318 100644
--- a/lib/swoosh/adapters/amazon_ses.ex
+++ b/lib/swoosh/adapters/amazon_ses.ex
@@ -41,9 +41,14 @@ defmodule Swoosh.Adapters.AmazonSES do
 
   ### 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.
+  The following [request parameters](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html#API_SendRawEmail_RequestParameters) can be set in the configuration:
+
+  * `:ses_source` - mapped to `Source` parameter in the API request
+  * `:ses_source_arn` - mapped to `SourceArn` parameter in the API request
+  * `:ses_from_arn` - mapped to `FromArn` parameter in the API request
+  * `:ses_return_path_arn` - mapped to `ReturnPathArn` parameter in the API request
+
+  See details on how to use the parameters from [the SES API documentation](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html#API_SendRawEmail_RequestParameters).
 
   ## Examples
 
@@ -154,6 +159,9 @@ defmodule Swoosh.Adapters.AmazonSES do
     |> Map.put("Version", Keyword.get(config, :version, @version))
     |> Map.put("RawMessage.Data", generate_raw_message_data(email, config))
     |> prepare_source(config)
+    |> prepare_source_arn(config)
+    |> prepare_from_arn(config)
+    |> prepare_return_path_arn(config)
     |> prepare_configuration_set_name(email)
     |> prepare_tags(email)
   end
@@ -175,6 +183,27 @@ defmodule Swoosh.Adapters.AmazonSES do
     end
   end
 
+  defp prepare_source_arn(body, config) do
+    case Keyword.fetch(config, :ses_source_arn) do
+      {:ok, source} -> Map.put(body, "SourceArn", source)
+      :error -> body
+    end
+  end
+
+  defp prepare_from_arn(body, config) do
+    case Keyword.fetch(config, :ses_from_arn) do
+      {:ok, from} -> Map.put(body, "FromArn", from)
+      :error -> body
+    end
+  end
+
+  defp prepare_return_path_arn(body, config) do
+    case Keyword.fetch(config, :ses_return_path_arn) do
+      {:ok, return_path} -> Map.put(body, "ReturnPathArn", return_path)
+      :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 d1ac14c..aa85a54 100644
--- a/test/swoosh/adapters/amazonses_test.exs
+++ b/test/swoosh/adapters/amazonses_test.exs
@@ -163,17 +163,30 @@ 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")
+  test "optional config params are present in the API request body when they're set in the config",
+       %{
+         bypass: bypass,
+         config: config,
+         valid_email: email
+       } do
+    config =
+      config ++
+        [
+          ses_source: "aaa@bbb.com",
+          ses_source_arn: "arn:aws:ses:us-east-1:123:identity/source.example.com",
+          ses_from_arn: "arn:aws:ses:us-east-1:123:identity/from.example.com",
+          ses_return_path_arn: "arn:aws:ses:us-east-1:123:identity/return.example.com"
+        ]
 
     Bypass.expect(bypass, fn conn ->
       conn = parse(conn)
 
-      assert {:ok, "aaa@bbb.com"} = Map.fetch(conn.body_params, "Source")
+      assert %{
+               "Source" => "aaa@bbb.com",
+               "SourceArn" => "arn:aws:ses:us-east-1:123:identity/source.example.com",
+               "FromArn" => "arn:aws:ses:us-east-1:123:identity/from.example.com",
+               "ReturnPathArn" => "arn:aws:ses:us-east-1:123:identity/return.example.com"
+             } = conn.body_params
 
       Plug.Conn.resp(conn, 200, @success_response)
     end)
@@ -181,15 +194,19 @@ defmodule Swoosh.Adapters.AmazonSESTest do
     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
+  test "optional config params are not present in the API request body when they're not set in the config",
+       %{
+         bypass: bypass,
+         config: config,
+         valid_email: email
+       } do
     Bypass.expect(bypass, fn conn ->
       conn = parse(conn)
 
       assert :error = Map.fetch(conn.body_params, "Source")
+      assert :error = Map.fetch(conn.body_params, "SourceArn")
+      assert :error = Map.fetch(conn.body_params, "FromArn")
+      assert :error = Map.fetch(conn.body_params, "ReturnPathArn")
 
       Plug.Conn.resp(conn, 200, @success_response)
     end)