geo-therminator/lib/geo_therminator/pump_api/auth/server.ex

129 lines
3.3 KiB
Elixir

defmodule GeoTherminator.PumpAPI.Auth.Server do
require Logger
require GeoTherminator.PumpAPI.Auth.User
require GeoTherminator.PumpAPI.Auth.InstallationInfo
require GeoTherminator.PumpAPI.Auth.Tokens
use GenServer
import GeoTherminator.TypedStruct
alias GeoTherminator.PumpAPI.Auth
@token_check_timer 10_000
@token_check_diff 5 * 60
defmodule Options do
deftypedstruct(%{
server_name: GenServer.name(),
username: String.t(),
password: String.t()
})
end
defmodule State do
deftypedstruct(%{
username: String.t(),
password: String.t(),
authed_user: {Auth.User.t() | nil, nil},
installations_fetched: {boolean(), false},
installations: {[Auth.InstallationInfo.t()], []}
})
end
@spec start_link(Options.t()) :: GenServer.on_start()
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: opts.server_name)
end
@impl true
def init(%Options{} = opts) do
case init_state(opts.username, opts.password) do
{:ok, state} -> {:ok, state}
:error -> {:stop, :error}
end
end
@impl true
def handle_call(msg, from, state)
def handle_call(:get_auth, _from, state) do
{:reply, state.authed_user, state}
end
def handle_call(:get_installations, _from, state) do
{:reply, state.installations, state}
end
def handle_call({:get_installation, id}, _from, state) do
{:reply, Enum.find(state.installations, &(Auth.InstallationInfo.record(&1, :id) == id)),
state}
end
@impl true
def handle_info(msg, state)
def handle_info(:token_check, state) do
now = DateTime.utc_now()
diff =
DateTime.diff(
state.authed_user
|> Auth.User.record(:tokens)
|> Auth.Tokens.record(:access_token_expiry),
now
)
schedule_token_check()
if diff < @token_check_diff do
Logger.info("Renewing auth token since #{diff} < #{@token_check_diff}")
case init_state(state.username, state.password) do
{:ok, new_state} -> {:noreply, new_state}
:error -> {:noreply, state}
end
else
{:noreply, state}
end
end
@spec get_auth(GenServer.name()) :: Auth.User.t()
def get_auth(server) do
GenServer.call(server, :get_auth)
end
@spec get_installations(GenServer.name()) :: Auth.InstallationInfo.t()
def get_installations(server) do
GenServer.call(server, :get_installations)
end
@spec get_installation(GenServer.name(), integer()) :: Auth.InstallationInfo.t() | nil
def get_installation(server, id) do
GenServer.call(server, {:get_installation, id})
end
defp schedule_token_check() do
Process.send_after(self(), :token_check, @token_check_timer)
end
@spec init_state(String.t(), String.t()) :: {:ok, State.t()} | {:stop, :error}
defp init_state(username, password) do
with {:ok, user} <- :pump_api@auth@api.auth(username, password),
{:ok, installations} <- :pump_api@auth@api.installation_info(user) do
schedule_token_check()
state = %State{
authed_user: user,
installations: installations,
installations_fetched: true,
username: username,
password: password
}
{:ok, state}
else
err ->
Logger.error("Could not auth or fetch installations! #{inspect(err)}")
:error
end
end
end