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 clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
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 clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/ac79058b-2c31-4b9c-9cf3-e2de998eb8deo%40googlegroups.com.

Reply via email to