Skip to content

Template.create, Template.update, Template.delete #50

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule SparkPost.Endpoint do
- `:get`
- `:head`
- `:options`
- `:patch`
- `:patch`
- `:post`
- `:put`
- `endpoint`: SparkPost API endpoint as string ("transmissions", "templates", ...)
Expand Down Expand Up @@ -72,7 +72,7 @@ defmodule SparkPost.Endpoint 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
if decode_results && Map.has_key?(decoded_body, :results) do
%SparkPost.Endpoint.Response{status_code: code, results: decoded_body.results}
else
%SparkPost.Endpoint.Response{status_code: code, results: decoded_body}
Expand All @@ -98,7 +98,7 @@ defmodule SparkPost.Endpoint do
}
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
Expand Down
118 changes: 103 additions & 15 deletions lib/template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,29 @@ 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.
"""

defstruct id: nil,
name: nil,
content: %SparkPost.Content.Inline{},
published: false,
description: nil,
options: %SparkPost.Transmission.Options{},
shared_with_subaccounts: false,
has_draft: nil,
has_published: nil

alias SparkPost.Endpoint

@doc """
Expand All @@ -31,17 +43,93 @@ 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
|> SparkPost.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
16 changes: 16 additions & 0 deletions lib/template/options.ex
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions lib/template/response.ex
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions test/data/createtemplate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"results": {
"id": "TEMPLATE_ID"
}
}
9 changes: 9 additions & 0 deletions test/data/templatedelete_fail_404.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"errors": [
{
"message": "resource not found",
"code": "1600",
"description": "Template does not exist"
}
]
}
9 changes: 9 additions & 0 deletions test/data/templatedelete_fail_409.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"errors": [
{
"message": "resource conflict",
"code": "1602",
"description": "Template is in use by msg generation"
}
]
}
5 changes: 5 additions & 0 deletions test/data/updatetemplate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"results": {
"id": "TEMPLATE_ID"
}
}
12 changes: 11 additions & 1 deletion test/endpoint_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule SparkPost.EndpointTest do
end

test "Endpoint.request succeeds with Endpoint.Response" do
with_mock HTTPoison, [request: fn(_, _, _, _, _) ->
with_mock HTTPoison, [request: fn(_, _, _, _, _) ->
r = MockServer.mk_resp
r.(nil, nil, nil, nil, nil)
end] do
Expand Down Expand Up @@ -112,4 +112,14 @@ defmodule SparkPost.EndpointTest 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
Loading