Skip to main content

lib/sendgrid/templates/templates.ex

defmodule SendGrid.Templates do
  alias SendGrid.Template
  @base_api_url "/v3/templates"
  @valid_generations [:legacy, :dynamic]
  @success_codes [200,201,202,203,204]

  #----------------------------------------

  # Result Set

  #----------------------------------------

  defstruct [
    templates: [],
    metadata: nil,
  ]

  @type t :: %SendGrid.Templates{
               templates: [SendGrid.Template.t],
               metadata: SendGrid.MetaData.t | nil,
             }

  @spec new(SendGrid.Response.t, SendGrid.query()) :: Templates.t | {:error, [String.t]} | {:error, String.t}
  def new(%SendGrid.Response{body: %{"_metadata" => metadata, "result" => result}}, options) do
    %__MODULE__{
      templates: Enum.map(result, &(SendGrid.Template.new(&1, :json))),
      metadata: SendGrid.MetaData.new(metadata, options, :json)
    }
  end
  def new(%SendGrid.Response{body: %{"templates" => templates}}, options) do
    %__MODULE__{
      templates: Enum.map(templates, &(SendGrid.Template.new(&1, :json))),
      metadata: SendGrid.MetaData.new(%{}, options, :json)
    }
  end
  def new(_), do: {:error, "#{__MODULE__} Unsupported Initializer"}

  #----------------------------------------

  # Pagination

  #----------------------------------------

  @spec next(SendGrid.Templates.t, SendGrid.query()) :: Templates.t | {:error, [String.t]} | {:error, String.t}
  def next(self, options \\ [])
  def next(%SendGrid.Templates{metadata: %SendGrid.MetaData{next: nil}}, options) do
    nil
  end
  def next(%SendGrid.Templates{} = self, options) do
    # Note only api_key may be changed when calling next

    options = cond do
                api_key = options[:api_key] -> Keyword.put(self.metadata.options || [], :api_key, api_key)
                :else -> self.metadata.options || []
              end
    options = cond do
                query = options[:query] ->
                  query = Keyword.put(query, :page_token, self.metadata.next)
                  Keyword.put(options, :query, query)
                :else ->
                  Keyword.put(options, :query, [page_token: self.metadata.next])
              end
    fetch = SendGrid.get(@base_api_url, options)
    case fetch do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        __MODULE__.new(response, options)
      { :ok, %SendGrid.Response{ body: body } } ->
        { :error, body["errors"] || body["error"] }
      _ ->
        { :error, "Unable to communicate with SendGrid API." }
    end
  end

  #----------------------------------------

  # CRUD

  #----------------------------------------

  @spec get(String.t, SendGrid.query()) :: SendGrid.DynamicTemplate.t | SendGrid.LegacyTemplate.t | {:error, [String.t]} | {:error, String.t}
  def get(identifier, options \\ []) do
    options = patch_options(options)
    case SendGrid.get(@base_api_url <> "/#{identifier}", options) do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        SendGrid.Template.new(response.body, :json)
      { :ok, %SendGrid.Response{ body: body } } -> { :error, body["errors"] || body["error"] }
      _ -> { :error, "Unable to communicate with SendGrid API." }
    end
  end

  @spec update(SendGrid.LegacyTemplate.t | SendGrid.DynamicTemplate.t, SendGrid.query()) :: SendGrid.DynamicTemplate.t | SendGrid.LegacyTemplate.t | {:error, [String.t]} | {:error, String.t}
  def update(template, options \\ [])
  def update(%SendGrid.LegacyTemplate{} = template, options) do
    options = patch_options(options)
    case SendGrid.patch(@base_api_url <> "/#{template.id}", template, options) do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        SendGrid.Template.new(response.body, :json)
      { :ok, %SendGrid.Response{ body: body } } ->
        { :error, body["errors"] || body["error"] }
      _ -> { :error, "Unable to communicate with SendGrid API." }
    end
  end
  def update(%SendGrid.DynamicTemplate{} = template, options) do
    options = patch_options(options)
    case SendGrid.patch(@base_api_url <> "/#{template.id}", template, options) do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        SendGrid.Template.new(response.body, :json)
      { :ok, %SendGrid.Response{ body: body } } ->
        { :error, body["errors"] || body["error"] }
      _ -> { :error, "Unable to communicate with SendGrid API." }
    end
  end

  @spec create(SendGrid.LegacyTemplate.t | SendGrid.DynamicTemplate.t, SendGrid.query()) :: SendGrid.DynamicTemplate.t | SendGrid.LegacyTemplate.t | {:error, [String.t]} | {:error, String.t}
  def create(template, options \\ [])
  def create(%SendGrid.LegacyTemplate{} = template, options) do
    options = patch_options(options)
    case SendGrid.post(@base_api_url, template, options) do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        SendGrid.Template.new(response.body, :json)
      { :ok, %SendGrid.Response{ body: body }} ->
        { :error, body["errors"] || body["error"] }
      _ -> { :error, "Unable to communicate with SendGrid API." }
    end
  end
  def create(%SendGrid.DynamicTemplate{} = template, options) do
    options = patch_options(options)
    case SendGrid.post(@base_api_url, template, options) do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        SendGrid.Template.new(response.body, :json)
      { :ok, %SendGrid.Response{ body: body }} ->
        { :error, body["errors"] || body["error"] }
      _ -> { :error, "Unable to communicate with SendGrid API." }
    end
  end

  @spec delete(String.t | SendGrid.DynamicTemplate.t | Sendgrid.LegacyTemplate.t, SendGrid.query()) :: :ok | {:error, [String.t]} | {:error, String.t}
  def delete(template, options \\ [])
  def delete(%SendGrid.LegacyTemplate{id: id}, options) do
    delete(id, options)
  end
  def delete(%SendGrid.DynamicTemplate{id: id}, options) do
    delete(id, options)
  end
  def delete(identifier, options) when is_bitstring(identifier) do
    options = patch_options(options)
    case SendGrid.delete(@base_api_url <> "/#{identifier}", options) do
      { :ok, %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        :ok
      { :ok, %SendGrid.Response{ body: body } } ->
        { :error, body["errors"] || body["error"] }
      _ ->
        { :error, "Unable to communicate with SendGrid API." }
    end
  end

  @spec list(SendGrid.query()) :: SendGrid.Templates.t | {:error, [String.t]} | {:error, String.t}
  def list(options \\ []) do
    options = patch_options(options)
    fetch = SendGrid.get(@base_api_url, options)
    case fetch do
      { :ok, response = %SendGrid.Response{ status: status_code } } when status_code in @success_codes ->
        __MODULE__.new(response, options)
      { :ok, %SendGrid.Response{ body: body } } ->
        { :error, body["errors"] || body["error"] }
      _ ->
        { :error, "Unable to communicate with SendGrid API." }
    end
  end

  #----------------------------------------

  # Support

  #----------------------------------------

  @doc """
    Param injector does not handle list data, overriding here to allow user to pass in array or desired generations.
  """
  @spec patch_options(SendGrid.query()) :: SendGrid.query()
  def patch_options(options \\ []) do
    case options[:query][:generations] do
      v when is_list(v) ->
        generations = v
                      |> Enum.map(&("#{&1}"))
                      |> Enum.join(",")
        put_in(options, [:query, :generations], generations)
      v when is_atom(v) -> options
      v when is_bitstring(v) -> options
      _else -> options
    end
  end

end