diff --git a/README.md b/README.md index 76e1b21..4d01fe5 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,8 @@ Code foundation was taken from https://github.com/joojscript/surrealdb_ex. Since ## Todo +- [x] handle live query updates properly +- [x] debug modus with verbose logging +- [x] integration tests - [ ] handle disconnects gracefully -- [ ] handle live query updates properly -- [ ] debug modus with verbose logging -- [ ] integration tests - [ ] benchmarks diff --git a/lib/surrealix.ex b/lib/surrealix.ex index 863af33..b9ca89f 100644 --- a/lib/surrealix.ex +++ b/lib/surrealix.ex @@ -7,6 +7,16 @@ defmodule Surrealix do defdelegate start_link(opts \\ []), to: Socket defdelegate stop(pid), to: Socket + @doc """ + Convenience method, that combines sending an query (live_query) and registering a callback + + Params: + sql: string + vars: map with variables to interpolate into SQL + callback: fn (event, data, config) + """ + defdelegate live_query(pid, sql, vars \\ %{}, callback), to: Socket + @doc """ ping This method pings the SurrealDB instance diff --git a/lib/surrealix/socket.ex b/lib/surrealix/socket.ex index a439c38..5c6000b 100644 --- a/lib/surrealix/socket.ex +++ b/lib/surrealix/socket.ex @@ -46,9 +46,13 @@ defmodule Surrealix.Socket do exit(:normal) end + def handle_cast({:register_lq, sql, query_id}, state) do + state = SocketState.add_lq(state, sql, query_id) + {:ok, state} + end + def handle_cast({method, args, id, task}, state) do Logger.debug("[surrealix] [handle_cast] #{inspect(state)}") - payload = build_cast_payload(method, args, id) state = SocketState.add_task(state, id, task) frame = {:text, payload} @@ -61,7 +65,7 @@ defmodule Surrealix.Socket do task = SocketState.get_task(state, id) if is_nil(task) do - # there is no registered task for this ID, it must be a live query update! + # No registered task for this ID, must be a live query update lq_id = get_in(json, ["result", "id"]) Surrealix.Dispatch.execute([:live_query, lq_id], json) else @@ -70,8 +74,7 @@ defmodule Surrealix.Socket do end end - state = SocketState.delete_task(state, id) - {:ok, state} + {:ok, SocketState.delete_task(state, id)} end defp exec_method(pid, {method, args}, opts \\ []) do @@ -142,6 +145,25 @@ defmodule Surrealix.Socket do |> Jason.encode!() end + @doc """ + Convenience method that combines sending a (live-)query and registering a callback. + + Params: + sql: string + vars: map with variables to interpolate into SQL + callback: fn (event, data, config) + """ + @spec live_query(pid(), String.t(), map(), (any, any, list() -> any)) :: :ok + def live_query(pid, sql, vars \\ %{}, callback) do + # TODO: check if SQL somewhat resembles a live query + with {:ok, res} <- query(pid, sql, vars), + %{"result" => [%{"result" => lq_id}]} <- res do + event = [:live_query, lq_id] + :ok = Surrealix.Dispatch.attach("#{lq_id}_main", event, callback) + WebSockex.cast(pid, {:register_lq, sql, lq_id}) + end + end + ### API METHODS : START ### @doc """ ping diff --git a/lib/surrealix_test.exs b/lib/surrealix_test.exs index 8956007..7136977 100644 --- a/lib/surrealix_test.exs +++ b/lib/surrealix_test.exs @@ -21,4 +21,56 @@ defmodule Surrealix.Test do assert res == [%{"id" => "user:marcus", "name" => "Marcus Aurelius - 3", "age" => 44}] end end + + describe "live_query" do + setup [:setup_surrealix] + + test "callbacks are properly executed", %{pid: pid} do + testpid = self() + + Surrealix.live_query(pid, "LIVE SELECT * FROM user;", fn _event, data, _config -> + send(testpid, {:lq, data}) + end) + + Surrealix.insert(pid, "user", %{id: "marcus", name: "Marcus Aurelius"}) + assert_receive {:lq, data} + + %{ + "result" => %{ + "action" => "CREATE", + "id" => _lq_id, + "result" => %{"id" => "user:marcus", "name" => "Marcus Aurelius"} + } + } = data + + res = Surrealix.query(pid, "select * from user:marcus") |> extract_res(0) + assert res == [%{"id" => "user:marcus", "name" => "Marcus Aurelius"}] + + Surrealix.merge(pid, "user:marcus", %{age: 44}) + assert_receive {:lq, data2} + + %{ + "result" => %{ + "action" => "UPDATE", + "result" => %{"age" => 44, "id" => "user:marcus", "name" => "Marcus Aurelius"} + } + } = data2 + + res = Surrealix.query(pid, "select * from user:marcus") |> extract_res(0) + assert res == [%{"id" => "user:marcus", "name" => "Marcus Aurelius", "age" => 44}] + + Surrealix.delete(pid, "user:marcus") + assert_receive {:lq, data3} + + %{ + "result" => %{ + "action" => "DELETE", + "result" => "user:marcus" + } + } = data3 + + res = Surrealix.query(pid, "select * from user:marcus") |> extract_res(0) + assert res == [] + end + end end