Skip to content

Instantly share code, notes, and snippets.

@indrajithi
Last active October 23, 2019 04:42
Show Gist options
  • Select an option

  • Save indrajithi/d2df74452fd6e9db7ea474cc8eb363e0 to your computer and use it in GitHub Desktop.

Select an option

Save indrajithi/d2df74452fd6e9db7ea474cc8eb363e0 to your computer and use it in GitHub Desktop.
Compojure Exception Handeling Middleware, Clojure.
(ns nurture-clj.middleware.exception-handler
(:require [clj-http.client :as client]
[cheshire.core :refer [generate-string]]
[config.core :refer [env]]
[nurture-clj.log :as log]
[clojure.string :as str]
[schema.utils :as sc]
[config.core :refer [env]]
[ring.util.http-response :as respond]))
(defn send-to-slack
[url request exception]
(client/post url
{:body (generate-string
{:text
(str "```"
(generate-string
{:exception (str exception)
:request_url (get request :uri)})
"```")})}))
(defn get-nested-error-reason
"get error reason from nested validation.
first error found is returned"
[error]
(let [er (if (symbol? error)
(str error)
(if (map? error)
(get-nested-error-reason (second (first error)))
;recur with first map value
(if (= (sc/type-of error) schema.utils.ValidationError)
(get-nested-error-reason (sc/validation-error-explain error))
(str/join " " (drop-last 1 (second error))))))]
(if (empty? er) "invalid" er)))
(defn parse-error-message
"parse validation error message and return response error message"
[error-symbols]
(if (symbol? error-symbols)
(str error-symbols) ; if the type is of symbol return that str
(try
(let [error-response-map
{"disallowed-key" "disallowed-key"
"sequential?" "sequential data requires to be in []"
"map?" "hashmap data requires to be inside {}"
"instance?" "invalid-type"
"missing-required-key" "missing-required-key"}
invalid-key (str (last (second error-symbols)))
reason (get-nested-error-reason error-symbols)
response (filter #(not (nil? %))
(map (fn [[key value]]
(if (re-find (re-pattern key) reason)
value
(if (re-find #"valid" reason)
(str "not a " reason))))
error-response-map))]
(if (empty? response) "invalid" (first response)))
(catch Exception e "invalid"))))
(defn get-base-error-type [errors]
(if (= (sc/type-of errors) schema.utils.ValidationError)
:validation_error
(if (= (map? errors))
:error-map)))
(defn get-formated-error-message [errors]
(reduce (fn [new-map [key val]]
(if (= (sc/type-of val) schema.utils.ValidationError)
(assoc new-map key (parse-error-message
(sc/validation-error-explain val)))
(if (or (symbol? val)
(= (type val) clojure.lang.PersistentArrayMap))
(assoc new-map key (parse-error-message val))
(if (= (sc/type-of val) clojure.lang.PersistentVector)
(assoc new-map key
(parse-error-message
(sc/validation-error-explain
(first (remove nil? val))))) ;noqa
(assoc new-map key "invalid")))))
{}
errors))
(defn generate-error-message
"generates error messages from exceptoon"
[ex]
(let [errors (get-in (first (get (Throwable->map ex) :via)) [:data :errors])
base-error-type (get-base-error-type errors)]
(if (= base-error-type :validation_error)
"Expecting a json input"
(if (and (re-find #"Request validation failed" (.getMessage ex))
(= base-error-type :error-map))
(let [
formated-error-message (get-formated-error-message errors)]
(log/error "EXCEPTION bad request status code 400" formated-error-message)
formated-error-message)
"unknown error"))))
(defn wrap-exception
"Every request will be wraped in this. When there is an exception it will be caught here and the stack trace will be
sent to slack channel. The client will receive 500 with a custome message."
[handler]
(fn [request]
(if (= (get env :env) "dev")
(handler request)
(try
(log/info "REQUEST" "URI: " (get request :uri))
(handler request)
(catch org.postgresql.util.PSQLException e
(log/error " EXCEPTION " (.getMessage e))
{:status 400
:body {:response "Invalid Data"}})
(catch clojure.lang.ExceptionInfo e
(log/error " EXCEPTION " (.getMessage e))
{:status 400
:body {:errors (generate-error-message e)}})
(catch Exception e
;This will be an internal server error; 500
;We will catch it and sent exception to slack handle
(log/error " EXCEPTION " (.getMessage e))
(send-to-slack (:slack-alert-url env) request (.getMessage e))
{:status 500
:body "Someting went wrong! It is me not you."})))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment