I have a function that makes an HTTP request for a token, which will be used in future requests. This token is valid for an unspecified amount of time, probably a few hours or so.
(defn request-app-token
"Request an app token from FB. Useful for app-global actions, such as creating test users."
[client-credentials]
(-> {"client_id" (:client-id client-credentials)
"client_secret" (:client-secret client-credentials)
"grant_type" "client_credentials"}
(form-encode)
((partial str fb-graph-api "/oauth/access_token?"))
(client/get {:throw-entire-message? true})
:body
(json/read-str)
(get "access_token")))
To me, this looks like a job for memoize: keep a copy of the token and reuse it, instead of requesting new tokens every time I need one.
(def get-app-token (memoize request-app-token)) ; So beautiful :D
I just need to handle the case where the token expires. For that, I will invert control; take a function that needs a token, attempt to run it with the memoized token, and if that fails, then try it again with a fresh token.
(defn with-app-token [client-credentials f]
(try (f (get-app-token client-credentials))
(catch Exception e ; I know I should be more specific here, I only want to catch HTTP 400 responses
(f (request-app-token client-credentials)))))
This would sort of work, but after the first token expires, then all subsequent calls to with-app-token
would make a request for a new token. I need some way to clear or invalidate the memoized return value of get-app-token
.
I could write my own memoize
function, with an invalidate
function that clears a specific result, but I am wondering if there is already something in the language that is meant to handle this?
clojure.core.memoize has what I need: a memo-clear!
function. After requiring [clojure.core.memoize :refer [memo memo-clear!]]
, the solution looks like this:
(defn request-app-token
"Request an app token from FB. Useful for app-global actions, such as creating test users."
[client-credentials]
(-> {"client_id" (:client-id client-credentials)
"client_secret" (:client-secret client-credentials)
"grant_type" "client_credentials"}
(form-encode)
((partial str fb-graph-api "/oauth/access_token?"))
(client/get {:throw-entire-message? true})
:body
(json/read-str)
(get "access_token")))
(def get-app-token (memo request-app-token))
(defn with-app-token [client-credentials f]
(try (f (get-app-token client-credentials))
(catch Exception e ; I know I should be more specific here, I only want to catch HTTP 400 responses
(memo-clear! get-app-token client-credentials)
(f (get-app-token client-credentials)))))
clojure.core.memoize
also has ttl
function which supports caching the value for configurable time.
If the token is valid for 60 minutes, then you can use the ttl function as follows
(def get-app-token (memo/ttl request-app-token :ttl/threshold 3600000))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With