Current section
Files
Jump to
Current section
Files
lib/listmonk/lists.ex
defmodule Listmonk.Lists do
@moduledoc """
Functions for managing Listmonk mailing lists.
"""
alias Listmonk.{Client, Config, Error}
alias Listmonk.Models.MailingList
@doc """
Retrieves all mailing lists.
## Examples
iex> Listmonk.Lists.get()
{:ok, [%MailingList{}, ...]}
"""
@spec get(Config.t() | nil) :: {:ok, list(MailingList.t())} | {:error, Error.t()}
def get(config \\ nil) do
path = "/api/lists?page=1&per_page=1000000"
case Client.get(path, config) do
{:ok, %{"data" => %{"results" => results}}} ->
lists = Enum.map(results, &MailingList.from_api/1)
{:ok, lists}
error ->
error
end
end
@doc """
Retrieves all mailing lists. Raises on error.
"""
@spec get!(Config.t() | nil) :: list(MailingList.t())
def get!(config \\ nil) do
case get(config) do
{:ok, lists} -> lists
{:error, error} -> raise error
end
end
@doc """
Retrieves a mailing list by ID.
## Examples
iex> Listmonk.Lists.get_by_id(7)
{:ok, %MailingList{}}
"""
@spec get_by_id(integer(), Config.t() | nil) ::
{:ok, MailingList.t() | nil} | {:error, Error.t()}
def get_by_id(id, config \\ nil) do
path = "/api/lists/#{id}"
case Client.get(path, config) do
{:ok, %{"data" => data}} when is_map(data) ->
# Handle the API bug where sometimes it returns results array
list_data =
case data do
%{"results" => results} when is_list(results) ->
Enum.find(results, &(&1["id"] == id))
_ ->
data
end
if list_data do
{:ok, MailingList.from_api(list_data)}
else
{:ok, nil}
end
error ->
error
end
end
@doc """
Retrieves a mailing list by ID. Raises on error.
"""
@spec get_by_id!(integer(), Config.t() | nil) :: MailingList.t() | nil
def get_by_id!(id, config \\ nil) do
case get_by_id(id, config) do
{:ok, list} -> list
{:error, error} -> raise error
end
end
@doc """
Creates a new mailing list.
## Attributes
- `:name` (required) - Name of the list
- `:type` - List type (:public or :private), default: :public
- `:optin` - Opt-in type (:single or :double), default: :single
- `:tags` - List of tags
- `:description` - Description of the list
## Examples
iex> Listmonk.Lists.create(%{name: "Newsletter"})
{:ok, %MailingList{}}
iex> Listmonk.Lists.create(%{
...> name: "VIP List",
...> type: :private,
...> optin: :double,
...> tags: ["vip", "premium"]
...> })
{:ok, %MailingList{}}
"""
@spec create(map(), Config.t() | nil) :: {:ok, MailingList.t()} | {:error, Error.t()}
def create(attrs, config \\ nil) do
with {:ok, payload} <- build_create_payload(attrs) do
case Client.post("/api/lists", config, json: payload) do
{:ok, %{"data" => data}} -> {:ok, MailingList.from_api(data)}
error -> error
end
end
end
@doc """
Creates a new mailing list. Raises on error.
"""
@spec create!(map(), Config.t() | nil) :: MailingList.t()
def create!(attrs, config \\ nil) do
case create(attrs, config) do
{:ok, list} -> list
{:error, error} -> raise error
end
end
@doc """
Deletes a mailing list by ID.
## Examples
iex> Listmonk.Lists.delete(7)
{:ok, true}
"""
@spec delete(integer(), Config.t() | nil) :: {:ok, boolean()} | {:error, Error.t()}
def delete(id, config \\ nil) do
case get_by_id(id, config) do
{:ok, nil} ->
{:ok, false}
{:ok, _list} ->
case Client.delete("/api/lists/#{id}", config) do
{:ok, %{"data" => result}} -> {:ok, result == true}
error -> error
end
error ->
error
end
end
@doc """
Deletes a mailing list. Raises on error.
"""
@spec delete!(integer(), Config.t() | nil) :: boolean()
def delete!(id, config \\ nil) do
case delete(id, config) do
{:ok, result} -> result
{:error, error} -> raise error
end
end
# Private functions
defp build_create_payload(attrs) do
name = Map.get(attrs, :name) |> validate_required(:name) |> String.trim()
list_type = Map.get(attrs, :type, :public)
optin = Map.get(attrs, :optin, :single)
unless list_type in [:public, :private] do
raise ArgumentError, "type must be :public or :private"
end
unless optin in [:single, :double] do
raise ArgumentError, "optin must be :single or :double"
end
payload = %{
"name" => name,
"type" => to_string(list_type),
"optin" => to_string(optin)
}
payload =
case Map.get(attrs, :tags) do
nil -> payload
tags -> Map.put(payload, "tags", tags)
end
payload =
case Map.get(attrs, :description) do
nil -> payload
description -> Map.put(payload, "description", description)
end
{:ok, payload}
rescue
e in ArgumentError -> {:error, Error.new(e.message)}
end
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
end