defmodule TokenServer do use GenServer require Logger alias TokenServer.ApiClient # Server @impl GenServer @spec init(any()) :: {:ok, DateTime.t()} def init(_) do :ets.new(:token_store, [:named_table, read_concurrency: true]) expires_at = get_and_store_token() {:ok, expires_at} end @impl GenServer @spec handle_info(any(), DateTime.t()) :: {:noreply, DateTime.t()} def handle_info(:refresh, _expires_at) do expires_at = get_and_store_token() {:noreply, expires_at} end def handle_info(msg, expires_at) do Logger.info("Received unknown message: #{inspect(msg)}") {:noreply, expires_at} end @impl GenServer @spec handle_call(:force_refresh, GenServer.from(), DateTime.t()) :: {:reply, :ok, DateTime.t()} def handle_call(:force_refresh, _from, _expires_at) do expires_at = get_and_store_token() {:reply, :ok, expires_at} end defp get_and_store_token() do {token, expires_at} = ApiClient.get_token() :ets.insert(:token_store, {:token, token}) next_refresh = DateTime.add(expires_at, -5, :minute) ms_to_refresh = DateTime.diff(next_refresh, DateTime.utc_now(), :millisecond) Process.send_after(self(), :refresh, ms_to_refresh) expires_at end # Client @doc """ A function to start this GenServer as part of the supervision tree, registered with the name of the module. """ @spec start_link(any()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, nil, name: __MODULE__) end @spec get() :: String.t() def get(), do: :ets.lookup_element(:token_store, :token, 2) @spec force_refresh() :: :ok def force_refresh() do GenServer.call(__MODULE__, :force_refresh) end end