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?