oliyh / re-graph

A graphql client for clojurescript and clojure
460 stars 39 forks source link

:resume-subscriptions? on subsequent init #67

Closed hipitihop closed 3 years ago

hipitihop commented 4 years ago

We use JWT auth tokens with expiry time and organise to refresh the JWT token prior to expiry, in turn, when we get the refreshed token, we dispatch ::re-graph/init again, however, despite specifying :resume-subscriptions? true any existing subscriptions are dropped.

Is this a bug or do you recommend a way to deal with this use case ?

oliyh commented 4 years ago

Hi,

That option is actually to determine the behaviour when the websocket connection is lost and then reconnected - it will re-subscribe if that flag is true, and won't if not. If you re-initialise re-graph it loses all state including which subscriptions existed (its usage is here: https://github.com/oliyh/re-graph/blob/master/src/re_graph/internals.cljc#L211)

There's nothing to directly support your use case, but it should be possible. You need to reach into the re-frame database via re-frame.db/app-db and set your new connection-init-payload (where I presume your token goes) at [:re-graph :re-graph.internals/default :websocket :connection-init-payload] and then grab the websocket connection at [:re-graph :re-graph.internals/default :websocket :connection] and close it - that should trigger re-graph into reconnecting the websocket, sending the new connection-init-payload and resuming subscriptions.

If this works for you then we could add a new API call to achieve this so you're not delving around in the internals.

stumitchell commented 4 years ago

Hi thanks for your help @hipitihop and I work together

The following code worked to refresh the jwt authentication

(reg-event-fx
  ::success-jwt
  (fn success-jwt-handler
    [{:keys [db]} [_ jwt]]
      (let [ms-prior-exp  (* 5 60 1000)                     ; 5 minutes in milliseconds
            jwt           (utils/map->nsmap jwt "jwt")
            refresh-ms    (-> (get jwt :jwt/exp)
                              (* 1000)
                              (time.coerce/from-long)
                              (refresh-in-milliseconds ms-prior-exp))
           headers       {:headers {"Authorization" (str "Bearer " (get jwt :jwt/token))}}]

        ;; Hang on to the token we were given and init re-graph
        (debug "Will refresh JWT in " refresh-ms "ms")
         {:db             (-> db
                                 (assoc :jwt jwt)
                                 (assoc-in [:re-graph :re-graph.internals/default 
                                            :websocket :connection-init-payload] headers)
                                 (update-in [:re-graph :re-graph.internals/default 
                                             :websocket :connection] #(.close %)))
            :dispatch-later [{:ms refresh-ms :dispatch [::fetch-jwt (get db :session) 1 true]}]}
          ))))

note the above code is heavy edited.

An API call to refresh the connection-payload would be great. would also be nice if it triggered an immediate re-connection instead of having to wait the 5000ms or whatever the default timeout is.

Thanks

oliyh commented 4 years ago

Hi, Glad that worked. I just had a simpler idea - if your server side would support it, could you just update the connection init payload and then call (dispatch [:re-graph.internals/connection-init]) to send it to the server? Then you don't have the overhead of new connections and the delay.

oliyh commented 4 years ago

Hi @stumitchell and @hipitihop did you have a chance to look at my suggestion?

Thanks,

stumitchell commented 4 years ago

yeah that seems to work too,

Not noticeably faster though still about a 6 second delay,

Stu

oliyh commented 3 years ago

Hi @stumitchell and @hipitihop I have just pushed 0.1.14-SNAPSHOT to clojars which adds the re-init api described in the README, this should be a more elegant solution for you.