lib/http.ex

defmodule Gogs.Http do
  @moduledoc """
  Documentation for the `HTTP` "verb" functions.
  Should be self-explanatory.
  Each function documented & typespecd.  
  If anything is unclear, please open an issue: 
  [github.com/dwyl/**gogs/issues**](https://github.com/dwyl/gogs/issues)
  """
  require Logger

  @mock Application.compile_env(:gogs, :mock)
  Logger.debug("GogsHttp > config :gogs, mock: #{to_string(@mock)}")
  @httpoison (@mock && Gogs.HTTPoisonMock) || HTTPoison

  defp access_token do
    Envar.get("GOGS_ACCESS_TOKEN")
  end

  defp auth_header do
    {"Authorization", "token #{access_token()}"}
  end

  defp json_headers do
    [
      {"Accept", "application/json"},
      auth_header(),
      {"Content-Type", "application/json"}
    ]
  end

  @doc """
  `inject_poison/0` injects a TestDouble of HTTPoison in Test.
  see: https://github.com/dwyl/elixir-auth-google/issues/35
  """
  def inject_poison, do: @httpoison

  @doc """
  `parse_body_response/1` parses the response returned by the Gogs Server
  so your app can use the resulting JSON.
  """
  @spec parse_body_response({atom, String.t()} | {:error, any}) :: {:ok, map} | {:error, any}
  def parse_body_response({:error, err}), do: {:error, err}

  def parse_body_response({:ok, response}) do
    # Logger.debug(response) # very noisy!
    body = Map.get(response, :body)
    if body == nil || byte_size(body) == 0 do
      Logger.warning("GogsHttp.parse_body_response: response body is nil!")
      {:error, :no_body}
    else
      {:ok, str_key_map} = Jason.decode(body)
      # make keys of map atoms for easier access in templates etc.
      {:ok, Useful.atomize_map_keys(str_key_map)}
    end
  end

  @doc """
  `get/1` accepts one argument: `url` the REST API endpoint. 
  Makes an `HTTP GET` request to the specified `url`.
  Auth Headers and Content-Type are implicit.
  returns `{:ok, map}`
  """
  @spec get(String.t()) :: {:ok, map} | {:error, any}
  def get(url) do
    Logger.debug("GogsHttp.get #{url}")
    inject_poison().get(url, json_headers())
    |> parse_body_response()
  end

  @doc """
  `get_raw/1` as it's name suggests gets the raw data
  (expects the reponse to be plaintext not JSON)
  accepts one argument: `url` the REST API endpoint. 
  Makes an `HTTP GET` request to the specified `url`.
  Auth Headers and Content-Type are implicit.
  returns `{:ok, map}`
  """
  @spec get_raw(String.t()) :: {:ok, map} | {:error, any}
  def get_raw(url) do
    Logger.debug("GogsHttp.get_raw #{url}")
    inject_poison().get(url, [auth_header()])
  end

  @doc """
  `post_raw_html/2` accepts two arguments: `url` and `raw_markdown`. 
  Makes an `HTTP POST` request to the specified `url`
  passing in the `params` as the request body.
  Does NOT attempt to parse the response body as JSON.
  Auth Headers and Content-Type are implicit.
  """
  @spec post_raw_html(String.t(), String.t()) :: {:ok, String.t()} | {:error, any}
  def post_raw_html(url, raw_markdown) do
    Logger.debug("GogsHttp.post_raw #{url}")
    # Logger.debug("raw_markdown: #{raw_markdown}")
    headers = [
      {"Accept", "text/html"},
      auth_header(),
    ]
    inject_poison().post(url, raw_markdown, headers)
  end

  @doc """
  `post/2` accepts two arguments: `url` and `params`. 
  Makes an `HTTP POST` request to the specified `url`
  passing in the `params` as the request body.
  Auth Headers and Content-Type are implicit.
  """
  @spec post(String.t(), map) :: {:ok, map} | {:error, any}
  def post(url, params \\ %{}) do
    Logger.debug("GogsHttp.post #{url}")
    body = Jason.encode!(params)
    inject_poison().post(url, body, json_headers())
    |> parse_body_response()
  end

  @doc """
  `delete/1` accepts a single argument `url`; 
  the `url` for the repository to be deleted.
  """
  @spec delete(String.t()) :: {:ok, map} | {:error, any}
  def delete(url) do
    Logger.debug("GogsHttp.delete #{url}")
    inject_poison().delete(url <> "?token=#{access_token()}")
    |> parse_body_response()
  end
end