From 49776c5db7791cb4e6bdab31d50463e7a93b8ab6 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 12 Jun 2021 08:13:35 +0900 Subject: [PATCH 1/5] replaced interceptors internal implementation with metosin/siepppari library --- project.clj | 1 + src/stepwise/activities.clj | 16 +--- src/stepwise/interceptors/core.clj | 113 ++++++++++------------- test/stepwise/interceptors/core_test.clj | 17 ++-- 4 files changed, 63 insertions(+), 84 deletions(-) diff --git a/project.clj b/project.clj index 9994386..39efa40 100644 --- a/project.clj +++ b/project.clj @@ -22,6 +22,7 @@ [org.clojure/tools.logging "1.1.0"] [org.clojure/data.json "2.2.2"] [org.clojure/core.async "1.3.618"] + [metosin/sieppari "0.0.0-alpha13"] [com.amazonaws/aws-java-sdk-iam "1.11.1007"] [com.amazonaws/aws-java-sdk-sts "1.11.1007"] [com.amazonaws/aws-java-sdk-stepfunctions "1.11.1007"]]) diff --git a/src/stepwise/activities.clj b/src/stepwise/activities.clj index e92b2f4..ed24a23 100644 --- a/src/stepwise/activities.clj +++ b/src/stepwise/activities.clj @@ -43,20 +43,14 @@ [activity-name (ensure activity-name)])) activity-names)) -(defn make-handler-interceptor [handler-fn] - [:handler - {:before (fn [{input :input :as env}] - (assoc env :output (handler-fn input)))}]) - -(defn identity-handler-fn [input] input) - (defn compile [handler] (if (fn? handler) handler - (interceptors/compile (into (vec (:interceptors handler)) - [(make-handler-interceptor (get handler - :handler-fn - identity-handler-fn))])))) + (interceptors/compile + (vec (:interceptors handler)) + (get handler + :handler-fn + identity)))) (defn compile-all [activity->handler] (into {} diff --git a/src/stepwise/interceptors/core.clj b/src/stepwise/interceptors/core.clj index 1e30aa0..3eb8f48 100644 --- a/src/stepwise/interceptors/core.clj +++ b/src/stepwise/interceptors/core.clj @@ -1,77 +1,58 @@ (ns stepwise.interceptors.core (:refer-clojure :exclude [compile]) - (:require [clojure.tools.logging :as log])) + (:require [sieppari.core :as s])) -; cribbed from re-frame -- thanks re-frame! +(defn well-formed-interceptor-tuple? + "Interceptor-tuple should be a tuple of the form, + [:name {:enter (fn [ctx] ... (update ctx :request myfn1)) + :leave (fn [ctx] ... (update ctx :response myfn2))}] -(defn- invoke-interceptor-fn - ; TODO clean up names - [context [id interceptor] direction] - (log/trace (prn-str {:interceptor id - :direction direction - :context context})) - (if-let [f (get interceptor direction)] - (f context) - context)) + A few notes: -(defn- invoke-interceptors - ([context direction] - (loop [context context] - (let [queue (:queue context)] ;; future interceptors - (if (empty? queue) - context - (let [interceptor (peek queue) ;; next interceptor to call - stack (:stack context)] ;; already completed interceptors - (recur (-> context - (assoc :queue (pop queue) - :stack (conj stack interceptor)) - (invoke-interceptor-fn interceptor direction))))))))) + 1. `:enter` and `:leave` functions need to return `ctx` or an updated + version of `ctx` for the next interceptor in the chain + 2. either `:enter` or `:leave` fn can be nil if not used + 3. incoming data are tucked away in `:request` for `:enter` fn + 4. outgoing data are in `:response` for `:leave` fn. -(defn- change-direction [context] - (-> context - (dissoc :queue) - (assoc :queue (:stack context)))) - -(defn well-formed-interceptor? - "Interceptor should be a tuple of the form, - [:name {:before (fn [env] ... env) - :after (fn [env] ... env)}] - - :before and :after fn needs to return env or an updated version of env for - the next interceptor in the queue. Either fn can be nil if not used." - [interceptor] - (let [stage-map (second interceptor)] - (and (vector? interceptor) - (= (count interceptor) 2) - (keyword? (first interceptor)) - (map? stage-map) - (or (nil? (:before stage-map)) - (fn? (:before stage-map))) - (or (nil? (:after stage-map)) - (fn? (:after stage-map)))))) + Reference: + https://github.com/metosin/sieppari" + [interceptor-tuple] + (let [[interceptor-name interceptor-map] interceptor-tuple] + (and (vector? interceptor-tuple) + (= (count interceptor-tuple) 2) + (keyword? interceptor-name) + (map? interceptor-map) + (or (nil? (:enter interceptor-map)) + (fn? (:enter interceptor-map))) + (or (nil? (:leave interceptor-map)) + (fn? (:leave interceptor-map)))))) + +(defn assoc-send-heartbeat-fn-to-context-interceptor + [send-heartbeat-fn] + {:enter (fn [ctx] (assoc ctx :send-heartbeat-fn send-heartbeat-fn))}) + +(defn interceptor-tuples->interceptors [interceptor-tuples] + (map second interceptor-tuples)) (defn compile - "Returns a fn that exercises a queue of interceptors against a task and returns a result. - - Reference: - https://day8.github.io/re-frame/Interceptors/" - [queue] - (doseq [[index interceptor] (map vector - (range 0 (count queue)) - queue)] - (when-not (well-formed-interceptor? interceptor) - (throw (ex-info "Malformed interceptor" + "Returns a fn that exercises a chain of interceptors against a task + and returns a result. Uses metosin/siepppari under the hood." + [named-chain handler-fn] + (doseq [[index interceptor-tuple] (map vector + (range 0 (count named-chain)) + named-chain)] + (when-not (well-formed-interceptor-tuple? interceptor-tuple) + (throw (ex-info "Malformed interceptor-tuple. See (doc stepwise.interceptors.core/well-formed-interceptor-tuple?) for example." {:index index - :form interceptor})))) - (with-meta (fn execute [input send-heartbeat] - (-> {:input input - :output nil - :context {:send-heartbeat send-heartbeat} - :stack () - :queue (into () (reverse queue))} - (invoke-interceptors :before) - change-direction - (invoke-interceptors :after) - :output)) + :form interceptor-tuple})))) + + (with-meta (fn [input send-heartbeat-fn] + (s/execute + (concat + (cons (assoc-send-heartbeat-fn-to-context-interceptor send-heartbeat-fn) + (interceptor-tuples->interceptors named-chain)) + [handler-fn]) + input)) {:heartbeat? true})) diff --git a/test/stepwise/interceptors/core_test.clj b/test/stepwise/interceptors/core_test.clj index 886bbaa..27ec59d 100644 --- a/test/stepwise/interceptors/core_test.clj +++ b/test/stepwise/interceptors/core_test.clj @@ -3,14 +3,17 @@ (:require [clojure.test :as test] [stepwise.interceptors.core :as main])) -(def hello-world - [:hello-world {:before (fn [context] - (assoc context :output :hello-world))}]) +(def inc-x-interceptor-tuple + [:inc-x + {:enter (fn [ctx] (update-in ctx [:request :x] inc))}]) + +(defn handler [request] + {:y (inc (:x request))}) (defn execute [queue task] - ((main/compile queue) task (fn []))) + ((main/compile queue handler) task (fn []))) -(test/deftest compile - (test/is (= :hello-world - (execute [hello-world] {})))) +(test/deftest compile-test + (test/is (= {:y 42} + (execute [inc-x-interceptor-tuple] {:x 40})))) From ec97c786842f1d7314f654ff586b8ec36e06f424 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 12 Jun 2021 08:32:01 +0900 Subject: [PATCH 2/5] refactor out assert-named-chain fn --- src/stepwise/interceptors/core.clj | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/stepwise/interceptors/core.clj b/src/stepwise/interceptors/core.clj index 3eb8f48..d369f89 100644 --- a/src/stepwise/interceptors/core.clj +++ b/src/stepwise/interceptors/core.clj @@ -28,6 +28,16 @@ (or (nil? (:leave interceptor-map)) (fn? (:leave interceptor-map)))))) +(defn assert-named-chain + [named-chain] + (doseq [[index interceptor-tuple] (map vector + (range 0 (count named-chain)) + named-chain)] + (when-not (well-formed-interceptor-tuple? interceptor-tuple) + (throw (ex-info "Malformed interceptor-tuple. See (doc stepwise.interceptors.core/well-formed-interceptor-tuple?) for example." + {:index index + :form interceptor-tuple}))))) + (defn assoc-send-heartbeat-fn-to-context-interceptor [send-heartbeat-fn] {:enter (fn [ctx] (assoc ctx :send-heartbeat-fn send-heartbeat-fn))}) @@ -36,16 +46,10 @@ (map second interceptor-tuples)) (defn compile - "Returns a fn that exercises a chain of interceptors against a task + "Returns a fn that exercises a chain of interceptor-tuples against a task and returns a result. Uses metosin/siepppari under the hood." [named-chain handler-fn] - (doseq [[index interceptor-tuple] (map vector - (range 0 (count named-chain)) - named-chain)] - (when-not (well-formed-interceptor-tuple? interceptor-tuple) - (throw (ex-info "Malformed interceptor-tuple. See (doc stepwise.interceptors.core/well-formed-interceptor-tuple?) for example." - {:index index - :form interceptor-tuple})))) + (assert-named-chain named-chain) (with-meta (fn [input send-heartbeat-fn] (s/execute From e74ca5e90cb85bd1e88282ce885dbd364c3963ff Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 12 Jun 2021 08:40:12 +0900 Subject: [PATCH 3/5] add test case to ensure that send-heartbeat-fn is attached to context --- test/stepwise/interceptors/core_test.clj | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/stepwise/interceptors/core_test.clj b/test/stepwise/interceptors/core_test.clj index 27ec59d..0cbbeed 100644 --- a/test/stepwise/interceptors/core_test.clj +++ b/test/stepwise/interceptors/core_test.clj @@ -1,6 +1,6 @@ (ns stepwise.interceptors.core-test (:refer-clojure :exclude [compile]) - (:require [clojure.test :as test] + (:require [clojure.test :refer [deftest testing is]] [stepwise.interceptors.core :as main])) (def inc-x-interceptor-tuple @@ -13,7 +13,13 @@ (defn execute [queue task] ((main/compile queue handler) task (fn []))) -(test/deftest compile-test - (test/is (= {:y 42} - (execute [inc-x-interceptor-tuple] {:x 40})))) +(deftest compile-test + (testing "interceptor with handler-fn" + (is (= {:y 42} + (execute [inc-x-interceptor-tuple] {:x 40})))) + + (testing "send-heartbeat-fn is associated to internal context" + (execute [[:check-heartbeat-fn + {:enter (fn [ctx] (is (fn? (:send-heartbeat-fn ctx))) ctx)}]] + {:x 40}))) From a38086ecdb52385bcc0894139845b725577381a1 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 12 Jun 2021 08:49:29 +0900 Subject: [PATCH 4/5] refactor out some helper fns in interceptors.core --- src/stepwise/interceptors/core.clj | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/stepwise/interceptors/core.clj b/src/stepwise/interceptors/core.clj index d369f89..107b338 100644 --- a/src/stepwise/interceptors/core.clj +++ b/src/stepwise/interceptors/core.clj @@ -38,13 +38,20 @@ {:index index :form interceptor-tuple}))))) -(defn assoc-send-heartbeat-fn-to-context-interceptor +(defn- assoc-send-heartbeat-fn-to-context-interceptor [send-heartbeat-fn] {:enter (fn [ctx] (assoc ctx :send-heartbeat-fn send-heartbeat-fn))}) -(defn interceptor-tuples->interceptors [interceptor-tuples] +(defn- interceptor-tuples->interceptors [interceptor-tuples] (map second interceptor-tuples)) +(defn- prepend-this-interceptor-to-interceptor-chain [this-interceptor chain] + (cons this-interceptor + chain)) + +(defn- form-interceptor-chain [handler-fn interceptors] + (concat interceptors [handler-fn])) + (defn compile "Returns a fn that exercises a chain of interceptor-tuples against a task and returns a result. Uses metosin/siepppari under the hood." @@ -53,10 +60,10 @@ (with-meta (fn [input send-heartbeat-fn] (s/execute - (concat - (cons (assoc-send-heartbeat-fn-to-context-interceptor send-heartbeat-fn) - (interceptor-tuples->interceptors named-chain)) - [handler-fn]) + (->> named-chain + (interceptor-tuples->interceptors) + (prepend-this-interceptor-to-interceptor-chain (assoc-send-heartbeat-fn-to-context-interceptor send-heartbeat-fn)) + (form-interceptor-chain handler-fn)) input)) {:heartbeat? true})) From 35bb49fa6c831f361468db2376b1691fc11fe5a3 Mon Sep 17 00:00:00 2001 From: Paul Lam Date: Sat, 12 Jun 2021 08:56:39 +0900 Subject: [PATCH 5/5] update send-heartbeat-interceptor --- src/stepwise/interceptors.clj | 15 ++++++++------- test/stepwise/interceptors_test.clj | 6 +++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/stepwise/interceptors.clj b/src/stepwise/interceptors.clj index f91fb66..b49900b 100644 --- a/src/stepwise/interceptors.clj +++ b/src/stepwise/interceptors.clj @@ -27,19 +27,20 @@ false))] (when continue? (recur))))) -(defn send-heartbeat-interceptor-fn +(defn send-heartbeat-interceptor "Usage: (stepwise/start-workers! ::addr {:handler-fn add - :interceptors [[:send-heartbeat - {:before (send-heartbeat-interceptor-fn 10)}]]} + :interceptors [send-heartbeat-interceptor [...] [...] ...]} " [n-seconds] - (fn [{{heartbeat-fn :send-heartbeat} :context - :as env}] - (beat-heart-every-n-seconds! (heartbeat-fn) n-seconds) - env)) + [:send-heartbeat + {:enter + (fn [{send-heartbeat-fn :send-heartbeat-fn + :as ctx}] + (beat-heart-every-n-seconds! (send-heartbeat-fn) n-seconds) + ctx)}]) diff --git a/test/stepwise/interceptors_test.clj b/test/stepwise/interceptors_test.clj index 2a090f1..ef08560 100644 --- a/test/stepwise/interceptors_test.clj +++ b/test/stepwise/interceptors_test.clj @@ -2,7 +2,8 @@ (:refer-clojure :exclude [compile]) (:require [clojure.test :refer [deftest testing is]] [bond.james :as bond] - [stepwise.interceptors :as i])) + [stepwise.interceptors :as i] + [stepwise.interceptors.core :refer [well-formed-interceptor-tuple?]])) (defn- heartbeat-fn [] :foo) (defn- failing-heartbeat-fn [] (throw (Exception. "testing failure mode"))) @@ -23,3 +24,6 @@ (Thread/sleep (* (inc n) period-sec 1000)) (is (= 1 (-> failing-heartbeat-fn bond/calls count))))))) +(deftest send-heartbeat-interceptor-test + (is (well-formed-interceptor-tuple? (i/send-heartbeat-interceptor 5)))) +