defmodule Translixir do @moduledoc """ Documentation for `Translixir`. """ alias Translixir.Helpers.Time alias Translixir.Http.Client alias Translixir.Http.EntityHistory alias Translixir.Model.HistoryTimeRange # credo:disable-for-next-line @http_client Application.get_env(:translixir, :httpoison) @spec tx_log({:ok, pid}, any) :: {:error} | {:ok, any} @doc """ `tx_log({:ok, }, actions)` POSTs a collection of `Action` at CruxDB endpoint [`/tx-log`](https://www.opencrux.com/reference/20.09-1.12.1/http.html#tx-log-post) Usage: ```elixir put = action(:put, "{ :crux.db/id :jorge-3, :first-name \"Michael\", :last-name \"Jorge\", }") Client.new("localhost", "3000") |> tx_log(put) |> IO.inspect ``` Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `Action`: * `{ :crux.db/id :jorge-3, :first-name \"Michael\", :last-name \"Jorge\", }` Example Response: * `{:ok, "{:crux.tx/tx-id 7, :crux.tx/tx-time #inst \"2020-07-16T21:50:39.309-00:00\"}"}` """ def tx_log({:ok, client}, actions) do url = Client.endpoint(client, :tx_log) headers = Client.headers(client) response = @http_client.post(url, "#{actions}", headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec tx_log!(pid, any) :: any @doc """ `tx_log!(, actions)` POSTs a collection of `Action` at CruxDB endpoint `/tx-log` Returns: * `status_2XX` -> body * _ -> exception is raised """ def tx_log!(client, actions) when is_pid(client) do url = Client.endpoint(client, :tx_log) headers = Client.headers(client) response = @http_client.post(url, "#{actions}", headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at tx-log with body #{actions} did not return 200" end end @spec tx_logs({:ok, pid}) :: {:error} | {:ok, any} @doc """ `tx_logs({:ok, })` GETs at CruxDB endpoint [`/tx-log`](https://www.opencrux.com/reference/20.09-1.12.1/http.html#tx-log) Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example Response: ```elixir {:ok, ({:crux.tx/tx-id 0, :crux.tx/tx-time #inst \"2020-10-14T03:48:43.298-00:00\", :crux.tx.event/tx-events [[:crux.tx/put #crux/id \"be21bd5ae7f3334b9b8abb185dfbeae1623088b1\" #crux/id \"9d2c7102d6408d465f85b0b35dfb209b34daadd1\"]]} {:crux.tx/tx-id 1, :crux.tx/tx-time #inst \"2020-10-16T01:10:08.451-00:00\", :crux.tx.event/tx-events [[:crux.tx/put #crux/id \"be21bd5ae7f3334b9b8abb185dfbeae1623088b1\" #crux/id \"9d2c7102d6408d465f85b0b35dfb209b34daadd1\"]]})} ``` """ def tx_logs({:ok, client}) do url = Client.endpoint(client, :tx_log) headers = Client.headers(client) response = @http_client.get(url, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec tx_logs!(pid) :: any @doc """ `tx_logs!()` GETs at CruxDB endpoint `/tx-log` Returns: * `status_2XX` -> body * _ -> exception is raised """ def tx_logs!(client) when is_pid(client) do url = Client.endpoint(client, :tx_log) headers = Client.headers(client) response = @http_client.get(url, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "GET at tx-log did not return 200" end end @spec entity({:ok, atom | pid | {atom, any} | {:via, atom, any}}, atom | binary | integer) :: {:error} | {:error, atom} | {:ok, any} @doc """ 1. `entity({:ok, }, entity_crux_id)` 2. `entity({:ok, }, entity_crux_id, transaction_time, valid_time)` POSTs an ID at CruxDB endpoint [`/entity[?[transaction-time=]&[valid-time=]]`}(https://www.opencrux.com/reference/20.09-1.12.1/http.html#entity) * `transaction_time` and `valid_time` are in `DateTime` format Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `entity_crux_id`: * `":jorge-3"`, `:jorge_3` or `3` * `":jorge-3"` and `:jorge_3` are not equivalent Example Response: * `{:ok, { :crux.db/id :jorge-3, :first-name \"Michael\", :last-name \"Jorge\", }}` """ def entity({:ok, client}, entity_id) when is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id) do url = Client.endpoint(client, :entity) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec entity( {:ok, atom | pid | {atom, any} | {:via, atom, any}}, atom | binary | integer, DateTime.t(), DateTime.t() ) :: {:error} | {:error, atom} | {:ok, any} def entity({:ok, client}, entity_id, tx_time, valid_time) when is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id) do url = Time.build_timed_url(Client.endpoint(client, :entity), tx_time, valid_time) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec entity!(pid, atom | binary | integer, DateTime.t(), DateTime.t()) :: any @doc """ 1. `entity!(, entity_crux_id)` 2. `entity!(, entity_crux_id, transaction_time, valid_time)` POSTs an ID at CruxDB endpoint `/entity[?[transaction-time=]&[valid-time=]]` * `transaction_time` and `valid_time` are in `DateTime` format Returns: * `status_2XX` -> body * _ -> exception is raised Example `entity_crux_id`: * `":jorge-3"`, `:jorge_3` or `3` * `":jorge-3"` and `:jorge_3` are not equivalent Example Response: * `{ :crux.db/id :jorge-3, :first-name \"Michael\", :last-name \"Jorge\", }` """ def entity!(client, entity_id, tx_time, valid_time) when is_pid(client) and (is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id)) do url = Time.build_timed_url(Client.endpoint(client, :entity), tx_time, valid_time) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at entity with id #{entity_id} did not return 200" end end @spec entity!(pid, atom | binary | integer) :: any def entity!(client, entity_id) when is_pid(client) and (is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id)) do url = Client.endpoint(client, :entity) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at entity with id #{entity_id} did not return 200" end end @spec entity_tx({:ok, atom | pid | {atom, any} | {:via, atom, any}}, atom | binary | integer) :: {:error} | {:error, atom} | {:ok, any} @doc """ 1. `entity_tx({:ok, }, entity_crux_id)` 2. `entity_tx({:ok, }, entity_crux_id, transaction_time, valid_time)` POSTs an ID at CruxDB endpoint [`/entity-tx`](https://www.opencrux.com/reference/20.09-1.12.1/http.html#entity-tx) * `transaction_time` and `valid_time` are in `DateTime` format Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `entity_crux_id`: * `":jorge-3"`, `:jorge_3` or `3` * `":jorge-3"` and `:jorge_3` are not equivalent Example Response: * `{:ok, "{:crux.db/id #crux/id \"be21bd5ae7f3334b9b8abb185dfbeae1623088b1\", :crux.db/content-hash #crux/id \"9d2c7102d6408d465f85b0b35dfb209b34daadd1\", :crux.db/valid-time #inst \"2020-10-16T01:51:50.568-00:00\", :crux.tx/tx-time #inst \"2020-10-16T01:51:50.568-00:00\", :crux.tx/tx-id 4}"}` """ def entity_tx({:ok, client}, entity_id) when is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id) do url = Client.endpoint(client, :entity_tx) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec entity_tx( {:ok, atom | pid | {atom, any} | {:via, atom, any}}, atom | binary | integer, DateTime.t(), DateTime.t() ) :: {:error} | {:error, atom} | {:ok, any} def entity_tx({:ok, client}, entity_id, tx_time, valid_time) when is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id) do url = Time.build_timed_url(Client.endpoint(client, :entity_tx), tx_time, valid_time) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec entity_tx!(pid, atom | binary | integer) :: any @doc """ 1. `entity_tx!(, entity_crux_id)` 2. `entity_tx!(, entity_crux_id, transaction_time, valid_time)` POSTs an ID at CruxDB endpoint `/entity-tx` * `transaction_time` and `valid_time` are in `DateTime` format Returns: * `status_2XX` -> body * _ -> exception is raised Example `entity_crux_id`: * `":jorge-3"`, `:jorge_3` or `3` * `":jorge-3"` and `:jorge_3` are not equivalent Example Response: * `"{:crux.db/id #crux/id \"be21bd5ae7f3334b9b8abb185dfbeae1623088b1\", :crux.db/content-hash #crux/id \"9d2c7102d6408d465f85b0b35dfb209b34daadd1\", :crux.db/valid-time #inst \"2020-10-16T01:51:50.568-00:00\", :crux.tx/tx-time #inst \"2020-10-16T01:51:50.568-00:00\", :crux.tx/tx-id 4}"` """ def entity_tx!(client, entity_id) when is_pid(client) and (is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id)) do url = Client.endpoint(client, :entity_tx) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at entity-tx with id #{entity_id} did not return 200" end end @spec entity_tx!(pid, atom | binary | integer, DateTime.t(), DateTime.t()) :: any def entity_tx!(client, entity_id, tx_time, valid_time) when is_pid(client) and (is_atom(entity_id) or is_integer(entity_id) or is_binary(entity_id)) do url = Time.build_timed_url(Client.endpoint(client, :entity_tx), tx_time, valid_time) headers = Client.headers(client) entity_data = case is_atom(entity_id) do true -> "{:eid :#{entity_id}}" false -> "{:eid #{entity_id}}" end response = @http_client.post(url, entity_data, headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at entity-tx with id #{entity_id} did not return 200" end end @spec entity_history({:ok, pid}, any, :asc | :desc, boolean) :: any @doc """ `entity_history({:ok, }, entity_hash, order, with_docs \\ false)` GETs an CruxdD hash at CruxDB endpoint [`/entity-history/?sort-order=&with-docs=`](https://www.opencrux.com/reference/20.09-1.12.1/http.html#entity-history) * `order` can be `:asc` or `:desc` Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `entity_hash`: * `"9d2c7102d6408d465f85b0b35dfb209b34daadd1"` Example Response (`with_docs = false`): ```elixir {:ok, [ %{ "crux.db/content-hash": %Eden.Tag{ name: "crux/id", value: "9d2c7102d6408d465f85b0b35dfb209b34daadd1" }, "crux.db/valid-time": ~U[2020-10-22 18:18:20.524Z], "crux.tx/tx-id": 160, "crux.tx/tx-time": ~U[2020-10-22 18:18:20.524Z] }, ...] }``` """ def entity_history({:ok, client}, entity_hash, order, with_docs \\ false) when is_pid(client) and is_boolean(with_docs) and is_atom(order) do url = Client.endpoint(client, :entity_history) headers = Client.headers(client) EntityHistory.entity_history(url, headers, entity_hash, with_docs, order) end @spec entity_history_timed({:ok, pid}, any, :asc | :desc, boolean, HistoryTimeRange.t()) :: any @doc """ `entity_history_timed({:ok, }, entity_hash, order, with_docs, timed)` GETs an CruxdD hash at CruxDB endpoint [`/entity-history/?sort-order=&with-docs=`](https://www.opencrux.com/reference/20.09-1.12.1/http.html#entity-history) * `order` can be `:asc` or `:desc` * `timed` is structured as ``` %HistoryTimeRange{ start_valid_time: DateTime.t(), end_valid_time: DateTime.t(), start_tx_time: DateTime.t(), endt_tx_time: DateTime.t(), } ``` Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `entity_hash`: * `"9d2c7102d6408d465f85b0b35dfb209b34daadd1"` Example Response (`with_docs = false`): ```elixir {:ok, [ %{ "crux.db/content-hash": %Eden.Tag{ name: "crux/id", value: "9d2c7102d6408d465f85b0b35dfb209b34daadd1" }, "crux.db/valid-time": ~U[2020-10-22 18:18:20.524Z], "crux.tx/tx-id": 160, "crux.tx/tx-time": ~U[2020-10-22 18:18:20.524Z] }, ...] }``` """ def entity_history_timed( {:ok, client}, entity_hash, order, with_docs, %HistoryTimeRange{} = timed ) when is_pid(client) and is_boolean(with_docs) and is_atom(order) do url = Client.endpoint(client, :entity_history) headers = Client.headers(client) EntityHistory.entity_history_timed(url, headers, entity_hash, with_docs, order, timed) end @spec query({:ok, atom | pid | {atom, any} | {:via, atom, any}}, any) :: {:error} | {:error, atom} | {:ok, any} @doc """ `entity_history!(, entity_hash, order, with_docs \\ false)` GETs an CruxdD hash at CruxDB endpoint `/entity-history/?sort-order=&with-docs=` * `order` can be `:asc` or `:desc` Returns: * `status_2XX` -> {:ok, body} * _ -> {:error} Example `entity_hash`: * `"9d2c7102d6408d465f85b0b35dfb209b34daadd1"` Example Response (`with_docs = false`): ```elixir [ %{ "crux.db/content-hash": %Eden.Tag{ EntityHistory.entity_history(url, headers, entity_hash, with_docs, order) query = %{} |> Query.find(["?n"]) |> Query.where([ "?n :first-name ?p", ]) |> Query.args(["?p \"Michael\""]) |> Query.with_full_results |> Query.build client |> query(query) |> IO.inspect # {:ok, # [ # #Array<[ # %{"crux.db/id": :"jorge-3", "first-name": "Michael", "last-name": "Jorge"} # ], fixed=false, default=nil> # ]} ``` """ def query({:ok, client}, query) do url = Client.endpoint(client, :query) headers = Client.headers(client) response = @http_client.post(url, "#{query}", headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode(content.body) _ -> {:error} end end @spec query!(pid, any) :: any @doc """ `query!(, query)` POSTs a `Query` at CruxDB endpoint `/query` """ def query!(client, query) when is_pid(client) do url = Client.endpoint(client, :query) headers = Client.headers(client) response = @http_client.post(url, "#{query}", headers) case response do {:ok, content} when content.status_code < 300 -> Eden.decode!(content.body) _ -> raise "POST at query with body #{query} did not return 200" end end end