chronoscope/lib/chronoscope/nts/client.ex

107 lines
2.6 KiB
Elixir

defmodule Chronoscope.NTS.Client do
use GenServer, restart: :transient
alias Chronoscope.NTS
alias Chronoscope.NTS.KeyEstablishmentClient
@interval_in_seconds 30
@timeout_in_milliseconds 10_000
@refresh_interval_in_milliseconds :timer.minutes(1)
@topic Application.compile_env(:chronoscope, :nts_topic)
@date_time Application.compile_env(:chronoscope, :date_time, DateTime)
def start_link(server: server, name: name) do
GenServer.start_link(__MODULE__, server, name: name)
end
@impl true
def init(server) do
now = utc_now()
{:ok,
%{
server: server,
key_establishment_response: {:error, "initializing"},
last_key_establishment: DateTime.add(now, -@interval_in_seconds, :second)
}}
end
@impl true
def handle_call(:terminate, _from, state) do
{:stop, :normal, self(), state}
end
@impl true
def handle_call(:list, _from, state) do
{:reply, state, state}
end
@impl true
def handle_call(:auto_refresh, _from, %{timer: _timer} = state) do
{:reply, :already_started, state}
end
@impl true
def handle_call(:auto_refresh, _from, state) do
{:ok, timer} = :timer.send_interval(@refresh_interval_in_milliseconds, :key_establishment)
{:reply, :ok, Map.put(state, :timer, timer)}
end
@impl true
def handle_call(:cancel_auto_refresh, _from, %{timer: timer} = state) do
:timer.cancel(timer)
{:reply, :ok, Map.delete(state, :timer)}
end
@impl true
def handle_call(:cancel_auto_refresh, _from, state) do
{:reply, :already_cancelled, state}
end
@impl true
def handle_call(:key_establishment, _from, state) do
new_state = update_state(state)
{:reply, new_state.key_establishment_response, new_state}
end
@impl true
def handle_info(:key_establishment, state) do
{:noreply, update_state(state)}
end
defp update_state(state) do
now = utc_now()
if interval_surpassed?(now, state.last_key_establishment) do
state
|> Map.merge(current_data(state, now))
|> tap(fn _ -> ChronoscopeWeb.Endpoint.broadcast(@topic, "", "") end)
else
state
end
end
defp current_data(state, now) do
%{
key_establishment_response: server_response(state),
last_key_establishment: now
}
end
defp server_response(%{server: server}) do
NTS.TaskSupervisor
|> Task.Supervisor.async(fn -> KeyEstablishmentClient.key_establishment(server) end)
|> Task.await(@timeout_in_milliseconds)
end
defp interval_surpassed?(now, last_key_establishment) do
DateTime.diff(now, last_key_establishment, :second) >= @interval_in_seconds
end
defp utc_now() do
@date_time.utc_now()
end
end