pathom

:pathom: https://github.com/wilkerlucio/pathom/ & https://pathom3.wsscode.com & https://roamresearch.com/#/app/wsscode
2020-06-28T00:20:37.259800Z

I was working on how to organize resolver code - mainly for common concerns like auth and made an interesting connection - combining pedestal interceptors with pathom-connect transform....

(require
    [io.pedestal.interceptor.chain :as chain]
    [io.pedestal.interceptor.helpers :as ih])

(def response-key :pathom-interceptors/response)

(defn assoc-response [in-map response]
  (assoc-in in-map [:env response-key] response))

(defn get-response [in-map]
  (get-in in-map [:env response-key]))

(defn response-interceptor
  [{:keys [opts env params] :as in}]
  (let [{::pc/keys [mutate resolve]} opts
        response (get-response in)]
    (if response
      in
      (if resolve
        (assoc-response in (resolve env params))
        (assoc-response in (mutate env params))))))

(defn interceptors->pathom-transform
  "Executes vector of interceptors on a pathom resolver or mutation.
  Each interceptor is passed a single map (the environment) which has the keys:
  :opts - The definition-time pathom resolver or mutation map of options.
  :env - The pathom connect environment for the resolver or mutation passed by pathom at request-time.
  :params - The params to the resolver or the mutation.

  Responses are set on the env like so:

  (assoc-response env {:my-pathom-resp :value})
  "
  [interceptors]
  (fn pathom-transform*
    [{::pc/keys [mutate resolve] :as opts}]
    (let [interceptors (conj interceptors (ih/after response-interceptor))]
      (cond
        resolve
        (assoc opts ::pc/resolve
                    (fn [en params]
                      (let [out (chain/execute {:opts opts :env en :params params} interceptors)]
                        (get-response out))))

        mutate
        (assoc opts ::pc/mutate
                    (fn [en params]
                      (let [out (chain/execute {:opts opts :env en :params params} interceptors)]
                        (get-response out))))
        :else (throw
                (Exception.
                       (str "Attempting to use interceptor transform on a map that does not have a resolve or mutate.")))))))

(defn auth-user-interceptor
  [{:keys [opts env] :as in}]
  (let [{:auth/keys [no-user-msg]
         :or        {no-user-msg "You must be logged in to perform this action."}} opts
        {:keys [current-user]} env]
        (cond-> in (not current-user)
          (assoc-response (server-error no-user-msg)))))

(pc/defmutation create-thing-mutation
  [{:keys [current-user] :as env} props]
  {:auth/no-user-msg "You must be logged in to create a thing."
   ::pc/transform    (interceptors->pathom-transform [(ih/before auth-user-interceptor)])}
   ;; do mutation
  )