lib/ex_azure_key_vault/http_utils.ex

defmodule ExAzureKeyVault.HTTPUtils do
  @moduledoc """
  Internal module for returning HTTP utilities.
  """

  @doc """
  Returns "application/x-www-form-urlencoded" header.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.headers_form_urlencoded()
      ["Content-Type": "application/x-www-form-urlencoded"]

  """
  @spec headers_form_urlencoded :: list
  def headers_form_urlencoded do
    ["Content-Type": "application/x-www-form-urlencoded"]
  end

  @doc """
  Returns authorization header.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.headers_authorization("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
      ["Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "Content-Type": "application/json; charset=utf-8"]

  """
  @spec headers_authorization(String.t()) :: list
  def headers_authorization(bearer_token) do
    [Authorization: bearer_token, "Content-Type": "application/json; charset=utf-8"]
  end

  @doc """
  Returns ssl options.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.options_ssl()
      [ssl: [{:versions, [:'tlsv1.2']}]]

  """
  @spec options_ssl :: list
  def options_ssl do
    [ssl: [{:versions, [:"tlsv1.2"]}]]
  end

  @doc """
  Returns ok response.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.response_ok("{}")
      {:ok, %{}}

  """
  @spec response_ok(String.t()) :: {:ok, any}
  def response_ok(body) do
    response = Jason.decode!(body)
    {:ok, response}
  end

  @doc """
  Returns basic error message for 4xx status codes.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error(401, "https://wrong-vault.vault.azure.net/secrets")
      {:error, "Error: 401: https://wrong-vault.vault.azure.net/secrets"}

  """
  @spec response_client_error(integer, String.t()) :: {:error, String.t()} | nil
  def response_client_error(status, url) do
    if is_client_error(status) do
      {:error, "Error: #{status}: #{url}"}
    end
  end

  @doc """
  Returns error message for 4xx status codes.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error(404, "", "{\\"error_message\\":\\"Not found\\"}")
      {:error, %{"error_message" => "Not found"}}

  """
  @spec response_client_error(integer, String.t(), String.t()) :: {:error, any} | nil
  def response_client_error(status, _url, body) do
    if is_client_error(status) do
      response = Jason.decode!(body)
      {:error, response}
    end
  end

  @doc """
  Returns basic error or :ok response.

  ## Examples

  When is a client error.

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error_or_ok(401, "https://wrong-vault.vault.azure.net/secrets")
      {:error, "Error: 401: https://wrong-vault.vault.azure.net/secrets"}

  When is a redirection.

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error_or_ok(301, "https://wrong-vault.vault.azure.net/secrets")
      :ok

  """
  @spec response_client_error_or_ok(integer, String.t()) :: {:error, String.t()} | :ok
  def response_client_error_or_ok(status, url) do
    response_client_error(status, url) || :ok
  end

  @doc """
  Returns client error or :ok response.

  ## Examples

  When is a client error.

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error_or_ok(404, "", "{\\"error_message\\":\\"Not found\\"}")
      {:error, %{"error_message" => "Not found"}}

  When is a redirection.

      iex(1)> ExAzureKeyVault.HTTPUtils.response_client_error_or_ok(301, "", "{}")
      {:ok, %{}}

  """
  @spec response_client_error_or_ok(integer, String.t(), String.t()) ::
          {:error, String.t()} | {:ok, String.t()}
  def response_client_error_or_ok(status, url, body) do
    response_client_error(status, url, body) || response_ok(body)
  end

  @doc """
  Returns error message for :nxdomain error.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.response_server_error(:nxdomain, "https://wrong-vault.vault.azure.net/secrets")
      {:error, "Error: Couldn't resolve host name https://wrong-vault.vault.azure.net/secrets"}

  """
  @spec response_server_error(atom, String.t()) :: {:error, String.t()}
  def response_server_error(:nxdomain, url) do
    {:error, "Error: Couldn't resolve host name #{url}"}
  end

  @doc """
  Returns error message for server errors.

  ## Examples

      iex(1)> ExAzureKeyVault.HTTPUtils.response_server_error(:econnrefused)
      {:error, :econnrefused}

  """
  @spec response_server_error(atom) :: {:error, any}
  def response_server_error(reason) do
    {:error, reason}
  end

  @spec is_client_error(integer) :: boolean
  defp is_client_error(status) do
    status
    |> Integer.to_string()
    |> String.starts_with?("4")
  end
end