(ns chain-of-responsibility
(:require [clojure.pprint :as pp]
[clojure.spec.alpha :as s]))
;; === Protocols and Utility Functions ===
(defprotocol RequestHandler
(handle-request [this request])
(set-next [this handler]))
(defprotocol ErrorHandler
(error [this message]))
(extend-type clojure.lang.PersistentArrayMap
ErrorHandler
(error [_ message] {:error message}))
(defmacro defhandler [name fields & body]
`(defrecord ~name ~fields
RequestHandler
~@body))
;; === Validation Specifications ===
(s/def ::data (s/map-of keyword? string?))
(s/def ::request (s/keys :req-un [::data]))
;; === Handlers ===
(defhandler AuthenticationHandler [next-handler]
(handle-request [this request]
"Handle authentication by checking for a valid auth token."
(if-let [auth-token (:auth-token request)]
(if (= auth-token "valid-token")
(if next-handler
(handle-request next-handler (assoc request :authenticated true))
(assoc request :authenticated true))
(error {} "Invalid authentication token"))
(error {} "Missing authentication token")))
(set-next [_ handler]
"Set the next handler in the chain."
(->AuthenticationHandler handler)))
(defhandler AuthorizationHandler [next-handler]
(handle-request [this request]
"Handle authorization by checking user roles."
(if (:authenticated request)
(if (contains? (:roles request) :admin)
(if next-handler
(handle-request next-handler (assoc request :authorized true))
(assoc request :authorized true))
(error {} "Insufficient permissions"))
(if next-handler
(handle-request next-handler request)
request)))
(set-next [_ handler]
"Set the next handler in the chain."
(->AuthorizationHandler handler)))
(defhandler ValidationHandler [next-handler]
(handle-request [this request]
"Validate request data format."
(if (s/valid? ::request request)
(if next-handler
(handle-request next-handler (assoc request :validated true))
(assoc request :validated true))
(error {} "Invalid request data format")))
(set-next [_ handler]
"Set the next handler in the chain."
(->ValidationHandler handler)))
(defhandler LoggingHandler [next-handler]
(handle-request [this request]
"Log the request and response."
(println "\nProcessing request:")
(pp/pprint (dissoc request :handler))
(let [response (if next-handler
(handle-request next-handler request)
request)]
(println "\nResponse:")
(pp/pprint response)
response))
(set-next [_ handler]
"Set the next handler in the chain."
(->LoggingHandler handler)))
(def request-cache (atom {}))
(defhandler CacheHandler [next-handler]
(handle-request [this request]
"Handle caching of requests."
(if-let [cached (@request-cache (:id request))]
(do
(println "Cache hit for request:" (:id request))
cached)
(let [response (if next-handler
(handle-request next-handler request)
request)]
(when (:id request)
(swap! request-cache assoc (:id request) response))
response)))
(set-next [_ handler]
"Set the next handler in the chain."
(->CacheHandler handler)))
;; === Request Processing ===
(defn build-chain []
(-> (->LoggingHandler nil)
(set-next (->CacheHandler nil))
(set-next (->AuthenticationHandler nil))
(set-next (->AuthorizationHandler nil))
(set-next (->ValidationHandler nil))))
;; === Example Usage ===
(defn run-examples []
(let [chain (build-chain)]
(println "\n=== Valid Admin Request ===")
(handle-request chain
{:id "req-1"
:auth-token "valid-token"
:roles #{:admin}
:data {:name "Borba"
:action "read"}})
(println "\n=== Invalid Token ===")
(handle-request chain
{:id "req-2"
:auth-token "invalid-token"
:roles #{:admin}
:data {:name "John"}})
(println "\n=== Missing Token ===")
(handle-request chain
{:id "req-3"
:roles #{:admin}
:data {:name "Alice"}})
(println "\n=== Insufficient Permissions ===")
(handle-request chain
{:id "req-4"
:auth-token "valid-token"
:roles #{:user}
:data {:name "Olívia"}})
(println "\n=== Invalid Data ===")
(handle-request chain
{:id "req-5"
:auth-token "valid-token"
:roles #{:admin}
:data {:name 123}})
(println "\n=== Cached Request ===")
(handle-request chain
{:id "req-1"
:auth-token "valid-token"
:roles #{:admin}
:data {:name "Borba"
:action "read"}})))
(run-examples)
![Cover image for Clojure Is Awesome!!! [PART 12]](https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0wrb9laph9xgxxq9f74.jpg)
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)