Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API endpoints to SentEmailViewerPlug #456

Merged
merged 1 commit into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions lib/bamboo/plug/sent_email_api/sent_email_api_plug.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
defmodule Bamboo.SentEmailApiPlug do
use Plug.Router
alias Bamboo.SentEmail

@moduledoc """
tomtaylor marked this conversation as resolved.
Show resolved Hide resolved
A plug that can be used in your router to expose delivered emails over a
JSON API. This is useful for an integration test runner that doesn't have
access to the `Bamboo.Test` helpers, but needs to be able to assert that
emails have been delivered.

You must use the `Bamboo.LocalAdapter`, or no emails will be available.

## Using with Plug or Phoenix

# Make sure you are using Bamboo.LocalAdapter in your config
config :my_app, MyApp.Mailer,
adapter: Bamboo.LocalAdapter

# In your Router
defmodule MyApp.Router do
use Phoenix.Router # or use Plug.Router if you're not using Phoenix

if Mix.env == :e2e do
# If using Phoenix
forward "/sent_emails_api", Bamboo.SentEmailApiPlug

# If using Plug.Router, make sure to add the `to`
forward "/sent_emails_api", to: Bamboo.SentEmailApiPlug
end
end

## API endpoints

There are two endpoints:

* `GET /emails.json` exposes the list of sent emails
* `POST /reset.json` resets the sent emails back to an empty list

Usually, you would `POST` to `/reset.json` at the beginning of a test run,
and `GET` from `/emails.json` as you need to during an integration test to
make assertions. The details of how you make assertions is left up to your
integration test runner.

## Email response JSON

Sent emails are returned in the following format:

```json
{
"to": [[null, "to@example.com"], ["Alice", "alice@example.com"]],
"text_body": "hello world",
"subject": "This is a test email",
"html_body": "<p>hello world</p>",
"headers": {},
"from": [null, "from@example.com"],
"cc": [[null, "cc@example.com"]],
"bcc": [["Bob", "bob@example.com"]],
"attachments": []
}
```

Email addresses are returned in array pairs, with the first element as the
name, and the second as the email address. The first element can be `null`
if the address didn't specify a name. The `to`, `cc` and `bcc` fields are
returned as arrays of these pairs.
"""

plug(:match)
plug(:dispatch)

get "/emails.json" do
data =
SentEmail.all()
|> Enum.map(&format_json_email/1)

conn
|> send_json(:ok, data)
end

post "/reset.json" do
SentEmail.reset()

conn
|> send_json(:ok, %{ok: true})
end

defp send_json(conn, status, data) do
body = data |> Bamboo.json_library().encode!()

conn
|> Plug.Conn.put_resp_content_type("application/json")
|> send_resp(status, body)
end

defp format_json_email(email) do
email
|> Map.take([:from, :to, :cc, :bcc, :subject, :text_body, :html_body, :headers, :attachments])
|> Map.update(:from, nil, &format_json_email_address/1)
|> Map.update(:to, nil, &format_json_email_addresses/1)
|> Map.update(:cc, nil, &format_json_email_addresses/1)
|> Map.update(:bcc, nil, &format_json_email_addresses/1)
end

defp format_json_email_addresses(addresses) do
addresses |> Enum.map(&format_json_email_address/1)
end

defp format_json_email_address({name, address}) do
[name, address]
end
end
65 changes: 65 additions & 0 deletions test/lib/bamboo/plug/sent_email_api_plug_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
defmodule Bamboo.SentEmailApiPlugTest do
use ExUnit.Case
use Plug.Test
import Bamboo.Factory
alias Bamboo.SentEmail

defmodule AppRouter do
use Plug.Router

plug(:match)
plug(:dispatch)

forward("/", to: Bamboo.SentEmailApiPlug)
end

setup do
SentEmail.reset()
:ok
end

test "list emails over API" do
normalize_and_push(:html_email,
from: "from@example.com",
to: ["to@example.com", {"Alice", "alice@example.com"}],
cc: "cc@example.com",
bcc: {"Bob", "bob@example.com"},
subject: "This is a test email",
html_body: "<p>hello world</p>",
text_body: "hello world"
)

conn = conn(:get, "/emails.json") |> AppRouter.call(nil)

assert conn.status == 200
assert {"content-type", "application/json; charset=utf-8"} in conn.resp_headers

json = Bamboo.json_library().decode!(conn.resp_body)

assert Enum.count(json) == 1

first_email = json |> Enum.at(0)
assert first_email["from"] == [nil, "from@example.com"]
assert first_email["to"] == [[nil, "to@example.com"], ["Alice", "alice@example.com"]]
assert first_email["cc"] == [[nil, "cc@example.com"]]
assert first_email["bcc"] == [["Bob", "bob@example.com"]]
assert first_email["subject"] == "This is a test email"
assert first_email["html_body"] == "<p>hello world</p>"
assert first_email["text_body"] == "hello world"
assert first_email["headers"] == %{}
end

test "reset emails over API" do
normalize_and_push(:html_email)
assert SentEmail.all() |> Enum.count() == 1

conn = conn(:post, "/reset.json") |> AppRouter.call(nil)

assert conn.status == 200
assert {"content-type", "application/json; charset=utf-8"} in conn.resp_headers

json = Bamboo.json_library().decode!(conn.resp_body)
assert json == %{"ok" => true}
assert SentEmail.all() |> Enum.count() == 0
end
end