defmodule Pollin.Backend.Memory.CallbackWorker do @moduledoc """ Worker for callbacks """ use GenServer use Pollin.CallbackInterface alias Pollin.Backend.Memory.EndpointWorker @doc false def start_link(id) when is_atom(id), do: GenServer.start_link(__MODULE__, [], name: id) @doc false def init(_opts), do: {:ok, []} ## Private API @doc false def handle_cast({:push, id, cb, secret}, state) do endpoint = EndpointWorker.find("#{id}") if endpoint && endpoint.secret == secret do if endpoint.ttl > 0 do Process.send_after(self, {:delete, cb.id}, endpoint.ttl) end {:noreply, List.insert_at(state, -1, {cb.id, cb})} else {:noreply, state} end end @doc false def handle_cast({:reset}, _state), do: {:noreply, []} @doc false def handle_cast({:delete, key}, state) do new_state = List.keydelete(state, key, 0) {:noreply, new_state} end @doc false def handle_cast({:update_status, key, status}, state) do case List.keyfind(state, key, 0) do {key, cb} -> cb = Map.put(cb, :status, status) {:noreply, List.keyreplace(state, key, 0, {key, cb})} nil -> {:noreply, state} end end @doc false def handle_call({:pop}, _from, [{_key, cb} | state]), do: {:reply, cb, state} @doc false def handle_call({:pop, key}, _from, state) when is_binary(key) do case List.keytake(state, key, 0) do {{_key, cb}, new_state} -> {:reply, cb, new_state} nil -> {:reply, nil, state} end end @doc false def handle_call({:pop, %{status: status, offset: offset, limit: limit}}, _from, state) do items = state |> Enum.group_by(fn {_key, cb} -> cb.status end) |> Map.get(status, []) |> Enum.slice(offset, limit) {:reply, items |> Enum.map(fn {_key, cb} -> cb end), state -- items} end @doc false def handle_call({:pop, %{offset: offset, limit: limit}}, _from, state) do items = state |> Enum.slice(offset, limit) {:reply, items |> Enum.map(fn {_key, cb} -> cb end), state -- items} end @doc false def handle_call({:fetch}, _from, [{_key, cb} | _state] = state), do: {:reply, cb, state} @doc false def handle_call({:fetch, key}, _from, state) when is_binary(key) do case List.keyfind(state, key, 0) do {_key, cb} -> {:reply, cb, state} nil -> {:reply, nil, state} end end @doc false def handle_call({:fetch, %{status: status, offset: offset, limit: limit}}, _from, state) do items = state |> Enum.group_by(fn {_key, cb} -> cb.status end) |> Map.get(status, []) |> Enum.slice(offset, limit) |> Enum.map(fn {_key, cb} -> cb end) {:reply, items, state} end @doc false def handle_call({:fetch, %{offset: offset, limit: limit}}, _from, state) do items = state |> Enum.slice(offset, limit) |> Enum.map(fn {_key, cb} -> cb end) {:reply, items, state} end @doc false def handle_call({:fetch_reverse, %{status: status, offset: offset, limit: limit}}, _from, state) do items = state |> Enum.reverse |> Enum.group_by(fn {_key, cb} -> cb.status end) |> Map.get(status, []) |> Enum.slice(offset, limit) |> Enum.map(fn {_key, cb} -> cb end) {:reply, items, state} end @doc false def handle_call({:fetch_reverse, %{offset: offset, limit: limit}}, _from, state) do items = state |> Enum.reverse |> Enum.slice(offset, limit) |> Enum.map(fn {_key, cb} -> cb end) {:reply, items, state} end @doc false def handle_call({:count}, _from, state), do: {:reply, Enum.count(state), state} @doc false def handle_call({:count, %{status: status}}, _from, state) do count = state |> Enum.group_by(fn {_key, cb} -> cb.status end) |> Map.get(status, []) |> Enum.count {:reply, count, state} end @doc false def handle_call({:dump}, _from, state) do items = state |> Enum.map(fn {_key, cb} -> cb end) {:reply, items, state} end end