From 3ba98e7f810f7ce36fbbae9f8d22e826c2d8ea6c Mon Sep 17 00:00:00 2001 From: Adam Millerchip Date: Fri, 28 Jun 2024 18:51:49 +0900 Subject: [PATCH] Add initial PoC --- lib/token_server.ex | 70 +++++++++++++++++++++++++++------ lib/token_server/api_client.ex | 10 +++++ lib/token_server/application.ex | 3 +- 3 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 lib/token_server/api_client.ex diff --git a/lib/token_server.ex b/lib/token_server.ex index f7ef771..9eb3a81 100644 --- a/lib/token_server.ex +++ b/lib/token_server.ex @@ -1,18 +1,64 @@ defmodule TokenServer do - @moduledoc """ - Documentation for `TokenServer`. - """ + 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 """ - Hello world. - - ## Examples - - iex> TokenServer.hello() - :world - + A function to start this GenServer as part of the supervision tree, registered with the + name of the module. """ - def hello do - :world + @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 diff --git a/lib/token_server/api_client.ex b/lib/token_server/api_client.ex new file mode 100644 index 0000000..0b3fb2d --- /dev/null +++ b/lib/token_server/api_client.ex @@ -0,0 +1,10 @@ +defmodule TokenServer.ApiClient do + def get_token do + Process.sleep(3000) + + token = Base.encode32(:rand.bytes(10)) + expires_at = DateTime.utc_now() |> DateTime.add(1, :hour) + + {token, expires_at} + end +end diff --git a/lib/token_server/application.ex b/lib/token_server/application.ex index c5a7995..ecd14fe 100644 --- a/lib/token_server/application.ex +++ b/lib/token_server/application.ex @@ -8,8 +8,7 @@ defmodule TokenServer.Application do @impl true def start(_type, _args) do children = [ - # Starts a worker by calling: TokenServer.Worker.start_link(arg) - # {TokenServer.Worker, arg} + {TokenServer, nil} ] # See https://hexdocs.pm/elixir/Supervisor.html