defmodule GoogleAds.TokenCache do @moduledoc """ Cache instance for Google Ads tokens. """ use GenServer @cache_table :google_ads_tokens @type prismatic_ref :: String.t() @spec start_link(any()) :: {:ok, pid()} | {:error, any()} def start_link(_opts) do GenServer.start_link(__MODULE__, nil, name: __MODULE__) end @impl GenServer def init(_opts) do @cache_table = :ets.new(@cache_table, [:set, :public, :named_table]) {:ok, %{}} end @spec insert(prismatic_ref(), GoogleAds.Token.t()) :: :ok @spec insert(prismatic_ref(), GoogleAds.Token.t(), DateTime.t()) :: :ok def insert(prismatic_ref, %GoogleAds.Token{} = token, now \\ DateTime.utc_now()) do GenServer.call(__MODULE__, {:insert, prismatic_ref, token, now}) end @spec get(prismatic_ref()) :: GoogleAds.Token.t() | nil def get(prismatic_ref) do case :ets.lookup(@cache_table, prismatic_ref) do [{^prismatic_ref, %GoogleAds.Token{} = token}] -> token _any -> nil end end @doc false @spec flush() :: any() def flush, do: :ets.delete_all_objects(@cache_table) @impl GenServer def handle_call({:insert, prismatic_ref, %GoogleAds.Token{} = token, now}, _from, state) do {:ok, expires_at, 0} = DateTime.from_iso8601(token.expires_at) expires_in_seconds = DateTime.diff(expires_at, now, :second) true = :ets.insert(@cache_table, {prismatic_ref, token}) Process.send_after(self(), {:remove, prismatic_ref}, :timer.seconds(expires_in_seconds)) {:reply, :ok, state} end @impl GenServer def handle_info({:remove, prismatic_ref}, state) do true = :ets.delete(@cache_table, prismatic_ref) {:noreply, state} end end