Packages

Elixir client for the Listmonk open-source email platform API

Current section

Files

Jump to
listmonk_client lib listmonk templates.ex
Raw

lib/listmonk/templates.ex

defmodule Listmonk.Templates do
@moduledoc """
Functions for managing Listmonk templates.
"""
alias Listmonk.{Server, Error}
alias Listmonk.Models.Template
@type server :: pid() | atom()
@doc """
Retrieves all templates.
"""
@spec get(server()) :: {:ok, list(Template.t())} | {:error, Error.t()}
def get(server) do
path = "/api/templates?page=1&per_page=1000000"
case Server.request(server, :get, path) do
{:ok, %{"data" => results}} when is_list(results) ->
templates = Enum.map(results, &Template.from_api/1)
{:ok, templates}
error ->
error
end
end
@doc """
Retrieves a template by ID.
"""
@spec get_by_id(server(), integer()) :: {:ok, Template.t() | nil} | {:error, Error.t()}
def get_by_id(server, id) do
path = "/api/templates/#{id}"
case Server.request(server, :get, path) do
{:ok, %{"data" => data}} when is_map(data) and map_size(data) > 0 ->
{:ok, Template.from_api(data)}
{:ok, %{"data" => _}} ->
{:ok, nil}
error ->
error
end
end
@doc """
Retrieves a template preview by ID.
"""
@spec preview(server(), integer()) :: {:ok, String.t()} | {:error, Error.t()}
def preview(server, id) do
path = "/api/templates/#{id}/preview"
case Server.request(server, :get, path) do
{:ok, %{"data" => preview}} when is_binary(preview) -> {:ok, preview}
{:ok, response} -> {:ok, to_string(response)}
error -> error
end
end
@doc """
Creates a new template.
## Attributes
- `:name` (required) - Template name
- `:body` (required) - Template body HTML (must include `{{ template "content" . }}`)
- `:type` - Template type (:campaign or :tx), default: :campaign
- `:subject` - Default subject (for tx templates)
- `:is_default` - Set as default template, default: false
"""
@spec create(server(), map()) :: {:ok, Template.t()} | {:error, Error.t()}
def create(server, attrs) do
with {:ok, payload} <- build_create_payload(attrs) do
case Server.request(server, :post, "/api/templates", json: payload) do
{:ok, %{"data" => data}} -> {:ok, Template.from_api(data)}
error -> error
end
end
end
@doc """
Updates a template.
"""
@spec update(server(), Template.t(), map()) :: {:ok, Template.t()} | {:error, Error.t()}
def update(server, %Template{id: id} = template, attrs) do
updated_template = merge_template_attrs(template, attrs)
payload = Template.to_api(updated_template)
case Server.request(server, :put, "/api/templates/#{id}", json: payload) do
{:ok, _response} -> get_by_id(server, id)
error -> error
end
end
@doc """
Deletes a template by ID.
"""
@spec delete(server(), integer()) :: {:ok, boolean()} | {:error, Error.t()}
def delete(server, id) do
case get_by_id(server, id) do
{:ok, nil} ->
{:ok, false}
{:ok, _template} ->
case Server.request(server, :delete, "/api/templates/#{id}") do
{:ok, %{"data" => result}} -> {:ok, result == true}
error -> error
end
error ->
error
end
end
@doc """
Sets a template as the default template.
"""
@spec set_default(server(), integer()) :: {:ok, boolean()} | {:error, Error.t()}
def set_default(server, id) do
case get_by_id(server, id) do
{:ok, nil} ->
{:ok, false}
{:ok, _template} ->
case Server.request(server, :put, "/api/templates/#{id}/default") do
{:ok, %{"data" => result}} -> {:ok, result == true}
error -> error
end
error ->
error
end
end
# Private functions
defp build_create_payload(attrs) do
name = Map.get(attrs, :name) |> validate_required(:name) |> String.trim()
body = Map.get(attrs, :body) |> validate_required(:body)
unless String.contains?(body, ~s({{ template "content" . }})) do
raise ArgumentError,
~s(The placeholder {{ template "content" . }} should appear in the template body)
end
payload = %{
"name" => name,
"body" => body,
"type" => template_type_to_string(Map.get(attrs, :type)),
"is_default" => Map.get(attrs, :is_default, false)
}
payload =
case Map.get(attrs, :subject) do
nil -> payload
subject -> Map.put(payload, "subject", subject)
end
{:ok, payload}
rescue
e in ArgumentError -> {:error, Error.new(e.message)}
end
defp template_type_to_string(:campaign), do: "campaign"
defp template_type_to_string(:tx), do: "tx"
defp template_type_to_string(nil), do: "campaign"
defp template_type_to_string(type) when is_binary(type), do: type
defp validate_required(nil, field), do: raise(ArgumentError, "#{field} is required")
defp validate_required("", field), do: raise(ArgumentError, "#{field} is required")
defp validate_required(value, _field), do: value
defp merge_template_attrs(template, attrs) do
%{
template
| name: Map.get(attrs, :name, template.name),
subject: Map.get(attrs, :subject, template.subject),
body: Map.get(attrs, :body, template.body),
type: Map.get(attrs, :type, template.type)
}
end
end