Skip to content

Instantly share code, notes, and snippets.

@danielneal
Created October 27, 2017 12:45
Show Gist options
  • Save danielneal/537b18d4532560529e92dd4f214a6ecf to your computer and use it in GitHub Desktop.
Save danielneal/537b18d4532560529e92dd4f214a6ecf to your computer and use it in GitHub Desktop.
(ns app.graphql-interceptor
(:require [re-frame.core :as reframe :refer [->interceptor]]
[cljs.spec.alpha :as s]
[app.reg-event :refer [reg-event-fx]]
[app.util :as util]
[clojure.set :as set]))
(set! *warn-on-infer* true)
(def graphql->http
(->interceptor
:id :graphql->http
:after (fn [context]
(let [token (get-in context [:coeffects :db :app.db/auth :auth/token])
db (get-in context [:coeffects :db])]
(update context :effects
(fn [effects]
(let [{:keys [graphql]} effects]
(if graphql
(do
(s/assert :graphql-interceptor/data graphql)
(let [{:keys [on-success on-unauthorized on-failure query variables timeout-ms]} (s/conform :graphql-interceptor/data graphql)]
(-> effects
(assoc :http {:url (util/route-for db :routes.api/graphql)
:method "POST"
:headers (cond->
{"Accept" "application/json"
"Content-Type" "application/json"}
token (assoc "authorization" (str "Bearer " token)))
:params {:query query
:variables variables}
:timeout-ms timeout-ms
;; Wrap success response, because there may be errors
:on-success [:handlers.graphql.http-response/success {:on-success on-success, :on-failure on-failure, :on-unauthorized on-unauthorized}]
:on-failure [:handlers.graphql.http-response/failure {:on-success on-success, :on-failure on-failure, :on-unauthorized on-unauthorized}]})
(dissoc :graphql))))
effects))))))))
;; At the moment we only handle one special graphql error - the unauthorized error.
(defn graphql-error->error
[error]
(-> error
(update :error/type (fn [error-type]
(case error-type
"error.types.auth/unauthorized" :error.types/unauthorized
:error.types/unidentified)))))
(reg-event-fx
:handlers.graphql.http-response/success
(fn [{:keys [db]} [_ {:keys [on-success on-unauthorized on-failure]} {:keys [data errors]}]]
;; Graphql errors come through as http 200
;; This handler, when combined with the graphql->http interceptor
;; ensures the _on-failure handler_ of graphql gets called if
;; errors are present.
(let [errors (map graphql-error->error errors)
unauthorized? (and (seq errors) (some #(= (:error/type %) :error.types/unauthorized) errors))
expired-session? (and (contains? (get data :me) :customer_id)
(nil? (get-in data [:me :customer_id])))]
(cond
(and (or unauthorized? expired-session?) on-unauthorized) {:dispatch (conj on-unauthorized {:data data, :errors errors})}
(and (or unauthorized? expired-session?) (not on-unauthorized)) {:dispatch (conj on-failure {:data data, :errors errors})}
(empty? errors) {:dispatch (conj on-success {:data data})}
:else {:dispatch (conj on-failure {:data data, :errors errors})}))))
(reg-event-fx
:handlers.graphql.http-response/failure
(fn [{:keys [db]} [_ {:keys [on-unauthorized on-failure]} {:keys [errors]}]]
(if (and on-unauthorized (some #(= (:error/type %) :error.types/unauthorized) errors))
{:dispatch (conj on-unauthorized {:errors errors})}
{:dispatch (conj on-failure {:errors errors})})))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment