diff --git a/.formatter.exs b/.formatter.exs
new file mode 100644
index 0000000..a677202
--- /dev/null
+++ b/.formatter.exs
@@ -0,0 +1,7 @@
+[
+ inputs: [
+ "{mix, .formatter, .credo}.exs",
+ "{config,lib,test}/**/*.{ex,exs}"
+ ],
+ line_length: 100
+]
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
new file mode 100644
index 0000000..e947931
--- /dev/null
+++ b/.github/workflows/build-test.yml
@@ -0,0 +1,74 @@
+name: Elixir CI
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+
+jobs:
+ build_and_test:
+ name: Build and test
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ elixir: ["1.14.1"]
+ otp: ["25.1.1"]
+ cache: [1]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Set up Elixir
+ uses: erlef/setup-beam@v1
+ with:
+ elixir-version: ${{ matrix.elixir }}
+ otp-version: ${{ matrix.otp }}
+
+ - name: Restore dependencies cache
+ id: mix-deps-cache
+ uses: actions/cache@v2
+ with:
+ path: deps
+ key: ${{ runner.os }}-mix-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
+ restore-keys: ${{ runner.os }}-${{ matrix.cache }}-mix
+
+ - name: Restore build cache
+ uses: actions/cache@v1
+ with:
+ path: _build
+ key: cache-${{ runner.os }}-dialyzer_build-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
+ restore-keys: cache-${{ runner.os }}-${{ matrix.cache }}-dialyzer_build-
+
+ - name: Install Mix Dependencies
+ if: steps.mix-deps-cache.outputs.cache-hit != 'true'
+ run: |
+ mix local.rebar --force
+ mix local.hex --force
+ mix deps.get
+
+ - name: Credo
+ run: mix credo --strict
+
+ - name: Check formatting
+ run: mix format --check-formatted
+
+ - name: Restore PLT Cache
+ uses: actions/cache@v1
+ id: plt-cache
+ with:
+ path: plts
+ key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plts-${{ hashFiles('**/mix.lock') }}
+ restore-keys: cache-${{ runner.os }}-${{ matrix.cache }}-dialyzer_build-
+
+ - name: Create PLTs
+ if: steps.plt-cache.outputs.cache-hit != 'true'
+ run: |
+ mkdir -p plts
+ mix dialyzer --plt
+
+ - name: Run dialyzer
+ run: mix dialyzer
+
+ - name: Run tests
+ run: mix test
diff --git a/.gitignore b/.gitignore
index b322b03..a0f49de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@
erl_crash.dump
*.ez
*.swp
+/plts/*.plt
+/plts/*.plt.hash
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000..1c75547
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1,2 @@
+elixir 1.14.1-otp-25
+erlang 25.1.1
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2a5b7d1..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-language: elixir
-elixir:
-- 1.2.6
-- 1.3.4
-- 1.4.2
-after_success:
-- MIX_ENV=test mix coveralls.travis
-notifications:
- slack:
- secure: SPvOIJ1GcVbU4o4XIV7E0NajNrYYXlGYhk7MBOlsh9cSX5Y/eqVXyjVQu5WSuo+NrSlPeoN+lVd5D3hLjPyZMPP80WUjKMExBK1xHB3ZaXLqTprdtiJOn4cFEjEWQZSJ0je33A9p2L3MIQQtWN2I6u8VV6FYPMDJRrczBEoWliREfY2ETfOyAcvLbVP9UfzvGjI7/P9KWUHnlRAta5/uGqyIXQkF5NbVwD5j0qJgtW+yufXUuA4DZtvFfOBJUM68C8ZHl4lyTOfjECpwtBx7Rrkb/vq+Y7GT6ASi+hUNh1kfHJI8piUDvKjNhCXf/k5L/oMbfuVox6u9/L2grrcTrj+lHk3R041/URNI5lzNYZ4pAQFRjw+gkZ74kWXfmKAynF2htrtr+gO+fgzhWMuDKCdWyqUau11yezkkac45LQs62d73R6D2etHwPScBT1Au2CwAG7ZYdx/4ywEsykFmlutYIj5uJOgoqpdgYhOQVwzwd/p8IWEP3TIJv41xk4nECuRxacE8DKbHrNbDnTRKmV91qFGd6YmXLzZ6n2TGDgvZbykwzWo1MWdOO5vXtUw2CkQoWN+vm+16fVMciKxjsD801ub1FHuxu0ZNmo65WApK/HX2VpzzydrVMn6Oc+G3Ilqr2t4laywJPxs6ad3l19fmAnxnemrbEs6+hwlyXE8=
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..1457210
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "[elixir]": {
+ "editor.defaultFormatter": "JakeBecker.elixir-ls"
+ }
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7664a4f..8c7cd64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,34 @@
## Change Log
+# 0.7 (2022-11-30)
+
+- [FEATURE] Add `SparkPost.Template.list/1`
+- Upgrade elixir to 1.14.1
+- Upgrade dialyxir to ~>1.2
+- Upgrade poison to ~>5.0
+- Upgrade excoveralls to ~>0.15
+- Upgrade credo to ~>1.6
+- Upgrade ex_doc to ~>0.29
+
+# Older releases
+## v0.6 (2020-12-14) [unreleased]
+
+- [FEATURE] Add `SparkPost.Event.search_message_events/1`
+- [INTERNAL] Add Github Actions Workflow for CI
+- [INTERNAL] Add elixir formatter
+- [INTERNAL] Add credo
+- [INTERNAL] Add dialyzer
+- Update mix elixir version 1.2 -> 1.10.4
+- Update htttpoison 1.0 -> 1.7
+- Update poison 2.0 or 3.0 -> 4.0
+- Update mock 0.2 -> 0.3.5
+- Update excoveralls 0.5.7 -> 0.13
+- Update earmark 1.0.3 -> 1.4
+- Update ex_doc 0.14.3 -> 0.20
+- [FEATURE] Add `SparkPost.Template.create/1`
+- [FEATURE] Add `SparkPost.Template.update/2`
+- [FEATURE] Add `SparkPost.Template.delete/1`
+
## v0.5.2 (2018-05-14)
- Bumped httpoison dep to ~1.0 (thanks @jasongoodwin)
@@ -22,7 +51,7 @@
- Bumped default connection timeout to 30 secs
## v0.2.1 (2016/11/03)
-- Added support for `start_time`, `ip_pool` and `inline_css` transmission options
+- Added support for `start_time`, `ip_pool` and `inline_css` transmission options
- Updated deps to latest versions
## v0.2.0 (2016/08/25)
diff --git a/README.md b/README.md
index 8557a8d..ed02b9b 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,6 @@
# SparkPost Elixir Library
-[](https://travis-ci.org/SparkPost/elixir-sparkpost) [](https://coveralls.io/github/SparkPost/elixir-sparkpost?branch=master) [](http://slack.sparkpost.com)
-
The official [Elixir](http://elixir-lang.org/) package for the [SparkPost API](https://www.sparkpost.com/api).
Capabilities include:
@@ -67,13 +65,13 @@ end
```elixir
defmodule MyApp.Example do
alias SparkPost.{Content, Recipient, Transmission}
-
+
def send_message do
Transmission.send(%Transmission{
recipients: [ "you@example.com" ],
content: %Content.Inline{
subject: "Sending email from Elixir is awesome!",
- from: "elixir@sparkpostbox.com",
+ from: "elixir@sparkpostbox.com",
text: "Hi there!",
html: "
Hi there!
"
}
diff --git a/config/config.exs b/config/config.exs
index f6060fc..a01ef32 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,6 +1,6 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
-use Mix.Config
+import Config
config :sparkpost,
api_endpoint: "https://api.sparkpost.com/api/v1/",
diff --git a/examples/simplesend.exs b/examples/simplesend.exs
index 966321c..ba6c6e2 100644
--- a/examples/simplesend.exs
+++ b/examples/simplesend.exs
@@ -3,7 +3,7 @@ from = "Elixir SparkPost "
SparkPost.send(
to: to,
from: from,
- subject: "My first Elixir email",
+ subject: "My first Elixir email",
text: "This is the boring version of the email body",
html: "This is the tasty rich version of the email body."
)
diff --git a/lib/address.ex b/lib/address.ex
index 4af05ce..d61a9ad 100644
--- a/lib/address.ex
+++ b/lib/address.ex
@@ -17,24 +17,27 @@ defmodule SparkPost.Address do
- `%{name: ..., email: ...}`
"""
def to_address(email) when is_binary(email) do
- parse_address(email)
+ parse_address(email)
end
- def to_address(%{name: name, email: email})do
+ def to_address(%{name: name, email: email}) do
%__MODULE__{name: name, email: email}
end
- def to_address(%{email: email})do
+ def to_address(%{email: email}) do
%__MODULE__{email: email}
end
defp parse_address(addr) when is_binary(addr) do
case Regex.run(~r/\s*(.+)\s+<(.+@.+)>\s*$/, addr) do
- [_, name, email] -> %__MODULE__{ name: name, email: email }
- nil -> case Regex.run(~r/\s*(.+@.+)\s*$/, addr) do
- [_, email] -> %__MODULE__{ email: email }
- nil -> raise __MODULE__.FormatError, message: "Invalid email address: #{addr}"
- end
+ [_, name, email] ->
+ %__MODULE__{name: name, email: email}
+
+ nil ->
+ case Regex.run(~r/\s*(.+@.+)\s*$/, addr) do
+ [_, email] -> %__MODULE__{email: email}
+ nil -> raise __MODULE__.FormatError, message: "Invalid email address: #{addr}"
+ end
end
end
end
diff --git a/lib/content.ex b/lib/content.ex
index e5efc9c..b35a8b4 100644
--- a/lib/content.ex
+++ b/lib/content.ex
@@ -64,12 +64,10 @@ defmodule SparkPost.Content do
end
def to_content(%SparkPost.Content.Inline{} = content) do
- %{ content |
- from: SparkPost.Address.to_address(content.from)}
+ %{content | from: SparkPost.Address.to_address(content.from)}
end
def to_content(content) when is_map(content) do
- %{ struct(SparkPost.Content.Inline, content) |
- from: SparkPost.Address.to_address(content.from)}
+ %{struct(SparkPost.Content.Inline, content) | from: SparkPost.Address.to_address(content.from)}
end
end
diff --git a/lib/content/inline.ex b/lib/content/inline.ex
index 8e803f2..9598ed6 100644
--- a/lib/content/inline.ex
+++ b/lib/content/inline.ex
@@ -18,18 +18,19 @@ defmodule SparkPost.Content.Inline do
"""
defstruct from: :required,
- reply_to: nil,
- headers: nil,
- subject: :required,
- text: nil,
- html: nil,
- attachments: nil,
- inline_images: nil
+ reply_to: nil,
+ headers: nil,
+ subject: :required,
+ text: nil,
+ html: nil,
+ attachments: nil,
+ inline_images: nil
@doc """
Convert a raw "from" field into a %SparkPost.Address{} object.
"""
def convert_from_field(%SparkPost.Endpoint.Error{} = content), do: content
+
def convert_from_field(%__MODULE__{} = content) do
%{content | from: SparkPost.Address.to_address(content.from)}
end
diff --git a/lib/endpoint.ex b/lib/endpoint.ex
index 728c7ba..94cb089 100644
--- a/lib/endpoint.ex
+++ b/lib/endpoint.ex
@@ -15,7 +15,7 @@ defmodule SparkPost.Endpoint do
- `:get`
- `:head`
- `:options`
- - `:patch`
+ - `:patch`
- `:post`
- `:put`
- `endpoint`: SparkPost API endpoint as string ("transmissions", "templates", ...)
@@ -32,82 +32,99 @@ defmodule SparkPost.Endpoint do
"id" => "102258558346809186", "name" => "102258558346809186",
"state" => "Success"}, ...], status_code: 200}
"""
- def request(method, endpoint, body \\ %{}, headers \\ %{}, options \\ [], decode_results \\ true) do
+ def request(method, endpoint, body \\ %{}, headers \\ %{}, options \\ [], decode_results \\ true) do
url = Application.get_env(:sparkpost, :api_endpoint, @default_endpoint) <> endpoint
{:ok, request_body} = encode_request_body(body)
- request_headers = if method in [:get, :delete] do
+ request_headers =
+ if method in [:get, :delete] do
headers
else
Map.merge(headers, %{"Content-Type": "application/json"})
end
|> Map.merge(base_request_headers())
- request_options = options
- |> Keyword.put(:timeout, Application.get_env(:sparkpost, :http_timeout, 30000))
- |> Keyword.put(:recv_timeout, Application.get_env(:sparkpost, :http_recv_timeout, 8000))
+ request_options =
+ options
+ |> Keyword.put(:timeout, Application.get_env(:sparkpost, :http_timeout, 30_000))
+ |> Keyword.put(:recv_timeout, Application.get_env(:sparkpost, :http_recv_timeout, 8000))
HTTPoison.request(method, url, request_body, request_headers, request_options)
|> handle_response(decode_results)
end
- def marshal_response(response, struct_type, subkey\\nil)
+ def marshal_response(response, struct_type, subkey \\ nil)
@doc """
Extract a meaningful structure from a generic endpoint response:
response.results[subkey] as struct_type
"""
+ def marshal_response(%SparkPost.Endpoint.Response{results: results}, struct_type, nil)
+ when is_list(results) do
+ Enum.map(results, &struct(struct_type, &1))
+ end
+
+ def marshal_response(%SparkPost.Endpoint.Response{} = response, struct_type, nil) do
+ struct(struct_type, response.results)
+ end
+
def marshal_response(%SparkPost.Endpoint.Response{} = response, struct_type, subkey) do
- if subkey do
- struct(struct_type, response.results[subkey])
- else
- struct(struct_type, response.results)
- end
+ struct(struct_type, response.results[subkey])
end
def marshal_response(%SparkPost.Endpoint.Error{} = response, _struct_type, _subkey) do
response
end
- defp handle_response({:ok, %HTTPoison.Response{status_code: code, body: body}}, decode_results) when code >= 200 and code < 300 do
+ defp handle_response({:ok, %HTTPoison.Response{status_code: code, body: body}}, decode_results)
+ when code >= 200 and code < 300 do
decoded_body = decode_response_body(body)
- if decode_results do
- %SparkPost.Endpoint.Response{status_code: code, results: decoded_body.results}
- else
- %SparkPost.Endpoint.Response{status_code: code, results: decoded_body}
- end
+
+ results =
+ case {decode_results, decoded_body} do
+ {true, %{results: results}} -> results
+ _ -> decoded_body
+ end
+
+ %SparkPost.Endpoint.Response{status_code: code, results: results}
end
- defp handle_response({:ok, %HTTPoison.Response{status_code: code, body: body}}, _decode_results) when code >= 400 do
- decoded_body = decode_response_body(body)
- if Map.has_key?(decoded_body, :errors) do
- %SparkPost.Endpoint.Error{status_code: code, errors: decoded_body.errors}
- end
+ defp handle_response({:ok, %HTTPoison.Response{status_code: code, body: body}}, _decode_results)
+ when code >= 400 do
+ errors =
+ case decode_response_body(body) do
+ %{errors: errors} -> errors
+ _ -> []
+ end
+
+ %SparkPost.Endpoint.Error{status_code: code, errors: errors}
end
defp handle_response({:error, %HTTPoison.Error{reason: reason}}, _decode_results) do
%SparkPost.Endpoint.Error{status_code: nil, errors: [reason]}
end
- defp base_request_headers() do
+ defp base_request_headers do
{:ok, version} = :application.get_key(:sparkpost, :vsn)
+
%{
"User-Agent": "elixir-sparkpost/" <> to_string(version),
- "Authorization": Application.get_env(:sparkpost, :api_key)
+ Authorization: Application.get_env(:sparkpost, :api_key)
}
end
- # Do not try to remove nils from an empty map
+ # Do not try to remove nils from an empty map
defp encode_request_body(body) when is_map(body) and map_size(body) == 0, do: {:ok, ""}
+
defp encode_request_body(body) do
- body |> Washup.filter |> Poison.encode
+ body |> Washup.filter() |> Poison.encode()
end
- defp decode_response_body(body) when byte_size(body) == 0, do: ""
- defp decode_response_body(body) do
- # TODO: [key: :atoms] is unsafe for open-ended structures such as
- # metadata and substitution_data
- body |> Poison.decode!([keys: :atoms])
+ @spec decode_response_body(String.t()) :: map
+ defp decode_response_body(body) when is_binary(body) and byte_size(body) == 0, do: %{}
+
+ defp decode_response_body(body) when is_binary(body) do
+ Poison.decode!(body, %{keys: :atoms})
end
end
diff --git a/lib/endpoint/error.ex b/lib/endpoint/error.ex
index 097e821..abc6ed4 100644
--- a/lib/endpoint/error.ex
+++ b/lib/endpoint/error.ex
@@ -10,4 +10,6 @@ defmodule SparkPost.Endpoint.Error do
"""
defstruct status_code: nil, errors: nil, results: nil
+
+ @type t :: %__MODULE__{}
end
diff --git a/lib/event.ex b/lib/event.ex
new file mode 100644
index 0000000..0127abc
--- /dev/null
+++ b/lib/event.ex
@@ -0,0 +1,59 @@
+defmodule SparkPost.Event do
+ @moduledoc """
+ Defines actions for the events endpoint
+
+ https://developers.sparkpost.com/api/events/
+ """
+ alias SparkPost.{Endpoint, Event}
+
+ @type params :: %{
+ optional(:ab_test_versions) => String.t(),
+ optional(:ab_tests) => String.t(),
+ optional(:bounce_classes) => String.t(),
+ optional(:campaigns) => String.t(),
+ optional(:cursor) => String.t(),
+ optional(:delimiter) => String.t(),
+ optional(:event_ids) => String.t(),
+ optional(:events) => String.t(),
+ optional(:from_addresses) => String.t(),
+ optional(:from) => String.t(),
+ optional(:ip_pools) => String.t(),
+ optional(:messages) => String.t(),
+ optional(:per_page) => integer,
+ optional(:reasons) => String.t(),
+ optional(:recipient_domains) => String.t(),
+ optional(:recipients) => String.t(),
+ optional(:sending_domains) => String.t(),
+ optional(:sending_ips) => String.t(),
+ optional(:subaccounts) => String.t(),
+ optional(:subjects) => String.t(),
+ optional(:templates) => String.t(),
+ optional(:to) => String.t(),
+ optional(:transmissions) => String.t()
+ }
+
+ @doc """
+ Search for message events
+
+ https://developers.sparkpost.com/api/events/#events-get-search-for-message-events
+
+ Does not sanitize parameters in any way. If a parameter is specified as needing
+ to be a comma-separated string, then it needs to be sent as such.
+ """
+ @spec search_message_events(params) :: Event.SearchResult.t() | Endpoint.Error.t()
+ def search_message_events(%{} = params \\ %{}) do
+ response = Endpoint.request(:get, "events/message", %{}, %{}, [params: params], false)
+
+ case response do
+ %SparkPost.Endpoint.Response{results: body} ->
+ %SparkPost.Event.SearchResult{
+ results: body.results,
+ links: body.links,
+ total_count: body.total_count
+ }
+
+ _ ->
+ response
+ end
+ end
+end
diff --git a/lib/event/search_result.ex b/lib/event/search_result.ex
new file mode 100644
index 0000000..9fb7040
--- /dev/null
+++ b/lib/event/search_result.ex
@@ -0,0 +1,24 @@
+defmodule SparkPost.Event.SearchResult do
+ @moduledoc """
+ Defines structure for an event search request response
+
+ The same response structure applies when searching for message and ingest events.
+
+ https://developers.sparkpost.com/api/events/#events-get-search-for-message-events
+ https://developers.sparkpost.com/api/events/#events-get-search-for-ingest-events
+ """
+
+ defstruct [
+ :links,
+ :results,
+ :total_count
+ ]
+
+ @type t :: %__MODULE__{
+ results: list(map),
+ total_count: integer(),
+ links: %{
+ optional(:next) => String.t()
+ }
+ }
+end
diff --git a/lib/mockserver.ex b/lib/mockserver.ex
index 6094ccd..dcbcb30 100644
--- a/lib/mockserver.ex
+++ b/lib/mockserver.ex
@@ -1,19 +1,19 @@
defmodule SparkPost.MockServer do
@moduledoc false
- def create_json(endpoint\\"transmission") do
+ def create_json(endpoint \\ "transmission") do
File.read!("test/data/create#{endpoint}.json")
end
- def create_fail_json(endpoint\\"transmission") do
+ def create_fail_json(endpoint \\ "transmission") do
File.read!("test/data/create#{endpoint}fail.json")
end
- def list_json(endpoint\\"transmission") do
+ def list_json(endpoint \\ "transmission") do
File.read!("test/data/list#{endpoint}.json")
end
- def get_json(endpoint\\"transmission") do
+ def get_json(endpoint \\ "transmission") do
File.read!("test/data/#{endpoint}.json")
end
@@ -34,10 +34,12 @@ defmodule SparkPost.MockServer do
end
def mk_http_resp(status_code, body) do
- fn (_method, _url, _body, _headers, _opts) -> {:ok, %HTTPoison.Response{status_code: status_code, body: body}} end
+ fn _method, _url, _body, _headers, _opts ->
+ {:ok, %HTTPoison.Response{status_code: status_code, body: body}}
+ end
end
def mk_error(reason) do
- fn (_method, _url, _body, _headers, _opts) -> {:error, %HTTPoison.Error{reason: reason}} end
+ fn _method, _url, _body, _headers, _opts -> {:error, %HTTPoison.Error{reason: reason}} end
end
end
diff --git a/lib/recipient.ex b/lib/recipient.ex
index c9f8f7f..b5b6b8b 100644
--- a/lib/recipient.ex
+++ b/lib/recipient.ex
@@ -12,12 +12,12 @@ defmodule SparkPost.Recipient do
"""
defstruct address: :required,
- return_path: nil,
- tags: nil,
- metadata: nil,
- substitution_data: nil
+ return_path: nil,
+ tags: nil,
+ metadata: nil,
+ substitution_data: nil
- alias SparkPost.{Recipient, Address}
+ alias SparkPost.{Address, Recipient}
@doc """
Convenience conversions to `[ %SparkPost.Recipient{} ]` from:
@@ -29,12 +29,11 @@ defmodule SparkPost.Recipient do
end
def to_recipient_list(email_list) when is_list(email_list) do
- Enum.map(email_list, fn (recip) -> to_recipient(recip)
- end)
+ Enum.map(email_list, fn recip -> to_recipient(recip) end)
end
def to_recipient_list(email) when is_binary(email) do
- [ to_recipient(email) ]
+ [to_recipient(email)]
end
@doc """
@@ -58,15 +57,16 @@ defmodule SparkPost.Recipient do
def to_recipient(%{address: address} = struc) do
struct(__MODULE__, %{
- struc | address: Address.to_address(address)
+ struc
+ | address: Address.to_address(address)
})
end
def to_recipient(%{name: _name, email: _email} = struc) do
- %__MODULE__{ address: Address.to_address(struc) }
+ %__MODULE__{address: Address.to_address(struc)}
end
def to_recipient(%{email: _} = struc) do
- %__MODULE__{ address: Address.to_address(struc) }
+ %__MODULE__{address: Address.to_address(struc)}
end
end
diff --git a/lib/suppression_list.ex b/lib/suppression_list.ex
index f341a6b..e0135d7 100644
--- a/lib/suppression_list.ex
+++ b/lib/suppression_list.ex
@@ -32,16 +32,21 @@ defmodule SparkPost.SuppressionList do
- description (optional): optional description of this entry in the suppression list
"""
def upsert_one(recipient, type, description \\ nil) do
- body = if description == nil do
- %{type: type}
- else
- %{type: type, description: description}
- end
+ body =
+ if description == nil do
+ %{type: type}
+ else
+ %{type: type, description: description}
+ end
+
response = Endpoint.request(:put, "suppression-list/#{recipient}", body)
+
case response do
%SparkPost.Endpoint.Response{status_code: 200, results: results} ->
{:ok, Map.get(results, :message, "")}
- _ -> {:error, response}
+
+ _ ->
+ {:error, response}
end
end
@@ -56,10 +61,13 @@ defmodule SparkPost.SuppressionList do
"""
def delete(recipient) do
response = Endpoint.request(:delete, "suppression-list/#{recipient}", %{}, %{}, [], false)
+
case response do
%SparkPost.Endpoint.Response{status_code: 204} ->
{:ok, ""}
- _ -> {:error, response}
+
+ _ ->
+ {:error, response}
end
end
@@ -81,15 +89,20 @@ defmodule SparkPost.SuppressionList do
"""
def search(params \\ []) do
response = Endpoint.request(:get, "suppression-list", %{}, %{}, [params: params], false)
+
case response do
%SparkPost.Endpoint.Response{results: body} ->
- mapped_results = Enum.map(body.results, fn res -> struct(SparkPost.SuppressionList.ListEntry, res) end)
+ mapped_results =
+ Enum.map(body.results, fn res -> struct(SparkPost.SuppressionList.ListEntry, res) end)
+
%SparkPost.SuppressionList.SearchResult{
results: mapped_results,
links: body.links,
total_count: body.total_count
}
- _ -> response
+
+ _ ->
+ response
end
end
end
diff --git a/lib/suppression_list/list_entry.ex b/lib/suppression_list/list_entry.ex
index 4b63e67..61f6a13 100644
--- a/lib/suppression_list/list_entry.ex
+++ b/lib/suppression_list/list_entry.ex
@@ -12,9 +12,9 @@ defmodule SparkPost.SuppressionList.ListEntry do
"""
defstruct recipient: :required,
- type: :required,
- source: nil,
- description: nil,
- transactional: nil,
- non_transactional: nil
+ type: :required,
+ source: nil,
+ description: nil,
+ transactional: nil,
+ non_transactional: nil
end
diff --git a/lib/suppression_list/search_result.ex b/lib/suppression_list/search_result.ex
index 16816e9..f374e84 100644
--- a/lib/suppression_list/search_result.ex
+++ b/lib/suppression_list/search_result.ex
@@ -10,6 +10,6 @@ defmodule SparkPost.SuppressionList.SearchResult do
"""
defstruct results: :required,
- links: :required,
- total_count: :required
+ links: :required,
+ total_count: :required
end
diff --git a/lib/template.ex b/lib/template.ex
index 4c0b529..907d6b7 100644
--- a/lib/template.ex
+++ b/lib/template.ex
@@ -6,18 +6,57 @@ defmodule SparkPost.Template do
Check out the documentation for each function
or use the [SparkPost API reference](https://developers.sparkpost.com/api/templates.html) for details.
- Returned by `SparkPost.template.preview/2`.
- - from
- - email
- - name
- - subject
- - reply_to
- - text
- - html
- - headers
+ ## Struct Fields
+
+ - id: Template identifier, auto-generated if not provided upon create.
+ - name: Editable template display name, auto-generated if not provided. At minimum, `:name` or `:id` is required, but not both
+ - content: Content that will be used to construct a message. Can be a `%SparkPost.Content.Inline` or a `%SparkPost.Content.Raw{}`
+ - published: Boolean indicating the published/draft state of the template. Defaults to false
+ - description: Detailed description of the template
+ - options: A `%SparkPost.Transmission.Options{}` struzct, but only `:open_tracking`, `:click_tracking` and `:transactional` are accepted when working with a template.
+ - shared_with_subaccounts: boolean indicating if the template is accessible to subaccounts. Defaults to false.
+ - has_draft: Read-only. Indicates if template has a draft version.
+ - has_published: Read-only. Indicates if template has a published version.
"""
+ alias SparkPost.Content
alias SparkPost.Endpoint
+ alias SparkPost.Transmission
+
+ defstruct id: nil,
+ name: nil,
+ content: %Content.Inline{},
+ published: false,
+ description: nil,
+ options: %Transmission.Options{},
+ shared_with_subaccounts: false,
+ has_draft: nil,
+ has_published: nil
+
+ @type t :: %__MODULE__{}
+
+ @doc """
+ Lists email templates
+
+ https://developers.sparkpost.com/api/templates/#templates-get-list-all-templates
+
+
+ """
+ @spec list(%{
+ optional(:draft) => boolean,
+ optional(:shared_with_subaccounts) => boolean
+ }) :: list(t)
+ def list(%{} = params \\ %{}) do
+ query =
+ params
+ |> Map.take([:draft, :shared_with_subaccounts])
+ |> Enum.reject(fn {_k, v} -> is_nil(v) end)
+ |> Keyword.new()
+
+ :get
+ |> Endpoint.request("templates", %{}, %{}, params: query)
+ |> Endpoint.marshal_response(__MODULE__)
+ end
@doc """
Generate a preview of an existing template.
@@ -31,17 +70,96 @@ defmodule SparkPost.Template do
- substitution_data: k,v map consisting of substituions. See the
[SparkPost Substitutions Reference](https://developers.sparkpost.com/api/substitutions-reference.html)
for more details.
+
+ Response is a `%SparkPost.Content.Inline{}` consisting of
+ - from
+ - email
+ - name
+ - subject
+ - reply_to
+ - text
+ - html
+ - headers
"""
def preview(%SparkPost.Content.TemplateRef{} = template, substitution_data) do
- qs = if is_nil(template.use_draft_template) do
- ""
- else
- "?draft=#{template.use_draft_template}"
- end
+ qs =
+ if is_nil(template.use_draft_template) do
+ ""
+ else
+ "?draft=#{template.use_draft_template}"
+ end
+
body = %{substitution_data: substitution_data}
+
:post
|> Endpoint.request("templates/#{template.template_id}/preview#{qs}", body)
|> Endpoint.marshal_response(SparkPost.Content.Inline)
- |> SparkPost.Content.Inline.convert_from_field
+ |> Content.Inline.convert_from_field()
+ end
+
+ @doc """
+ Create a SparkPost Template
+
+ ## Parameters
+
+ - `%SparkPost.Template{}`
+
+ ## Response
+
+ - `%SparkPost.Template.Response{}`
+ """
+ def create(%__MODULE__{} = template) do
+ :post
+ |> Endpoint.request("templates", template)
+ |> Endpoint.marshal_response(SparkPost.Template.Response)
+ end
+
+ @doc """
+ Update a SparkPost Template
+
+ ## Parameters
+
+ - `%SparkPost.Template{}` containing a valid `:id` as well as the updated content
+ - optional keyword list as a second argument, supporting the fields
+ - `:update_published` - defaults to false, specifies if the published version of the template should be directly updated, instead of storing the update as a draft
+
+ ## Note on `:update_published` option, vs `:published` struct field
+
+ Setting `published: true` on the struct itself performs the act of publishing a draft template. If the field is set to
+ `true`, the `:update_published` option is ingored completely.
+ """
+ def update(
+ %__MODULE__{id: template_id, published: published} = template,
+ options \\ [update_published: false]
+ ) do
+ qs =
+ if published != true && Keyword.get(options, :update_published, false) == true do
+ "?update_published=true"
+ else
+ ""
+ end
+
+ :put
+ |> Endpoint.request("templates/#{template_id}#{qs}", template)
+ |> Endpoint.marshal_response(SparkPost.Template.Response)
+ end
+
+ @doc """
+ Delete a SparkPost Template
+
+ ## Parameters
+
+ - a valid template id
+
+ ## Response
+
+ - `{:ok, %SparkPost.Endpoint.Response{}}` if successful
+ - `{:error, %SparkPost.Endpoint.Error{}}` if failure
+ """
+ def delete(template_id) do
+ case Endpoint.request(:delete, "templates/#{template_id}") do
+ %SparkPost.Endpoint.Response{status_code: 200} = response -> {:ok, response}
+ other -> {:error, other}
+ end
end
end
diff --git a/lib/template/options.ex b/lib/template/options.ex
new file mode 100644
index 0000000..498280d
--- /dev/null
+++ b/lib/template/options.ex
@@ -0,0 +1,16 @@
+defmodule SparkPost.Template.Options do
+ @moduledoc """
+ Template options.
+
+ Designed for use in `%SparkPost.Content.Template{options: ...}`
+
+ ## Fields
+ - open_tracking: enable 'email open' tracking?
+ - click_tracking: enable 'link click' tracking?
+ - transactional: is this a transactional message?
+ """
+
+ defstruct open_tracking: true,
+ click_tracking: true,
+ transactional: nil
+end
diff --git a/lib/template/response.ex b/lib/template/response.ex
new file mode 100644
index 0000000..9debc94
--- /dev/null
+++ b/lib/template/response.ex
@@ -0,0 +1,12 @@
+defmodule SparkPost.Template.Response do
+ @moduledoc """
+ The response generated when SparkPost receives a Template request.
+
+ Returned by `SparkPost.Template.create/1`
+
+ ## Fields
+ - id: Unique id of the template, generated automatically or specified as part of the original request
+ """
+
+ defstruct id: nil
+end
diff --git a/lib/transmission.ex b/lib/transmission.ex
index 1c94d16..03167c1 100644
--- a/lib/transmission.ex
+++ b/lib/transmission.ex
@@ -1,13 +1,17 @@
defmodule SparkPost.Transmission do
@moduledoc """
- The SparkPost Transmission API endpoint for sending email. Use `SparkPost.Transmission.send/1` to
- send messages, `SparkPost.Transmission.list/1` to list previous sends and `SparkPost.Transmission.get/1` to
- retrieve details on a given transmission.
+ The SparkPost Transmission API endpoint for sending email.
+
+ Use `SparkPost.Transmission.send/1` to send messages,
+ `SparkPost.Transmission.list/1` to list previous sends and
+ `SparkPost.Transmission.get/1` to retrieve details on a given transmission.
Check out the documentation for each function
- or use the [SparkPost API reference](https://www.sparkPost.com/api#/reference/transmissions) for details.
+ or use the [SparkPost API reference](https://www.sparkPost.com/api#/reference/transmissions)
+ for details.
## Request Fields
+
Used in calls to `SparkPost.Transmission.send/1`.
- campaign_id
- return_path
@@ -42,24 +46,30 @@ defmodule SparkPost.Transmission do
"""
defstruct options: %SparkPost.Transmission.Options{},
- campaign_id: nil,
- return_path: nil,
- metadata: nil,
- substitution_data: nil,
- recipients: :required,
- content: :required,
- id: nil, # System generated fields from this point on
- description: nil,
- state: nil,
- rcpt_list_chunk_size: nil,
- rcp_list_total_chunks: nil,
- num_rcpts: nil,
- num_generated: nil,
- num_failed_gen: nil,
- generation_start_time: nil,
- generation_end_time: nil
+ campaign_id: nil,
+ return_path: nil,
+ metadata: nil,
+ substitution_data: nil,
+ recipients: :required,
+ content: :required,
+ # System generated fields from this point on
+ id: nil,
+ description: nil,
+ state: nil,
+ rcpt_list_chunk_size: nil,
+ rcp_list_total_chunks: nil,
+ num_rcpts: nil,
+ num_generated: nil,
+ num_failed_gen: nil,
+ generation_start_time: nil,
+ generation_end_time: nil
- alias SparkPost.{Transmission, Recipient, Endpoint, Content}
+ alias SparkPost.{
+ Content,
+ Endpoint,
+ Recipient,
+ Transmission
+ }
@doc """
Create a new transmission and send some email.
@@ -75,54 +85,74 @@ defmodule SparkPost.Transmission do
- substitution_data: transmission-level substitution_data k/v pairs (keyword)
## Examples
- Send a message to a single recipient with inline text and HTML content:
-
- alias SparkPost.{Content, Transmission}
- Transmission.send(%Transmission{
- recipients: ["to@you.com"],
- content: %Content.Inline{
- from: "from@me.com",
- subject: subject,
- text: text,
- html: html
- }
- })
- #=> %Transmission.Response{id: "102258889940193104",
- total_accepted_recipients: 1, total_rejected_recipients: 0}
-
- Send a message to 2 recipients using a stored message template:
- alias SparkPost.Content, Transmission}
- Transmission.send(
- %Transmission{
- recipients: ["to@you.com", "to@youtoo.com"],
- content: %Content.TemplateRef{ template_id: "test-template-1" }
- }
- )
- #=> %Transmission.Response{id: "102258889940193105",
- total_accepted_recipients: 2, total_rejected_recipients: 0}
-
- Send a message with an attachment:
- alias SparkPost.{Content, Transmission}
- Transmission.send(
- %Transmission{
- recipients: ["to@you.com"],
- content: %Content.Inline{
- subject: "Now with attachments!",
- text: "There is an attachment with this message",
- attachments: [
- Content.to_attachment("cat.jpg", "image/jpeg", File.read!("cat.jpg"))
- ]
- }
- }
- )
- #=> %Transmission.Response{id: "102258889940193106",
- total_accepted_recipients: 1, total_rejected_recipients: 0}
+
+ ### Send a message to a single recipient with inline text and HTML content
+
+ ```
+ SparkPost.Transmission.send(%SparkPost.Transmission{
+ recipients: ["to@you.com"],
+ content: %SparkPost.Content.Inline{
+ from: "from@me.com",
+ subject: "A subject",
+ text: "Text body",
+ html: "HTML body"
+ }
+ })
+ ```
+
+ #=>
+
+ ```
+ %SparkPost.Transmission.Response{
+ id: "102258889940193104",
+ total_accepted_recipients: 1,
+ total_rejected_recipients: 0
+ }
+ ```
+
+ ### Send a message to 2 recipients using a stored message template
+
+ SparkPost.Transmission.send(%SparkPost.Transmission{
+ recipients: ["to@you.com", "to@youtoo.com"],
+ content: %SparkPost.Content.TemplateRef{template_id: "test-template-1"}
+ })
+
+ #=>
+
+ %SparkPost.Transmission.Response{
+ id: "102258889940193105",
+ total_accepted_recipients: 2,
+ total_rejected_recipients: 0
+ }
+
+ ### Send a message with an attachment
+
+ SparkPost.Transmission.send(%SparkPost.Transmission{
+ recipients: ["to@you.com"],
+ content: %SparkPost.Content.Inline{
+ subject: "Now with attachments!",
+ text: "There is an attachment with this message",
+ attachments: [
+ SparkPost.Content.to_attachment("cat.jpg", "image/jpeg", File.read!("cat.jpg"))
+ ]
+ }
+ })
+
+ #=>
+
+ %SparkPost.Transmission.Response{
+ id: "102258889940193106",
+ total_accepted_recipients: 1,
+ total_rejected_recipients: 0
+ }
"""
def send(%__MODULE__{} = body) do
- body = %{body |
- recipients: Recipient.to_recipient_list(body.recipients),
- content: Content.to_content(body.content)
+ body = %{
+ body
+ | recipients: Recipient.to_recipient_list(body.recipients),
+ content: Content.to_content(body.content)
}
+
response = Endpoint.request(:post, "transmissions", body)
Endpoint.marshal_response(response, Transmission.Response)
end
@@ -133,19 +163,33 @@ defmodule SparkPost.Transmission do
## Parameters
- transmission ID: identifier of the transmission to retrieve
- ## Example
- Transmission.get("102258889940193105")
- #=> %Transmission{campaign_id: "",
- content: %{template_id: "inline", template_version: 0,
- use_draft_template: false}, description: "",
- generation_end_time: "2016-01-14T12:52:05+00:00",
- generation_start_time: "2016-01-14T12:52:05+00:00", id: "48215348926834924",
- metadata: "", num_failed_gen: 0, num_generated: 2, num_rcpts: 2,
- options: %{click_tracking: true, conversion_tracking: "", open_tracking: true},
- rcp_list_total_chunks: nil, rcpt_list_chunk_size: 100, recipients: :required,
- return_path: nil, state: "Success",
- substitution_data: ""}
+ ## Example: Fetch a transmission
+
+ SparkPost.Transmission.get("102258889940193105")
+
+ #=>
+
+ %SparkPost.Transmission{
+ campaign_id: "",
+ content: %{template_id: "inline", template_version: 0, use_draft_template: false},
+ description: "",
+ generation_end_time: "2016-01-14T12:52:05+00:00",
+ generation_start_time: "2016-01-14T12:52:05+00:00",
+ id: "48215348926834924",
+ metadata: "",
+ num_failed_gen: 0,
+ num_generated: 2,
+ num_rcpts: 2,
+ options: %{click_tracking: true, conversion_tracking: "", open_tracking: true},
+ rcp_list_total_chunks: nil,
+ rcpt_list_chunk_size: 100,
+ recipients: :required,
+ return_path: nil,
+ state: "Success",
+ substitution_data: ""
+ }
"""
+
def get(transid) do
response = Endpoint.request(:get, "transmissions/" <> transid)
Endpoint.marshal_response(response, __MODULE__, :transmission)
@@ -159,28 +203,62 @@ defmodule SparkPost.Transmission do
- campaign_id
- template_id
- ## Example
- List all multi-recipient transmissions:
- Transmission.list()
- #=> [%Transmission{campaign_id: "", content: %{template_id: "inline"},
- description: "", generation_end_time: nil, generation_start_time: nil,
- id: "102258558346809186", metadata: nil, num_failed_gen: nil,
- num_generated: nil, num_rcpts: nil, options: :required,
- rcp_list_total_chunks: nil, rcpt_list_chunk_size: nil, recipients: :required,
- return_path: :nil, state: "Success", substitution_data: nil},
- %Transmission{campaign_id: "", content: %{template_id: "inline"},
- description: "", generation_end_time: nil, generation_start_time: nil,
- id: "48215348926834924", metadata: nil, num_failed_gen: nil,
- num_generated: nil, num_rcpts: nil, options: :required,
- rcp_list_total_chunks: nil, rcpt_list_chunk_size: nil, recipients: :required,
- return_path: :nil, state: "Success", substitution_data: nil}]
+ ## Example: List all multi-recipient transmissions:
+
+ SparkPost.Transmission.list()
+
+ #=>
+
+ [
+ %SparkPost.Transmission{
+ campaign_id: "",
+ content: %{template_id: "inline"},
+ description: "",
+ generation_end_time: nil,
+ generation_start_time: nil,
+ id: "102258558346809186",
+ metadata: nil,
+ num_failed_gen: nil,
+ num_generated: nil,
+ num_rcpts: nil,
+ options: :required,
+ rcp_list_total_chunks: nil,
+ rcpt_list_chunk_size: nil,
+ recipients: :required,
+ return_path: nil,
+ state: "Success",
+ substitution_data: nil
+ },
+ %SparkPost.Transmission{
+ campaign_id: "",
+ content: %{template_id: "inline"},
+ description: "",
+ generation_end_time: nil,
+ generation_start_time: nil,
+ id: "48215348926834924",
+ metadata: nil,
+ num_failed_gen: nil,
+ num_generated: nil,
+ num_rcpts: nil,
+ options: :required,
+ rcp_list_total_chunks: nil,
+ rcpt_list_chunk_size: nil,
+ recipients: :required,
+ return_path: nil,
+ state: "Success",
+ substitution_data: nil
+ }
+ ]
"""
- def list(filters\\[]) do
- response = Endpoint.request(:get, "transmissions", %{}, %{}, [params: filters])
+ def list(filters \\ []) do
+ response = Endpoint.request(:get, "transmissions", %{}, %{}, params: filters)
+
case response do
%Endpoint.Response{} ->
- Enum.map(response.results, fn (trans) -> struct(__MODULE__, trans) end)
- _ -> response
+ Enum.map(response.results, fn trans -> struct(__MODULE__, trans) end)
+
+ _ ->
+ response
end
end
end
diff --git a/lib/transmission/options.ex b/lib/transmission/options.ex
index acf30dc..e05fc5c 100644
--- a/lib/transmission/options.ex
+++ b/lib/transmission/options.ex
@@ -16,11 +16,11 @@ defmodule SparkPost.Transmission.Options do
"""
defstruct start_time: nil,
- open_tracking: true,
- click_tracking: true,
- transactional: nil,
- sandbox: nil,
- skip_suppression: nil,
- ip_pool: nil,
- inline_css: nil
+ open_tracking: true,
+ click_tracking: true,
+ transactional: nil,
+ sandbox: nil,
+ skip_suppression: nil,
+ ip_pool: nil,
+ inline_css: nil
end
diff --git a/lib/transmission/response.ex b/lib/transmission/response.ex
index af3bd0e..9fdfae6 100644
--- a/lib/transmission/response.ex
+++ b/lib/transmission/response.ex
@@ -10,6 +10,6 @@ defmodule SparkPost.Transmission.Response do
"""
defstruct id: nil,
- total_accepted_recipients: nil,
- total_rejected_recipients: nil
+ total_accepted_recipients: nil,
+ total_rejected_recipients: nil
end
diff --git a/lib/washup.ex b/lib/washup.ex
index 8606e5c..e7c8e68 100644
--- a/lib/washup.ex
+++ b/lib/washup.ex
@@ -16,7 +16,7 @@ defmodule Washup do
def filter(it) do
cond do
is_map(it) and Map.has_key?(it, :__struct__) -> filter(Map.from_struct(it))
- is_map(it) -> for {k,v} <- it, not is_nil(v), into: %{}, do: {k, do_filter(v)}
+ is_map(it) -> for {k, v} <- it, not is_nil(v), into: %{}, do: {k, do_filter(v)}
is_list(it) -> for x <- it, not is_nil(x), do: do_filter(x)
true -> it
end
@@ -39,21 +39,24 @@ defmodule Washup do
iex> Washup.verify(jenny)
** (Washup.RequiredError) pets->listidx->species required
"""
- def verify(it, path\\[]) do
+ def verify(it, path \\ []) do
cond do
is_map(it) and Map.has_key?(it, :__struct__) -> verify(Map.from_struct(it), path)
- is_map(it) -> for {k,v} <- it, into: %{}, do: {k, verify(v, [k|path])}
- is_list(it) -> for x <- it, do: verify(x, ["listidx"|path])
+ is_map(it) -> for {k, v} <- it, into: %{}, do: {k, verify(v, [k | path])}
+ is_list(it) -> for x <- it, do: verify(x, ["listidx" | path])
true -> verify_val(it, path)
end
end
defp verify_val(val, path) do
case val do
- :required -> raise Washup.RequiredError,
- path: path,
- message: Enum.join(Enum.reverse(path), "->") <> " required"
- _ -> val
+ :required ->
+ raise Washup.RequiredError,
+ path: path,
+ message: Enum.join(Enum.reverse(path), "->") <> " required"
+
+ _ ->
+ val
end
end
end
diff --git a/mix.exs b/mix.exs
index f0412f9..65078e5 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,44 +1,54 @@
defmodule SparkPost.Mixfile do
+ @moduledoc false
use Mix.Project
def project do
- [app: :sparkpost,
- version: "0.5.2",
- elixir: "~> 1.2",
- build_embedded: Mix.env == :prod,
- start_permanent: Mix.env == :prod,
- source_url: "https://github.com/SparkPost/elixir-sparkpost",
- description: "The official Elixir package for the SparkPost API",
- package: package(),
- deps: deps(),
- docs: [
- extras: ["README.md", "CONTRIBUTING.md", "CHANGELOG.md"]
- ],
- test_coverage: [tool: ExCoveralls],
- preferred_cli_env: ["coveralls": :test, "coveralls.detail": :test, "coveralls.post": :test]
- ]
+ [
+ app: :sparkpost,
+ build_embedded: Mix.env() == :prod,
+ deps: deps(),
+ description: "The official Elixir package for the SparkPost API",
+ dialyzer: dialyzer(),
+ docs: [extras: ["README.md", "CONTRIBUTING.md", "CHANGELOG.md"]],
+ elixir: "~> 1.10",
+ package: package(),
+ preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test],
+ source_url: "https://github.com/SparkPost/elixir-sparkpost",
+ start_permanent: Mix.env() == :prod,
+ test_coverage: [tool: ExCoveralls],
+ version: "0.6.0"
+ ]
+ end
+
+ defp dialyzer do
+ [
+ plt_add_apps: [:mix, :ex_unit, :poison],
+ plt_core_path: "plts",
+ plt_file: {:no_warn, "plts/dialyzer.plt"}
+ ]
end
def application do
- [applications: [:httpoison]]
+ []
end
defp deps do
[
- {:httpoison, "~> 1.0"},
- {:poison, "~> 2.0 or ~> 3.0"},
- {:mock, "~> 0.2.0", only: :test},
- {:excoveralls, "~> 0.5.7", only: :test},
- {:credo, "~> 0.5.1", only: [:dev, :test]},
- {:earmark, "~> 1.0.3", only: :dev},
- {:ex_doc, "~> 0.14.3", only: :dev}
+ {:dialyxir, "~> 1.2", only: [:dev, :test], runtime: false},
+ {:httpoison, "~> 1.7"},
+ {:poison, "~> 5.0"},
+ {:mock, "~> 0.3.5", only: :test},
+ {:excoveralls, "~> 0.15", only: :test},
+ {:credo, "~> 1.6", only: [:dev, :test], runtime: false},
+ {:earmark, "~> 1.4", only: :dev},
+ {:ex_doc, "~> 0.29", only: :dev, runtime: false}
]
end
defp package do
[
files: ["mix.exs", "lib", "README.md", "CONTRIBUTING.md", "LICENSE.md"],
- maintainers: ["Ewan Dennis"],
+ maintainers: ["Ewan Dennis", "Nikola Begedin"],
licenses: ["Apache 2.0"],
links: %{
"Github" => "https://github.com/SparkPost/elixir-sparkpost",
@@ -47,4 +57,3 @@ defmodule SparkPost.Mixfile do
]
end
end
-
diff --git a/mix.lock b/mix.lock
index 6c58af5..5c74df7 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,24 +1,33 @@
%{
- "bunt": {:hex, :bunt, "0.1.6", "5d95a6882f73f3b9969fdfd1953798046664e6f77ec4e486e6fafc7caad97c6f", [:mix], []},
- "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
- "credo": {:hex, :credo, "0.5.1", "2395862b94628cadf0f5c68975c1440393f425b955f1e70ce1aea267e00187a1", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]},
- "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []},
- "ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]},
- "excoveralls": {:hex, :excoveralls, "0.5.7", "5d26e4a7cdf08294217594a1b0643636accc2ad30e984d62f1d166f70629ff50", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]},
- "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]},
- "hackney": {:hex, :hackney, "1.12.1", "8bf2d0e11e722e533903fe126e14d6e7e94d9b7983ced595b75f532e04b7fdc7", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
- "httpoison": {:hex, :httpoison, "1.1.1", "96ed7ab79f78a31081bb523eefec205fd2900a02cda6dbc2300e7a1226219566", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
+ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
+ "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
+ "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
+ "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
+ "earmark": {:hex, :earmark, "1.4.10", "bddce5e8ea37712a5bfb01541be8ba57d3b171d3fa4f80a0be9bcf1db417bcaf", [:mix], [{:earmark_parser, ">= 1.4.10", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "12dbfa80810478e521d3ffb941ad9fbfcbbd7debe94e1341b4c4a1b2411c1c27"},
+ "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
+ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
+ "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
+ "excoveralls": {:hex, :excoveralls, "0.15.1", "83c8cf7973dd9d1d853dce37a2fb98aaf29b564bf7d01866e409abf59dac2c0e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8416bd90c0082d56a2178cf46c837595a06575f70a5624f164a1ffe37de07e7"},
+ "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "b55727b206dab96feb025267e5c122ddb448f55b6648f9156b8d481215d80290"},
+ "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
+ "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
+ "httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "975cc87c845a103d3d1ea1ccfd68a2700c211a434d8428b10c323dc95dc5b980"},
"httpotion": {:hex, :httpotion, "2.1.0", "3fe84fbd13d4560c2514da656d022b1191a079178ee4992d245fc3c33c01ee18", [:mix], []},
"ibrowse": {:git, "https://github.com/cmullaparthi/ibrowse.git", "ea3305d21f37eced4fac290f64b068e56df7de80", [tag: "v4.1.2"]},
- "idna": {:hex, :idna, "5.1.1", "cbc3b2fa1645113267cc59c760bafa64b2ea0334635ef06dbac8801e42f7279c", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
- "jsx": {:hex, :jsx, "2.8.0", "749bec6d205c694ae1786d62cea6cc45a390437e24835fd16d12d74f07097727", [:mix, :rebar], []},
- "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:make, :rebar], []},
- "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
- "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
- "mock": {:hex, :mock, "0.2.0", "5991877be6bb514b647dbd6f4869bc12bd7f2829df16e86c98d6108f966d34d7", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]},
- "parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
- "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
- "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
+ "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
+ "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
+ "jsx": {:hex, :jsx, "2.8.0", "749bec6d205c694ae1786d62cea6cc45a390437e24835fd16d12d74f07097727", [:mix, :rebar], [], "hexpm", "a8ba15d5bac2c48b2be1224a0542ad794538d79e2cc16841a4e24ca75f0f8378"},
+ "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
+ "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
+ "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
+ "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
+ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
+ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
+ "mock": {:hex, :mock, "0.3.5", "feb81f52b8dcf0a0d65001d2fec459f6b6a8c22562d94a965862f6cc066b5431", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "6fae404799408300f863550392635d8f7e3da6b71abdd5c393faf41b131c8728"},
+ "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
+ "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
+ "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
+ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5", "2e73e068cd6393526f9fa6d399353d7c9477d6886ba005f323b592d389fb47be", [:make], []},
- "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
+ "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
}
diff --git a/test/data/createtemplate.json b/test/data/createtemplate.json
new file mode 100644
index 0000000..9e91f98
--- /dev/null
+++ b/test/data/createtemplate.json
@@ -0,0 +1,5 @@
+{
+ "results": {
+ "id": "TEMPLATE_ID"
+ }
+}
diff --git a/test/data/event.search_message_events.json b/test/data/event.search_message_events.json
new file mode 100644
index 0000000..dcc0099
--- /dev/null
+++ b/test/data/event.search_message_events.json
@@ -0,0 +1,57 @@
+{
+ "results": [
+ {
+ "type": "click",
+ "campaign_id": "Example Campaign Name",
+ "customer_id": "1",
+ "delv_method": "esmtp",
+ "event_id": "92356927693813856",
+ "friendly_from": "sender@example.com",
+ "geo_ip": {
+ "country": "US",
+ "region": "MD",
+ "city": "Columbia",
+ "latitude": "39.1749",
+ "longitude": "-76.8375"
+ },
+ "injection_time": "2016-04-18T14:25:07.000+00:00",
+ "ip_address": "127.0.0.1",
+ "ip_pool": "Example-Ip-Pool",
+ "message_id": "000443ee14578172be22",
+ "msg_from": "sender@example.com",
+ "msg_size": "1337",
+ "num_retries": "2",
+ "queue_time": "12",
+ "rcpt_meta": {
+ "customKey": "customValue",
+ "anotherKey": [
+ "value1",
+ "value2",
+ "value3"
+ ]
+ },
+ "rcpt_tags": [
+ "male",
+ "US"
+ ],
+ "rcpt_to": "recipient@example.com",
+ "raw_rcpt_to": "recipient@example.com",
+ "rcpt_type": "cc",
+ "recipient_domain": "example.com",
+ "routing_domain": "example.com",
+ "sending_domain": "example.com",
+ "sending_ip": "127.0.0.1",
+ "subaccount_id": "101",
+ "subject": "Summer deals are here!",
+ "target_link_name": "Example Link Name",
+ "target_link_url": "http://example.com",
+ "template_id": "my-template",
+ "template_version": "1",
+ "timestamp": "2016-04-18T14:25:07.000+00:00",
+ "transmission_id": "65832150921904138",
+ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36"
+ }
+ ],
+ "total_count": 1,
+ "links": {}
+}
\ No newline at end of file
diff --git a/test/data/listtemplate.json b/test/data/listtemplate.json
new file mode 100644
index 0000000..9f9750f
--- /dev/null
+++ b/test/data/listtemplate.json
@@ -0,0 +1,25 @@
+{
+ "results": [
+ {
+ "id": "summer_sale",
+ "name": "Summer Sale!",
+ "published": false,
+ "description": "",
+ "has_draft": true,
+ "has_published": true,
+ "last_update_time": "2017-08-11T12:12:12+00:00",
+ "shared_with_subaccount": true
+ },
+ {
+ "id": "daily",
+ "name": "daily",
+ "published": false,
+ "description": "Daily roundup email.",
+ "has_draft": true,
+ "has_published": true,
+ "last_use": "2018-05-08T14:49:03+00:00",
+ "last_update_time": "2018-02-10T14:15:16+00:00",
+ "shared_with_subaccount": true
+ }
+ ]
+}
diff --git a/test/data/templatedelete_fail_404.json b/test/data/templatedelete_fail_404.json
new file mode 100644
index 0000000..34cd884
--- /dev/null
+++ b/test/data/templatedelete_fail_404.json
@@ -0,0 +1,9 @@
+{
+ "errors": [
+ {
+ "message": "resource not found",
+ "code": "1600",
+ "description": "Template does not exist"
+ }
+ ]
+}
diff --git a/test/data/templatedelete_fail_409.json b/test/data/templatedelete_fail_409.json
new file mode 100644
index 0000000..3a5e918
--- /dev/null
+++ b/test/data/templatedelete_fail_409.json
@@ -0,0 +1,9 @@
+{
+ "errors": [
+ {
+ "message": "resource conflict",
+ "code": "1602",
+ "description": "Template is in use by msg generation"
+ }
+ ]
+}
diff --git a/test/data/updatetemplate.json b/test/data/updatetemplate.json
new file mode 100644
index 0000000..9e91f98
--- /dev/null
+++ b/test/data/updatetemplate.json
@@ -0,0 +1,5 @@
+{
+ "results": {
+ "id": "TEMPLATE_ID"
+ }
+}
diff --git a/test/endpoint_test.exs b/test/endpoint_test.exs
index 2d44d60..7b79ef9 100644
--- a/test/endpoint_test.exs
+++ b/test/endpoint_test.exs
@@ -1,4 +1,5 @@
defmodule SparkPost.EndpointTest do
+ @moduledoc false
use ExUnit.Case, async: false
alias SparkPost.Endpoint
@@ -7,96 +8,119 @@ defmodule SparkPost.EndpointTest do
import Mock
defmodule Headers do
+ @moduledoc false
def for_method(method) do
- cond do
- method in [:post, :put] -> Map.merge(for_body_requests(), core())
- true -> core()
+ if method in [:post, :put] do
+ Map.merge(for_body_requests(), core())
+ else
+ core()
end
end
defp for_body_requests do
- %{"Content-Type" => &(&1=="application/json")}
+ %{"Content-Type" => &(&1 == "application/json")}
end
def core do
%{
"Authorization" => &(Application.get_env(:sparkpost, :api_key) == &1),
- "User-Agent" => &(Regex.match?(~r/elixir-sparkpost\/\d+\.\d+\.\d+/, &1))
+ "User-Agent" => &Regex.match?(~r/elixir-sparkpost\/\d+\.\d+\.\d+/, &1)
}
end
end
test "Endpoint.request succeeds with Endpoint.Response" do
- with_mock HTTPoison, [request: fn(_, _, _, _, _) ->
- r = MockServer.mk_resp
- r.(nil, nil, nil, nil, nil)
- end] do
+ with_mock HTTPoison,
+ request: fn _, _, _, _, _ ->
+ r = MockServer.mk_resp()
+ r.(nil, nil, nil, nil, nil)
+ end do
Endpoint.request(:get, "transmissions", %{})
end
end
- test "Endpoint.request populates Endpoint.Response" do
+ test "Endpoint.request populates Endpoint.Response" do
status_code = 200
- results = Poison.decode!(MockServer.create_json, [keys: :atoms]).results
- with_mock HTTPoison, [request: MockServer.mk_resp] do
+ results = Poison.decode!(MockServer.create_json(), %{keys: :atoms}).results
+
+ with_mock HTTPoison, request: MockServer.mk_resp() do
resp = %Endpoint.Response{} = Endpoint.request(:get, "transmissions", %{}, %{}, [])
assert %Endpoint.Response{status_code: ^status_code, results: ^results} = resp
end
end
test "Endpoint.request fails with Endpoint.Error" do
- with_mock HTTPoison, [request: MockServer.mk_fail] do
- %Endpoint.Error{} = Endpoint.request(
- :get, "transmissions", [])
+ with_mock HTTPoison, request: MockServer.mk_fail() do
+ %Endpoint.Error{} =
+ Endpoint.request(
+ :get,
+ "transmissions",
+ []
+ )
end
end
test "Endpoint.request populates Endpoint.Error" do
status_code = 400
- errors = Poison.decode!(MockServer.create_fail_json, [keys: :atoms]).errors
- with_mock HTTPoison, [request: MockServer.mk_fail] do
- resp = %Endpoint.Error{} = Endpoint.request(
- :get, "transmissions", [])
+ errors = Poison.decode!(MockServer.create_fail_json(), %{keys: :atoms}).errors
+
+ with_mock HTTPoison, request: MockServer.mk_fail() do
+ resp =
+ %Endpoint.Error{} =
+ Endpoint.request(
+ :get,
+ "transmissions",
+ []
+ )
assert %Endpoint.Error{status_code: ^status_code, errors: ^errors} = resp
end
end
test "Endpoint.request includes the core HTTP headers" do
- respfn = MockServer.mk_resp
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- Enum.each(Headers.for_method(method), fn {header, tester} ->
- header_atom = String.to_atom(header)
- assert Map.has_key?(headers, header_atom), "#{header} header required for #{method} requests"
- assert tester.(headers[header_atom]), "Malformed header: #{header}. See Headers module in #{__ENV__.file} for formatting rules."
- end)
- respfn.(method, url, body, headers, opts)
- end
- ] do
+ respfn = MockServer.mk_resp()
+
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ Enum.each(Headers.for_method(method), fn {header, tester} ->
+ header_atom = String.to_atom(header)
+
+ assert Map.has_key?(headers, header_atom),
+ "#{header} header required for #{method} requests"
+
+ assert tester.(headers[header_atom]),
+ "Malformed header: #{header}. See Headers module in #{__ENV__.file} for formatting rules."
+ end)
+
+ respfn.(method, url, body, headers, opts)
+ end do
Enum.each([:get, :post, :put, :delete], fn method ->
- Endpoint.request(method, "transmissions", %{}, %{}, []) end)
+ Endpoint.request(method, "transmissions", %{}, %{}, [])
+ end)
end
end
test "Endpoint.request includes request bodies for appropriate methods" do
- respfn = MockServer.mk_resp
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert body == ""
- respfn.(method, url, body, headers, opts)
- end
- ] do
+ respfn = MockServer.mk_resp()
+
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert body == ""
+ respfn.(method, url, body, headers, opts)
+ end do
Endpoint.request(:post, "transmissions", %{}, %{}, [])
Endpoint.request(:put, "transmissions", %{}, %{}, [])
end
end
test "Endpoint.request includes request timeout" do
- respfn = MockServer.mk_resp
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert Keyword.has_key?(opts, :timeout)
- respfn.(method, url, body, headers, opts)
- end
- ] do
+ respfn = MockServer.mk_resp()
+
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert Keyword.has_key?(opts, :timeout)
+ respfn.(method, url, body, headers, opts)
+ end do
Endpoint.request(:post, "transmissions", %{}, %{}, [])
Endpoint.request(:put, "transmissions", %{}, %{}, [])
Endpoint.request(:get, "transmissions", %{}, %{}, [])
@@ -104,12 +128,20 @@ defmodule SparkPost.EndpointTest do
end
test_with_mock "Endpoint request can handle httpoison timeouts", HTTPoison,
- [request: fn (method, url, body, headers, opts) ->
+ request: fn method, url, body, headers, opts ->
fun = MockServer.mk_error(:timeout)
fun.(method, url, body, headers, opts)
- end
- ] do
- assert %Endpoint.Error{errors: [:timeout], status_code: nil, results: nil} ==
- Endpoint.request(:post, "transmissions", %{}, %{}, [])
+ end do
+ assert %Endpoint.Error{errors: [:timeout], status_code: nil, results: nil} ==
+ Endpoint.request(:post, "transmissions", %{}, %{}, [])
+ end
+
+ test_with_mock "Endpoint request can handle blank map as response", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ fun = MockServer.mk_http_resp(200, "{}")
+ fun.(method, url, body, headers, opts)
+ end do
+ assert %Endpoint.Response{status_code: 200, results: %{}} ==
+ Endpoint.request(:post, "transmissions", %{}, %{}, [])
end
end
diff --git a/test/event_test.exs b/test/event_test.exs
new file mode 100644
index 0000000..8af6f5c
--- /dev/null
+++ b/test/event_test.exs
@@ -0,0 +1,29 @@
+defmodule SparkPost.EventTest do
+ @moduledoc false
+
+ use ExUnit.Case, async: false
+
+ alias SparkPost.{Event, MockServer}
+
+ import Mock
+
+ test_with_mock "Event.search_message_events/1 succeeds with Event.SearchResult",
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :get
+
+ fun =
+ MockServer.mk_http_resp(
+ 200,
+ MockServer.get_json("event.search_message_events")
+ )
+
+ fun.(method, url, body, headers, opts)
+ end do
+ assert %Event.SearchResult{
+ results: [_],
+ links: %{},
+ total_count: 1
+ } = Event.search_message_events()
+ end
+end
diff --git a/test/filter_test.exs b/test/filter_test.exs
index 680328e..71bb39d 100644
--- a/test/filter_test.exs
+++ b/test/filter_test.exs
@@ -23,6 +23,6 @@ defmodule Washup.Test.Filter do
test "map -> map" do
assert Washup.filter(%{key1: %{a: 1, b: nil, c: 3}, key2: nil, key3: %{q: "q"}}) ==
- %{key1: %{a: 1, c: 3}, key3: %{q: "q"}}
+ %{key1: %{a: 1, c: 3}, key3: %{q: "q"}}
end
end
diff --git a/test/sparkpost_test.exs b/test/sparkpost_test.exs
index 91a5d00..19544a0 100644
--- a/test/sparkpost_test.exs
+++ b/test/sparkpost_test.exs
@@ -1,32 +1,37 @@
defmodule SparkPostTest do
+ @moduledoc false
use ExUnit.Case
- alias SparkPost.{Address, Content, Recipient, MockServer}
+ alias SparkPost.{Address, Content, MockServer, Recipient}
import Mock
test "send succeeds with a Transmission.Response" do
- with_mock HTTPoison, [request: MockServer.mk_resp] do
- resp = SparkPost.send(
- to: "you@there.com",
- from: "me@here.com",
- subject: "Elixir and SparkPost...",
- text: "Raw text email is boring",
- html: ""
- )
+ with_mock HTTPoison, request: MockServer.mk_resp() do
+ resp =
+ SparkPost.send(
+ to: "you@there.com",
+ from: "me@here.com",
+ subject: "Elixir and SparkPost...",
+ text: "Raw text email is boring",
+ html: ""
+ )
+
assert %SparkPost.Transmission.Response{} = resp
end
end
test "send fails with a Endpoint.Error" do
- with_mock HTTPoison, [request: MockServer.mk_fail] do
- resp = SparkPost.send(
- to: "you@there.com",
- from: "me@here.com",
- subject: "Elixir and SparkPost...",
- text: nil,
- html: nil
- )
+ with_mock HTTPoison, request: MockServer.mk_fail() do
+ resp =
+ SparkPost.send(
+ to: "you@there.com",
+ from: "me@here.com",
+ subject: "Elixir and SparkPost...",
+ text: nil,
+ html: nil
+ )
+
assert %SparkPost.Endpoint.Error{} = resp
end
end
@@ -37,17 +42,21 @@ defmodule SparkPostTest do
subject = "Elixir and SparkPost..."
text = "Raw text email is boring"
html = ""
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- inreq = Poison.decode!(body, [keys: :atoms])
- assert Recipient.to_recipient_list(inreq.recipients) == Recipient.to_recipient_list(to)
- assert Content.to_content(inreq.content) == %Content.Inline{
- from: Address.to_address(from),
- subject: subject,
- text: text,
- html: html
- }
- MockServer.mk_resp.(method, url, body, headers, opts)
- end] do
+
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ inreq = Poison.decode!(body, %{keys: :atoms})
+ assert Recipient.to_recipient_list(inreq.recipients) == Recipient.to_recipient_list(to)
+
+ assert Content.to_content(inreq.content) == %Content.Inline{
+ from: Address.to_address(from),
+ subject: subject,
+ text: text,
+ html: html
+ }
+
+ MockServer.mk_resp().(method, url, body, headers, opts)
+ end do
SparkPost.send(
to: to,
from: from,
diff --git a/test/suppression_list_test.exs b/test/suppression_list_test.exs
index 175631f..9b848f0 100644
--- a/test/suppression_list_test.exs
+++ b/test/suppression_list_test.exs
@@ -4,26 +4,30 @@ defmodule SparkPost.SuppressionListTest do
use ExUnit.Case, async: false
alias SparkPost.{MockServer, SuppressionList}
- alias SparkPost.SuppressionList.{SearchResult, ListEntry}
+ alias SparkPost.SuppressionList.{ListEntry, SearchResult}
import Mock
test_with_mock "SuppressionList.upsert_one succeeds with message",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :put
- fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionlistupdate"))
- fun.(method, url, body, headers, opts)
- end] do
- {:ok, resp} = SuppressionList.upsert_one("test@marketing.com", "non_transactional", "test description")
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :put
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionlistupdate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ {:ok, resp} =
+ SuppressionList.upsert_one("test@marketing.com", "non_transactional", "test description")
+
assert resp == "Test response message"
end
test_with_mock "SuppressionList.upsert_one fails with invalid type",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :put
- fun = MockServer.mk_http_resp(400, MockServer.get_json("suppressionupdate_fail"))
- fun.(method, url, body, headers, opts)
- end] do
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :put
+ fun = MockServer.mk_http_resp(400, MockServer.get_json("suppressionupdate_fail"))
+ fun.(method, url, body, headers, opts)
+ end do
{:error, resp} = SuppressionList.upsert_one("test@marketing.com", "bad_type")
assert %SparkPost.Endpoint.Error{} = resp
assert resp.status_code == 400
@@ -31,87 +35,92 @@ defmodule SparkPost.SuppressionListTest do
end
test_with_mock "SuppressionList.delete succeeds with empty body",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :delete
- fun = MockServer.mk_http_resp(204, "")
- fun.(method, url, body, headers, opts)
- end] do
- {:ok, resp} = SuppressionList.delete("test@marketing.com")
- assert resp == ""
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :delete
+ fun = MockServer.mk_http_resp(204, "")
+ fun.(method, url, body, headers, opts)
+ end do
+ {:ok, resp} = SuppressionList.delete("test@marketing.com")
+ assert resp == ""
end
test_with_mock "SuppressionList.delete fails 404",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :delete
- fun = MockServer.mk_http_resp(404, MockServer.get_json("suppressiondelete_fail"))
- fun.(method, url, body, headers, opts)
- end] do
- {:error, resp} = SuppressionList.delete("test@marketing.com")
- assert %SparkPost.Endpoint.Error{} = resp
- assert resp.status_code == 404
- assert resp.errors == [%{message: "Recipient could not be found"}]
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :delete
+ fun = MockServer.mk_http_resp(404, MockServer.get_json("suppressiondelete_fail"))
+ fun.(method, url, body, headers, opts)
+ end do
+ {:error, resp} = SuppressionList.delete("test@marketing.com")
+ assert %SparkPost.Endpoint.Error{} = resp
+ assert resp.status_code == 404
+ assert resp.errors == [%{message: "Recipient could not be found"}]
end
test_with_mock "SuppressionList.search succeeds with SuppressionList.SearchResult",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :get
- fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionsearch"))
- fun.(method, url, body, headers, opts)
- end] do
- resp = SuppressionList.search()
- assert %SearchResult{} = resp
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :get
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionsearch"))
+ fun.(method, url, body, headers, opts)
+ end do
+ resp = SuppressionList.search()
+ assert %SearchResult{} = resp
end
test_with_mock "SuppressionList.search fails with Endpoint.Error", HTTPoison,
- [request: MockServer.mk_fail] do
- resp = SuppressionList.search()
- assert %SparkPost.Endpoint.Error{} = resp
+ request: MockServer.mk_fail() do
+ resp = SuppressionList.search()
+ assert %SparkPost.Endpoint.Error{} = resp
end
test_with_mock "SuppressionList.search creates ListEntry structs", HTTPoison,
- [request: fn (method, url, body, headers, opts) ->
+ request: fn method, url, body, headers, opts ->
assert method == :get
fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionsearch"))
fun.(method, url, body, headers, opts)
- end] do
- resp = SuppressionList.search()
- assert %SearchResult{
- results: [
- %ListEntry{
- recipient: "test@marketing.com",
- type: "non_transactional",
- source: nil,
- description: nil,
- non_transactional: nil
- }
- ],
- links: [],
- total_count: 1
- } == resp
+ end do
+ resp = SuppressionList.search()
+
+ assert %SearchResult{
+ results: [
+ %ListEntry{
+ recipient: "test@marketing.com",
+ type: "non_transactional",
+ source: nil,
+ description: nil,
+ non_transactional: nil
+ }
+ ],
+ links: [],
+ total_count: 1
+ } == resp
end
test_with_mock "SuppressionList.search parses out cursor info", HTTPoison,
- [request: fn (method, url, body, headers, opts) ->
+ request: fn method, url, body, headers, opts ->
assert method == :get
fun = MockServer.mk_http_resp(200, MockServer.get_json("suppressionsearch_links"))
fun.(method, url, body, headers, opts)
- end] do
- resp = SuppressionList.search()
- assert %SearchResult{
- results: [
- %ListEntry{
- recipient: "test@marketing.com",
- type: "non_transactional",
- source: nil,
- description: nil,
- non_transactional: nil
- }
- ],
- links: [
- %{href: "/currentlink", rel: "first"},
- %{href: "/linkwithcursor", rel: "next"}
- ],
- total_count: 1
- } == resp
+ end do
+ resp = SuppressionList.search()
+
+ assert %SearchResult{
+ results: [
+ %ListEntry{
+ recipient: "test@marketing.com",
+ type: "non_transactional",
+ source: nil,
+ description: nil,
+ non_transactional: nil
+ }
+ ],
+ links: [
+ %{href: "/currentlink", rel: "first"},
+ %{href: "/linkwithcursor", rel: "next"}
+ ],
+ total_count: 1
+ } == resp
end
end
diff --git a/test/template_test.exs b/test/template_test.exs
index e2477ea..b81c726 100644
--- a/test/template_test.exs
+++ b/test/template_test.exs
@@ -1,13 +1,19 @@
defmodule SparkPost.TemplateTest do
+ @moduledoc false
use ExUnit.Case, async: false
- alias SparkPost.Content.{TemplateRef, Inline}
alias SparkPost.{Endpoint, MockServer, Template}
+ alias SparkPost.Content.{Inline, TemplateRef}
import Mock
defmodule TestStruct do
+ @moduledoc false
def basic_template do
+ %SparkPost.Template{id: "TEMPLATE_ID"}
+ end
+
+ def basic_template_ref do
%TemplateRef{template_id: "TEMPLATE_ID", use_draft_template: nil}
end
@@ -23,58 +29,222 @@ defmodule SparkPost.TemplateTest do
end
end
+ describe "Template.list" do
+ test_with_mock "returns list of templates", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :get
+ assert url =~ "/templates"
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("listtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert [%SparkPost.Template{}, %SparkPost.Template{}] = Template.list()
+ end
+
+ test_with_mock "passes :draft param", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert opts[:params] == [draft: true]
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("listtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.list(%{draft: true})
+ end
+
+ test_with_mock "passes :shared_with_subaccounts param", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert opts[:params] == [shared_with_subaccounts: false]
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("listtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.list(%{shared_with_subaccounts: false})
+ end
+
+ test_with_mock "returns error response", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ fun = MockServer.mk_error("Uknown")
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.list() == %SparkPost.Endpoint.Error{
+ status_code: nil,
+ errors: ["Uknown"],
+ results: nil
+ }
+ end
+ end
+
test_with_mock "Template.preview succeeds with Content.Inline",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :post
- # draft not set
- assert String.ends_with?(url, "preview")
- fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate"))
- fun.(method, url, body, headers, opts)
- end] do
- resp = Template.preview(TestStruct.basic_template(), TestStruct.substitution_data())
- assert %Inline{} = resp
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :post
+ # draft not set
+ assert String.ends_with?(url, "preview")
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ resp = Template.preview(TestStruct.basic_template_ref(), TestStruct.substitution_data())
+ assert %Inline{} = resp
end
test_with_mock "Template.preview succeeds with Content.Inline and draft set",
- HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :post
- assert String.ends_with?(url, "preview?draft=true")
- fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate"))
- fun.(method, url, body, headers, opts)
- end] do
- resp = Template.preview(TestStruct.template_with_draft(), TestStruct.substitution_data())
- assert %Inline{} = resp
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :post
+ assert String.ends_with?(url, "preview?draft=true")
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ resp = Template.preview(TestStruct.template_with_draft(), TestStruct.substitution_data())
+ assert %Inline{} = resp
end
test_with_mock "Template.preview fails with Endpoint.Error", HTTPoison,
- [request: MockServer.mk_fail] do
- resp = Template.preview(TestStruct.basic_template(), TestStruct.substitution_data())
- assert %Endpoint.Error{} = resp
+ request: MockServer.mk_fail() do
+ resp = Template.preview(TestStruct.basic_template_ref(), TestStruct.substitution_data())
+ assert %Endpoint.Error{} = resp
end
test_with_mock "Template.preview unmarshals complex from field correctly", HTTPoison,
- [request: fn (method, url, body, headers, opts) ->
+ request: fn method, url, body, headers, opts ->
assert method == :post
fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate"))
fun.(method, url, body, headers, opts)
- end] do
- resp = Template.preview(TestStruct.basic_template(), TestStruct.substitution_data())
- assert %SparkPost.Address{
- name: "Example Company Marketing",
- "email": "marketing@bounces.company.example"
- } == resp.from
+ end do
+ resp = Template.preview(TestStruct.basic_template_ref(), TestStruct.substitution_data())
+
+ assert %SparkPost.Address{
+ name: "Example Company Marketing",
+ email: "marketing@bounces.company.example"
+ } == resp.from
end
test_with_mock "Template.preview unmarshals simple from field correctly", HTTPoison,
- [request: fn (method, url, body, headers, opts) ->
+ request: fn method, url, body, headers, opts ->
assert method == :post
fun = MockServer.mk_http_resp(200, MockServer.get_json("previewtemplate_simpleemail"))
fun.(method, url, body, headers, opts)
- end] do
- resp = Template.preview(TestStruct.basic_template(), TestStruct.substitution_data())
- assert %SparkPost.Address{
- name: nil,
- "email": "marketing@bounces.company.example"
- } == resp.from
+ end do
+ resp = Template.preview(TestStruct.basic_template_ref(), TestStruct.substitution_data())
+
+ assert %SparkPost.Address{
+ name: nil,
+ email: "marketing@bounces.company.example"
+ } == resp.from
+ end
+
+ test_with_mock "Template.create succeeds with Template.Response", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :post
+ assert url =~ "/templates"
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("createtemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.create(TestStruct.basic_template()) ==
+ %SparkPost.Template.Response{id: "TEMPLATE_ID"}
+ end
+
+ test_with_mock "Template.create fails with Endpoint.Error", HTTPoison,
+ request: MockServer.mk_fail() do
+ resp = Template.create(TestStruct.basic_template())
+ assert %Endpoint.Error{} = resp
+ end
+
+ test_with_mock "Template.update succeeds with Template.Response", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :put
+ assert url =~ "/templates/TEMPLATE_ID"
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("updatetemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.update(TestStruct.basic_template()) ==
+ %SparkPost.Template.Response{id: "TEMPLATE_ID"}
+ end
+
+ test_with_mock "Template.update succeeds with update_published set", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :put
+ assert url =~ "/templates/TEMPLATE_ID?update_published=true"
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("updatetemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.update(TestStruct.basic_template(), update_published: true) ==
+ %SparkPost.Template.Response{id: "TEMPLATE_ID"}
+ end
+
+ test_with_mock "Template.update ignores update_published set if published field set", HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :put
+ refute url =~ "/templates/TEMPLATE_ID?update_published=true"
+ assert url =~ "/templates/TEMPLATE_ID"
+ fun = MockServer.mk_http_resp(200, MockServer.get_json("updatetemplate"))
+ fun.(method, url, body, headers, opts)
+ end do
+ template = %{TestStruct.basic_template() | published: true}
+
+ assert Template.update(template, update_published: true) == %SparkPost.Template.Response{
+ id: "TEMPLATE_ID"
+ }
+ end
+
+ test_with_mock "Template.update fails with Endpoint.Error", HTTPoison,
+ request: MockServer.mk_fail() do
+ resp = Template.update(TestStruct.basic_template())
+ assert %Endpoint.Error{} = resp
+ end
+
+ test_with_mock "Template.delete succeeds with empty body",
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :delete
+ assert url =~ "/templates/TEMPLATE_ID"
+ fun = MockServer.mk_http_resp(200, "{}")
+ fun.(method, url, body, headers, opts)
+ end do
+ assert Template.delete("TEMPLATE_ID") ==
+ {:ok, %SparkPost.Endpoint.Response{results: %{}, status_code: 200}}
+ end
+
+ test_with_mock "Template.delete fails with 404",
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :delete
+ assert url =~ "/templates/TEMPLATE_ID"
+
+ fun =
+ MockServer.mk_http_resp(404, MockServer.get_json("templatedelete_fail_404"))
+
+ fun.(method, url, body, headers, opts)
+ end do
+ assert {:error, %Endpoint.Error{} = resp} = Template.delete("TEMPLATE_ID")
+ assert resp.status_code == 404
+
+ assert resp.errors == [
+ %{
+ code: "1600",
+ description: "Template does not exist",
+ message: "resource not found"
+ }
+ ]
+ end
+
+ test_with_mock "Template.delete fails with 409",
+ HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :delete
+ assert url =~ "/templates/TEMPLATE_ID"
+
+ fun =
+ MockServer.mk_http_resp(409, MockServer.get_json("templatedelete_fail_409"))
+
+ fun.(method, url, body, headers, opts)
+ end do
+ assert {:error, %Endpoint.Error{} = resp} = Template.delete("TEMPLATE_ID")
+ assert resp.status_code == 409
+
+ assert resp.errors == [
+ %{
+ code: "1602",
+ description: "Template is in use by msg generation",
+ message: "resource conflict"
+ }
+ ]
end
end
diff --git a/test/transmission_test.exs b/test/transmission_test.exs
index 0e3864e..64cdfff 100644
--- a/test/transmission_test.exs
+++ b/test/transmission_test.exs
@@ -1,11 +1,19 @@
defmodule SparkPost.TransmissionTest do
+ @moduledoc false
use ExUnit.Case
- alias SparkPost.{Transmission, Recipient, Address, Content, MockServer}
+ alias SparkPost.{
+ Address,
+ Content,
+ MockServer,
+ Recipient,
+ Transmission
+ }
import Mock
defmodule TestStructs do
+ @moduledoc false
def skeleton(options: options, recipients: recipients, content: content) do
%Transmission{
options: options,
@@ -22,21 +30,21 @@ defmodule SparkPost.TransmissionTest do
end
end
- def full_addr_recipient(name\\"You There", email\\"you@there.com") do
- %Recipient{ address: %Address{ name: name, email: email} }
+ def full_addr_recipient(name \\ "You There", email \\ "you@there.com") do
+ %Recipient{address: %Address{name: name, email: email}}
end
- def addr_spec_recipient(email\\"you@there.com") do
- %Recipient{ address: %Address{ email: email } }
+ def addr_spec_recipient(email \\ "you@there.com") do
+ %Recipient{address: %Address{email: email}}
end
def inline_content do
%Content.Inline{
- subject: "Subject line",
- from: %Address{ email: "from@me.com" },
- text: "text content",
- html: "html content"
- }
+ subject: "Subject line",
+ from: %Address{email: "from@me.com"},
+ text: "text content",
+ html: "html content"
+ }
end
def basic_transmission do
@@ -49,25 +57,28 @@ defmodule SparkPost.TransmissionTest do
end
defmodule TestRequests do
+ @moduledoc false
def test_send(req, test_fn) do
- with_mock HTTPoison, [
- request: handle_send(test_fn)
- ] do
+ with_mock HTTPoison,
+ request: handle_send(test_fn) do
Transmission.send(req)
end
end
defp handle_send(response_test_fn) do
- fn (method, url, body, headers, opts) ->
- req = Poison.decode!(body, [keys: :atoms])
- fullreq = struct(Transmission, %{
- req |
- options: struct(Transmission.Options, req.options),
- recipients: parse_recipients_field(req.recipients),
- content: Content.to_content(req.content)
- })
+ fn method, url, body, headers, opts ->
+ req = Poison.decode!(body, %{keys: :atoms})
+
+ fullreq =
+ struct(Transmission, %{
+ req
+ | options: struct(Transmission.Options, req.options),
+ recipients: parse_recipients_field(req.recipients),
+ content: Content.to_content(req.content)
+ })
+
response_test_fn.(fullreq)
- MockServer.mk_resp.(method, url, body, headers, opts)
+ MockServer.mk_resp().(method, url, body, headers, opts)
end
end
@@ -95,26 +106,27 @@ defmodule SparkPost.TransmissionTest do
end
test "Transmission.send succeeds with Transmission.Response" do
- with_mock HTTPoison, [request: MockServer.mk_resp] do
- resp = Transmission.send(TestStructs.basic_transmission)
+ with_mock HTTPoison, request: MockServer.mk_resp() do
+ resp = Transmission.send(TestStructs.basic_transmission())
assert %Transmission.Response{} = resp
end
end
test "Transmission.send fails with Endpoint.Error" do
- with_mock HTTPoison, [request: MockServer.mk_fail] do
- req = TestStructs.basic_transmission
+ with_mock HTTPoison, request: MockServer.mk_fail() do
+ req = TestStructs.basic_transmission()
resp = Transmission.send(req)
assert %SparkPost.Endpoint.Error{} = resp
end
end
test "Transmission.send emits a POST" do
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :post
- MockServer.mk_resp.(method, url, body, headers, opts)
- end] do
- Transmission.send(TestStructs.basic_transmission)
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :post
+ MockServer.mk_resp().(method, url, body, headers, opts)
+ end do
+ Transmission.send(TestStructs.basic_transmission())
end
end
@@ -126,9 +138,10 @@ defmodule SparkPost.TransmissionTest do
sandbox: false,
skip_suppression: false
}
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | options: transopts},
- &(assert &1.options == transopts)
+ %{TestStructs.basic_transmission() | options: transopts},
+ &assert(&1.options == transopts)
)
end
@@ -137,33 +150,40 @@ defmodule SparkPost.TransmissionTest do
TestStructs.full_addr_recipient("You There", "you@there.com"),
TestStructs.full_addr_recipient("Them There", "them@there.com")
]
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: recipients},
- &(assert &1.recipients == recipients)
+ %{TestStructs.basic_transmission() | recipients: recipients},
+ &assert(&1.recipients == recipients)
)
end
test "Transmission.send accepts a list of long-form recipient email addresses" do
# RFC2822 3.4: Address Specification
recipients = ["You There ", "You Too There "]
- expected = Enum.map recipients, fn recip ->
- TestStructs.full_addr_recipient(recip)
- end
+
+ expected =
+ Enum.map(recipients, fn recip ->
+ TestStructs.full_addr_recipient(recip)
+ end)
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: recipients},
- &(assert &1.recipients == expected)
+ %{TestStructs.basic_transmission() | recipients: recipients},
+ &assert(&1.recipients == expected)
)
end
test "Transmission.send accepts a list of short-form recipient email addresses" do
# RFC2822 3.4.1: Addr-spec specification
recipients = ["you@there.com", "youtoo@theretoo.com"]
- expected = Enum.map recipients, fn recip ->
- TestStructs.addr_spec_recipient(recip)
- end
+
+ expected =
+ Enum.map(recipients, fn recip ->
+ TestStructs.addr_spec_recipient(recip)
+ end)
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: recipients},
- &(assert &1.recipients == expected)
+ %{TestStructs.basic_transmission() | recipients: recipients},
+ &assert(&1.recipients == expected)
)
end
@@ -174,6 +194,7 @@ defmodule SparkPost.TransmissionTest do
recip3 = %{name: "And You", email: "and@you.com"}
recip4 = %{email: "me@too.com"}
recipients = [recip0, recip1, recip2, recip3, recip4]
+
expected = [
TestStructs.full_addr_recipient(recip0),
TestStructs.addr_spec_recipient(recip1),
@@ -181,33 +202,40 @@ defmodule SparkPost.TransmissionTest do
%Recipient{address: %Address{name: recip3.name, email: recip3.email}},
%Recipient{address: %Address{email: recip4.email}}
]
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: recipients},
- &(assert &1.recipients == expected)
+ %{TestStructs.basic_transmission() | recipients: recipients},
+ &assert(&1.recipients == expected)
)
end
test "Transmission.send requires correctly-formatted email addresses" do
- assert_raise Address.FormatError, fn -> TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: "paula and paul"}, &(&1))
+ assert_raise Address.FormatError, fn ->
+ TestRequests.test_send(
+ %{TestStructs.basic_transmission() | recipients: "paula and paul"},
+ & &1
+ )
end
end
test "Transmission.send marshals recipient lists correctly" do
recip_lst = %Recipient.ListRef{list_id: "list101"}
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | recipients: recip_lst},
- &(assert &1.recipients == recip_lst)
+ %{TestStructs.basic_transmission() | recipients: recip_lst},
+ &assert(&1.recipients == recip_lst)
)
end
test "Transmission.send marshals inline raw content correctly" do
content = %Content.Raw{
- email_rfc822: "Content-Type: text/plain\r\nTo: \"{{address.name}}\" <{{address.email}}>\r\n\r\n We are testing Elixir and SparkPost together\r\n"
+ email_rfc822:
+ "Content-Type: text/plain\r\nTo: \"{{address.name}}\" <{{address.email}}>\r\n\r\n We are testing Elixir and SparkPost together\r\n"
}
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | content: content},
- &(assert &1.content == content)
+ %{TestStructs.basic_transmission() | content: content},
+ &assert(&1.content == content)
)
end
@@ -217,14 +245,16 @@ defmodule SparkPost.TransmissionTest do
subject: "Testing SparkPost and Elixir",
text: "We all live in a transient theoretical construct"
}
+
expected = %Content.Inline{
from: %Address{email: "me@here.com"},
subject: "Testing SparkPost and Elixir",
text: "We all live in a transient theoretical construct"
}
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | content: content},
- &(assert &1.content == expected)
+ %{TestStructs.basic_transmission() | content: content},
+ &assert(&1.content == expected)
)
end
@@ -233,33 +263,35 @@ defmodule SparkPost.TransmissionTest do
template_id: "template101",
use_draft_template: true
}
+
TestRequests.test_send(
- %{TestStructs.basic_transmission | content: content},
- &(assert &1.content == content)
+ %{TestStructs.basic_transmission() | content: content},
+ &assert(&1.content == content)
)
end
test "Transmission.list succeeds with a list of Transmission" do
- with_mock HTTPoison, [request: MockServer.mk_list] do
- resp = Transmission.list
+ with_mock HTTPoison, request: MockServer.mk_list() do
+ resp = Transmission.list()
assert is_list(resp)
Enum.each(resp, fn r -> assert %Transmission{} = r end)
end
end
test "Transmission.list fails with Endpoint.Error" do
- with_mock HTTPoison, [request: MockServer.mk_fail] do
- resp = Transmission.list
+ with_mock HTTPoison, request: MockServer.mk_fail() do
+ resp = Transmission.list()
assert %SparkPost.Endpoint.Error{} = resp
end
end
test "Transmission.list emits a GET" do
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :get
- MockServer.mk_list.(method, url, body, headers, opts)
- end] do
- Transmission.list
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :get
+ MockServer.mk_list().(method, url, body, headers, opts)
+ end do
+ Transmission.list()
end
end
@@ -267,10 +299,11 @@ defmodule SparkPost.TransmissionTest do
end
test "Transmission.get emits a GET" do
- with_mock HTTPoison, [request: fn (method, url, body, headers, opts) ->
- assert method == :get
- MockServer.mk_get.(method, url, body, headers, opts)
- end] do
+ with_mock HTTPoison,
+ request: fn method, url, body, headers, opts ->
+ assert method == :get
+ MockServer.mk_get().(method, url, body, headers, opts)
+ end do
Transmission.get("TRANSMISSION_ID")
end
end