Created
August 23, 2019 23:48
-
-
Save tr00gle/362ee1778f08515b56b8b8de99d6e1af to your computer and use it in GitHub Desktop.
auth gen server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule TransactionalMessagingService.Providers.Cheetah.Auth do | |
@moduledoc """ | |
GenServer to handle all Cheetah auth token related functionality | |
All requests to Cheetah require using a valid auth token in the request | |
header, the tokens also expire after a certain time (currently 8 hours). | |
Setup a GenServer to keep a active token in its state and manage retrieval | |
of a new token when needed. | |
This is accomplished by storing the token value and the calculated expiration | |
time in the GenServer state, when a token is retrieved if it is valid it is | |
given right away, otherwise if it has expired a new token is retrieved and | |
placed into the state. | |
""" | |
use GenServer | |
require Logger | |
alias TransactionalMessagingService.DataDog | |
############# | |
# Client API - these methods run in the calling elixir process | |
############# | |
def start_link do | |
GenServer.start_link(__MODULE__, [], name: __MODULE__) | |
end | |
@timeout 8000 | |
def get_token(shop) do | |
GenServer.call(__MODULE__, {:get_token, shop}, @timeout) | |
end | |
################### | |
# Server Callbacks - these run in a separate elixir process than the above | |
# client api | |
################### | |
def init(_) do | |
{:ok, nil, {:continue, :init}} | |
end | |
@token_creds # removed | |
def handle_continue(:init, nil) do | |
{:noreply, | |
%{ | |
"www" => retrieve_new_token(@token_creds["www"]), | |
"uk" => retrieve_new_token(@token_creds["uk"]), | |
"au" => retrieve_new_token(@token_creds["au"]), | |
"ca" => retrieve_new_token(@token_creds["ca"]) | |
}} | |
end | |
def handle_call({:get_token, shop}, _from, token_store) do | |
case token_store[shop] do | |
%{token: token, expiration: expiration} -> | |
if expiration < :os.system_time(:seconds) do | |
# fetch a new one and update the token_store | |
updated_shop_state = %{token: new_token} = retrieve_new_token(@token_creds[shop]) | |
{:reply, new_token, Map.merge(token_store, %{shop => updated_shop_state})} | |
else | |
# reply with the token | |
{:reply, token, token_store} | |
end | |
nil -> | |
updated_shop_state = %{token: new_token} = retrieve_new_token(@token_creds[shop]) | |
{:reply, new_token, Map.merge(token_store, %{shop => updated_shop_state})} | |
end | |
end | |
@endpoint # removed | |
defp retrieve_new_token({username, password}) do | |
case HTTPoison.post( | |
@endpoint, | |
"username=#{username}&" <> | |
"password=#{password}&" <> | |
"grant_type=password", | |
[{"Content-Type", "application/x-www-form-urlencoded"}] | |
) do | |
# Cheetah might return a error without a error status code, | |
# this causes the Poison.decode!() below to error out because | |
# it tries to parse the response as JSON when it is a HTML | |
# error page | |
{:ok, %HTTPoison.Response{body: response}} -> | |
response | |
|> Poison.decode!() | |
|> extract_new_token_info() | |
{:error, error} -> | |
DataDog.increment_metric("cheetah.auth_token_fetch_failure") | |
Logger.error("Error getting auth token: #{inspect(error)}") | |
nil | |
end | |
end | |
defp extract_new_token_info(%{"access_token" => token, "expires_in" => expiration}) do | |
DataDog.increment_metric("cheetah.auth_token_fetch_success") | |
%{token: token, expiration: :os.system_time(:seconds) + (expiration - 300)} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment