summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/couch.ex96
1 files changed, 86 insertions, 10 deletions
diff --git a/lib/couch.ex b/lib/couch.ex
index fdd8579..2d75314 100644
--- a/lib/couch.ex
+++ b/lib/couch.ex
@@ -1,18 +1,94 @@
defmodule Couch do
- def get(db, doc) do
- config = Application.get_env(:lsg, :couch)
- url = [Keyword.get(config, :url), db, doc] |> Enum.join("/")
- user = Keyword.get(config, :user)
- pass = Keyword.get(config, :pass)
- client_options = Keyword.get(config, :client_options, [])
- headers = [{"accept", "application/json"}, {"user-agent", "beautte"}]
- options = [hackney: [:insecure, {:basic_auth, {user, pass}}]] ++ client_options
+ @moduledoc """
+ Simple, no-frills, CouchDB client
+ """
+
+ @type base_error :: {:error, :bad_request} | {:error, :unauthorized} |
+ {:error, :server_error} | {:error, :service_unavailable} |
+ {:error, :bad_response} | {:error, HTTPoison.Error.t()}
+
+ def new(db, params \\ []) do
+ {url, headers, options} = prepare_request([db], [], params)
+ case HTTPoison.put(url, headers, options) do
+ {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
+ :ok
+ {:ok, %HTTPoison.Response{status_code: 412}} ->
+ {:error, :exists}
+ error ->
+ handle_generic_response(error)
+ end
+ end
+
+ @doc """
+ Retrieve a document `doc` from `db`.
+
+ `params` are [documented here](https://docs.couchdb.org/en/3.2.2-docs/api/document/common.html)
+ """
+ @spec get(String.t(), String.t(), Keyword.t()) :: {:ok, Map.t()} | {:error, :not_found} | {:error, any()}
+ def get(db, doc, params \\ []) do
+ {url, headers, options} = prepare_request([db, doc], [], params)
case HTTPoison.get(url, headers, options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Poison.decode!(body)}
- {:ok, %HTTPoison.Response{status_code: 404}} ->
- {:error, :not_found}
error -> {:error, {:couchdb_error, error}}
end
end
+
+ @spec post(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :exists} | {:error, any()}
+ def post(db, data, params \\ []) do
+ {url, headers, options} = prepare_request([db], [{"content-type", "application/json"}], params)
+ with \
+ {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] <- HTTPoison.post(url, Poison.encode!(data), headers, options),
+ {:json, {:ok, %{"ok" => true, "id" => id, "rev" => rev}}} <- {:json, Poison.decode(body)} do
+ {:ok, id, rev}
+ else
+ {:ok, %HTTPoison.Response{status_code: 409}} -> {:error, :exists}
+ {:json, {:ok, body}} ->
+ Logger.error("couch: operation failed: #{inspect body}")
+ {:error, :operation_failed}
+ error -> handle_generic_response(error)
+ end
+ end
+
+ @spec put(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :conflict} | {:error, any()}
+ def put(db, doc = %{"_id" => id, "_rev" => _}, params \\ []) do
+ {url, headers, options} = prepare_request([db, id], [{"content-type", "application/json"}], params)
+ case HTTPoison.put(url, Poison.encode!(doc), headers, options) do
+ {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
+ body = Poison.decode!(body)
+ if Map.get(body, "ok") do
+ {:ok, Map.get(body, "id"), Map.get(body, "rev")}
+ else
+ {:error, :operation_failed}
+ end
+ {:ok, %HTTPoison.Response{status_code: 209}} ->
+ {:error, :conflict}
+ error -> handle_generic_response(error)
+ end
+ end
+
+ defp prepare_request(path, headers \\ [], params \\ [], options \\ []) do
+ config = Application.get_env(:lsg, :couch)
+
+ base_url = Keyword.get(config, :url, "http://localhost:5984")
+ url = URI.merge(base_url, Path.join(path)) |> to_string()
+
+ headers = headers ++ [{"accept", "application/json"}, {"user-agent", "beautte"}]
+
+ params = Enum.map(params, fn({k, v}) -> {to_string(k), v} end)
+ client_options = Keyword.get(config, :client_options, [])
+ options = [params: params] ++ options ++ client_options
+
+ user = Keyword.get(config, :user)
+ pass = Keyword.get(config, :pass)
+ hackney_options = Keyword.get(options, :hackney, [])
+ hackney_options = if user, do: [{:basic_auth, {user, pass}} | hackney_options], else: []
+ options = [{:hackney, [:insecure | hackney_options]} | options]
+
+ {url, headers, options}
+ end
+
+ defp handle_generic_response(%HTTPoison.Response{status_code: code}), do: {:error, Plug.Conn.Status.reason_atom(code)}
+ defp handle_generic_response(%HTTPoison.Error{reason: reason}), do: {:error, reason}
+
end