Improved error handling and add specs to emails.

This commit is contained in:
Andrey Antukh 2019-11-26 13:34:37 +01:00
parent 792303a833
commit d1b000dcc6
7 changed files with 50 additions and 50 deletions

View file

@ -6,6 +6,7 @@
(ns user (ns user
(:require (:require
[clojure.spec.alpha :as s]
[clojure.tools.namespace.repl :as repl] [clojure.tools.namespace.repl :as repl]
[clojure.walk :refer [macroexpand-all]] [clojure.walk :refer [macroexpand-all]]
[clojure.pprint :refer [pprint]] [clojure.pprint :refer [pprint]]

View file

@ -21,9 +21,13 @@
{:static media/resolve-asset {:static media/resolve-asset
:comment (constantly nil)}) :comment (constantly nil)})
(s/def ::name ::us/string)
(s/def ::register
(s/keys :req-un [::name]))
(def register (def register
"A new profile registration welcome email." "A new profile registration welcome email."
(emails/build :register default-context)) (emails/build ::register default-context))
(defn render (defn render
[email context] [email context]

View file

@ -41,11 +41,11 @@
(vxi/params) (vxi/params)
(vxi/cors cors-opts) (vxi/cors cors-opts)
interceptors/parse-request-body interceptors/parse-request-body
interceptors/format-response-body] interceptors/format-response-body
(vxi/errors errors/handle)]
routes [["/api" {:interceptors interceptors} routes [["/api" {:interceptors interceptors}
["/echo" {:interceptors [(session/auth)] ["/echo" {:all handlers/echo-handler}]
:all handlers/echo-handler}]
["/login" {:post handlers/login-handler}] ["/login" {:post handlers/login-handler}]
["/logout" {:post handlers/logout-handler}] ["/logout" {:post handlers/logout-handler}]
["/register" {:post handlers/register-handler}] ["/register" {:post handlers/register-handler}]

View file

@ -6,7 +6,9 @@
(ns uxbox.http.errors (ns uxbox.http.errors
"A errors handling for the http server." "A errors handling for the http server."
(:require [io.aviso.exception :as e])) (:require
[clojure.tools.logging :as log]
[io.aviso.exception :as e]))
(defmulti handle-exception #(:type (ex-data %))) (defmulti handle-exception #(:type (ex-data %)))
@ -30,32 +32,14 @@
(defmethod handle-exception :default (defmethod handle-exception :default
[err] [err]
(println "--- START REQ EXCEPTION ---") (log/error err "Unhandled exception on request:")
(e/write-exception err)
(println "--- END REQ EXCEPTION ---")
{:status 500 {:status 500
:body {:type :exception :body {:type :exception
:message (ex-message err)}}) :message (ex-message err)}})
(defn- handle-data-access-exception
[err]
(let [err (.getCause err)
state (.getSQLState err)
message (.getMessage err)]
(case state
"P0002" {:status 412 ;; precondition-failed
:body {:message message
:type :occ}}
(handle-exception err))))
(defn handle (defn handle
[error] [error req]
(cond (if (or (instance? java.util.concurrent.CompletionException error)
(or (instance? java.util.concurrent.CompletionException error) (instance? java.util.concurrent.ExecutionException error))
(instance? java.util.concurrent.ExecutionException error)) (handle-exception (.getCause error))
(handle (.getCause error)) (handle-exception error)))
;; (instance? org.jooq.exception.DataAccessException error)
;; (handle-data-access-exception error)
:else (handle-exception error)))

View file

@ -8,6 +8,7 @@
(:require (:require
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[promesa.core :as p] [promesa.core :as p]
[uxbox.emails :as emails]
[uxbox.http.errors :as errors] [uxbox.http.errors :as errors]
[uxbox.http.session :as session] [uxbox.http.session :as session]
[uxbox.services.core :as sv] [uxbox.services.core :as sv]
@ -20,11 +21,9 @@
{::sv/type (keyword type) {::sv/type (keyword type)
:user (:user req)})] :user (:user req)})]
(-> (sv/query (with-meta data {:req req})) (-> (sv/query (with-meta data {:req req}))
(p/handle (fn [result error] (p/then' (fn [result]
(if error {:status 200
(errors/handle error) :body result})))))
{:status 200
:body result}))))))
(defn mutation-handler (defn mutation-handler
[req] [req]
@ -35,10 +34,8 @@
{::sv/type (keyword type) {::sv/type (keyword type)
:user (:user req)})] :user (:user req)})]
(-> (sv/mutation (with-meta data {:req req})) (-> (sv/mutation (with-meta data {:req req}))
(p/handle (fn [result error] (p/then' (fn [result]
(if error {:status 200 :body result})))))
(errors/handle error)
{:status 200 :body result}))))))
(defn login-handler (defn login-handler
[req] [req]
@ -46,22 +43,20 @@
user-agent (get-in req [:headers "user-agent"])] user-agent (get-in req [:headers "user-agent"])]
(-> (sv/mutation (assoc data ::sv/type :login)) (-> (sv/mutation (assoc data ::sv/type :login))
(p/then #(session/create % user-agent)) (p/then #(session/create % user-agent))
(p/then (fn [token] (p/then' (fn [token]
{:status 204 {:status 204
:cookies {"auth-token" {:value token}} :cookies {"auth-token" {:value token}}
:body ""})) :body ""})))))
(p/catch errors/handle))))
(defn logout-handler (defn logout-handler
[req] [req]
(let [token (get-in req [:cookies "auth-token"]) (let [token (get-in req [:cookies "auth-token"])
token (uuid/from-string token)] token (uuid/from-string token)]
(-> (session/delete token) (-> (session/delete token)
(p/then (fn [token] (p/then' (fn [token]
{:status 204 {:status 204
:cookies {"auth-token" {:value nil}} :cookies {"auth-token" {:value nil}}
:body ""})) :body ""})))))
(p/catch errors/handle))))
(defn register-handler (defn register-handler
[req] [req]
@ -74,8 +69,7 @@
(p/then' (fn [token] (p/then' (fn [token]
{:status 204 {:status 204
:cookies {"auth-token" {:value token}} :cookies {"auth-token" {:value token}}
:body ""})) :body ""})))))
(p/catch' errors/handle))))
(defn echo-handler (defn echo-handler
[req] [req]

View file

@ -91,6 +91,9 @@
(s/assert keyword? id) (s/assert keyword? id)
(fn [context] (fn [context]
(s/assert ::context context) (s/assert ::context context)
(when-let [spec (s/get-spec id)]
(s/assert spec context))
(let [context (merge extra-context context) (let [context (merge extra-context context)
email (impl-build-email id context)] email (impl-build-email id context)]
(when-not email (when-not email

View file

@ -108,6 +108,20 @@
(.fileUploads ^RoutingContext context))] (.fileUploads ^RoutingContext context))]
(update data :request assoc attr (persistent! uploads))))})) (update data :request assoc attr (persistent! uploads))))}))
;; --- Errors
(defn errors
"A error handling interceptor."
[handler-fn]
{:error
(fn [data]
(let [request (:request data)
error (:error data)
response (handler-fn error request)]
(-> data
(assoc :response response)
(dissoc :error))))})
;; --- CORS ;; --- CORS
(s/def ::origin string?) (s/def ::origin string?)