Last active
November 9, 2022 09:40
-
-
Save tmoerman/c4c863c00e5364932ad209f921d9e7d2 to your computer and use it in GitHub Desktop.
Illustration of Pathom eql processing split into :enter and :leave phases of an interceptor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns com.aa.nexus.eql.interceptor-spike | |
"See: | |
https://gist.github.com/tmoerman/c4c863c00e5364932ad209f921d9e7d2" | |
(:require | |
[clojure.string :as str] | |
[clojure.test :refer :all] | |
[com.wsscode.pathom3.connect.indexes :as pci] | |
[com.wsscode.pathom3.connect.operation :as pco] | |
[com.wsscode.pathom3.connect.runner :as pcr] | |
[com.wsscode.pathom3.interface.eql :as p.eql] | |
[com.wsscode.pathom3.format.eql :as pf.eql] | |
[com.wsscode.pathom3.plugin :as p.plugin] | |
[com.wsscode.pathom3.connect.built-in.plugins :as pbip] | |
[sieppari.core :as exe] | |
[edn-query-language.core :as eql] | |
[taoensso.timbre :as log])) | |
(def weather-db | |
{:brussels :overcast | |
:leuven :thunder | |
:genk :sunny}) | |
(def population-db | |
{:brussels "1M" | |
:leuven "100K" | |
:genk "50K"}) | |
(def mayor-db | |
{:brussels "Philippe Close" | |
:leuven "Mo Ridouani" | |
:genk "Wim Dries"}) | |
(pco/defresolver city->weather [env {:keys [city] :as input}] | |
(log/info "resolver `city->weather`" city) | |
{:weather (weather-db city)}) | |
(pco/defresolver city->population [env {:keys [city] :as input}] | |
(log/info "resolver `city->weather`" city) | |
{:population (population-db city)}) | |
(pco/defresolver city->mayor [env {:keys [city] :as input}] | |
(log/info "resolver `city->mayor`" city) | |
{:mayor (mayor-db city)}) | |
(pco/defmutation print-city! [env {:keys [city mayor] :as input}] | |
(log/info "mutation `print-city!` -> input" [city, mayor]) | |
(log/warn (format "!! %s (%s) !!" (str/upper-case (name city)) mayor)) | |
input) | |
(def eql-processor-enter | |
{::p.plugin/id `eql-processor/enter | |
::pcr/wrap-mutate | |
(fn [mutate] | |
(fn [{:keys [acc] :as env} ast] | |
;; capture the mutation result | |
(let [result (mutate env ast)] | |
(swap! acc conj result) | |
result))) | |
::p.eql/wrap-process-ast | |
(fn [process] | |
(fn [env ast] | |
;; strip the mutation join from the AST | |
(let [ast* (eql/transduce-children (map (fn [{:keys [type] :as node}] | |
(if (= type :call) | |
(dissoc node :children :query) | |
node))) ast)] | |
(process env ast*))))}) | |
(def eql-processor-leave | |
{::p.plugin/id `eql-processor/leave | |
::pcr/wrap-mutate | |
(fn [mutate] | |
(fn [{:keys [acc] :as env} ast] | |
;; !! re-use the mutation result !! | |
(let [result (log/spy :info (peek @acc))] | |
(swap! acc pop) | |
result)))}) | |
(def env | |
(-> (p.plugin/register [pbip/mutation-resolve-params]) | |
(pci/register [city->weather | |
city->population | |
city->mayor | |
print-city!]))) | |
(defn process-intercepted | |
"Simulation of :enter and :leave phase of an interceptor system." | |
[env tx] | |
(let [acc (atom nil) | |
enter-env (-> env | |
(assoc :acc acc) | |
(p.plugin/register eql-processor-enter)) | |
enter-result (p.eql/process enter-env tx) | |
_ (log/info "Executing other interceptors") | |
_ (log/spy :info @acc) | |
_ (swap! acc reverse) ;; because we conj onto nil... | |
leave-env (-> env | |
(assoc :acc acc) | |
(p.plugin/register eql-processor-leave)) | |
leave-result (p.eql/process leave-env tx)] | |
;enter-result | |
leave-result)) | |
(def query [{[:city :leuven] [:city :weather]}]) | |
(def mutation [`(print-city! [:city :leuven])]) | |
(def mutation+join [{`(print-city! [:city :leuven]) [:city :weather]}]) | |
(def mutation+joins [{`(print-city! [:city :leuven]) [:city :weather]} | |
{`(print-city! [:city :brussels]) [:city :population]}]) | |
(comment | |
(process-intercepted env query) | |
(process-intercepted env mutation+join) | |
(process-intercepted env mutation+joins)) | |
;; --- | |
;; TODO -> reimplement plugins, can we turn it into one plugin? | |
(def ix-plugin | |
{::p.plugin/id `ix-plugin | |
::pcr/wrap-mutate (fn [mutate] | |
(fn [{:ix/keys [acc phase] :as env} ast] | |
(condp = phase | |
:enter ;; capture the mutation result | |
(let [result (mutate env ast)] | |
(swap! acc conj result) | |
result) | |
:leave ;; re-use the mutation results | |
;; selection strategy = last | |
(last @acc)))) | |
::p.eql/wrap-process-ast (fn [process] | |
(fn [{:ix/keys [phase] :as env} ast] | |
(condp = phase | |
:enter ;; strip the mutation join from the AST | |
(let [ast* (eql/transduce-children (map (fn [{:keys [type] :as node}] | |
(if (= type :call) | |
(dissoc node :children :query) | |
node))) ast)] | |
(process env ast*)) | |
:leave ;; proceed as usual | |
(process env ast))))}) | |
(def eql-processor-interceptor | |
{:enter (fn [ctx] | |
(let [acc (atom []) | |
env (-> ctx | |
(assoc :ix/acc acc) | |
(assoc :ix/phase :enter) | |
(p.plugin/register ix-plugin)) | |
eql (get-in ctx [:request :body-params])] | |
;; results captured in acc | |
(log/spy :info (p.eql/process env eql)) | |
(-> ctx | |
;; ensure the accumulator is carried to the :leave phase | |
(assoc :ix/acc acc)))) | |
:leave (fn [{:ix/keys [acc] :as ctx}] | |
(let [env (-> ctx | |
(assoc :ix/phase :leave) | |
(p.plugin/register ix-plugin)) | |
eql (get-in ctx [:request :body-params]) | |
;; results are retrieved from acc | |
_ (p.eql/process env eql) | |
response {:status 200 | |
:body @acc}] | |
(log/info :info response) | |
(-> ctx | |
(dissoc :ix/acc :ix/phase) | |
(assoc :response response)))) | |
}) | |
(defn ix-exe | |
[env eql] | |
(-> (exe/execute-context [eql-processor-interceptor] | |
(assoc-in env [:request :body-params] eql)) | |
(select-keys [:request :response]))) | |
(comment | |
(ix-exe env query) | |
(ix-exe env mutation) | |
(ix-exe env mutation+join) | |
(ix-exe env mutation+joins) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment