Hi, it's a long time that this question was posted, but I have found it
interesting in the implementation of token refreshes.
First of all, for service invocation, given a `revise-oauth-token` method,
I think this is good client code:
(http/request
{:method :get
:url "https://example.com/"
:oauth-token (revise-oauth-token token-store)})
If you find it too repetitive or fragile in your client code, you can make
a local function, but I wouldn't abstract the service invocation at a
higher layer.
Regarding the implementation of the token store, we could initially think
of a synchronized store, like an atom, and `revise-oauth-token` would swap
its content when a refresh is required. This is inconvenient for
multithreaded clients, because there could be several refresh invocations
going on concurrently.
In order to avoid concurrent refreshes, I propose to implement the token
store as an atom of promises. Implementation of `revise-oauth-token` would
be:
(defn revise-oauth-token [token-store]
(:access_token
@(swap! token-store
(fn [token-promise]
(if (token-needs-refresh? @token-promise (Instant/now))
(delay (refresh-oauth-token (:refresh_token @token-promise)))
token-promise)))))
Note that using a delay avoids running `refresh-oauth-token` within the
`swap!` operation, as this operation may be run multiple times.
Also note that `token-needs-refresh` takes an argument with the present
time. This keeps the function pure, which could help for unit testing, for
example.
There is an alternative implementation using `compare-and-set!` that avoids
checking `token-needs-refresh?` several times, but it is more complicated.
I have posted full sample code in a gist:
https://gist.github.com/titogarcia/4f09bcc5fa38fbdc1076954b9a99a8fc
Remark: None of this refers to "functional programming" per se. Dealing
with state in a purely functional way involves using different constructs
(like possibly monads, for which you can find Clojure libraries if you are
interested), and best practices are still a topic of research. Clojure has
taken the pragmatic approach of making purely functional code easy to
write, but it doesn't reject the use of state, rather it provides
well-behaved primitives like vars, atoms, agents, etc.
Ernesto
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/clojure/ac79058b-2c31-4b9c-9cf3-e2de998eb8deo%40googlegroups.com.