mirror of
https://github.com/penpot/penpot.git
synced 2025-03-13 16:21:57 -05:00
✨ Improve backend and worker error handling
This commit is contained in:
parent
c026d05bc3
commit
701a98fab6
7 changed files with 112 additions and 47 deletions
|
@ -49,12 +49,20 @@
|
|||
|
||||
(defmethod handle-exception :validation
|
||||
[err _]
|
||||
(let [data (ex-data err)
|
||||
explain (us/pretty-explain data)]
|
||||
(yrs/response :status 400
|
||||
:body (-> data
|
||||
(dissoc ::s/problems ::s/value)
|
||||
(cond-> explain (assoc :explain explain))))))
|
||||
(let [{:keys [code] :as data} (ex-data err)]
|
||||
(cond
|
||||
(= code :spec-validation)
|
||||
(let [explain (us/pretty-explain data)]
|
||||
(yrs/response :status 400
|
||||
:body (-> data
|
||||
(dissoc ::s/problems ::s/value)
|
||||
(cond-> explain (assoc :explain explain)))))
|
||||
|
||||
(= code :request-body-too-large)
|
||||
(yrs/response :status 413 :body data)
|
||||
|
||||
:else
|
||||
(yrs/response :status 400 :body data))))
|
||||
|
||||
(defmethod handle-exception :assertion
|
||||
[error request]
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
[yetti.middleware :as ymw]
|
||||
[yetti.request :as yrq]
|
||||
[yetti.response :as yrs])
|
||||
(:import java.io.OutputStream))
|
||||
(:import
|
||||
com.fasterxml.jackson.core.io.JsonEOFException
|
||||
io.undertow.server.RequestTooBigException
|
||||
java.io.OutputStream))
|
||||
|
||||
(def server-timing
|
||||
{:name ::server-timing
|
||||
|
@ -46,16 +49,29 @@
|
|||
(update :params merge params))))
|
||||
|
||||
:else
|
||||
request)))]
|
||||
request)))
|
||||
|
||||
(handle-error [raise cause]
|
||||
(cond
|
||||
(instance? RequestTooBigException cause)
|
||||
(raise (ex/error :type :validation
|
||||
:code :request-body-too-large
|
||||
:hint (ex-message cause)))
|
||||
|
||||
(instance? JsonEOFException cause)
|
||||
(raise (ex/error :type :validation
|
||||
:code :malformed-json
|
||||
:hint (ex-message cause)))
|
||||
:else
|
||||
(raise cause)))]
|
||||
|
||||
(fn [request respond raise]
|
||||
(when-let [request (try
|
||||
(process-request request)
|
||||
(catch Exception cause
|
||||
(raise (ex/error :type :validation
|
||||
:code :malformed-params
|
||||
:hint (ex-message cause)
|
||||
:cause cause))))]
|
||||
(catch RuntimeException cause
|
||||
(handle-error raise (or (.getCause cause) cause)))
|
||||
(catch Throwable cause
|
||||
(handle-error raise cause)))]
|
||||
(handler request respond raise)))))
|
||||
|
||||
(def parse-request
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
:read-only (cf/get :database-readonly false)
|
||||
:metrics (ig/ref :app.metrics/metrics)
|
||||
:migrations (ig/ref :app.migrations/all)
|
||||
:name :main
|
||||
:min-size (cf/get :database-min-pool-size 0)
|
||||
:max-size (cf/get :database-max-pool-size 30)}
|
||||
:name :main
|
||||
:min-size (cf/get :database-min-pool-size 0)
|
||||
:max-size (cf/get :database-max-pool-size 30)}
|
||||
|
||||
;; Default thread pool for IO operations
|
||||
[::default :app.worker/executor]
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
(ns app.main.errors
|
||||
"Generic error handling"
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.pprint :as pp]
|
||||
[app.config :as cf]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.users :as du]
|
||||
|
@ -17,7 +19,6 @@
|
|||
[app.util.i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as ts]
|
||||
[fipp.edn :as fpp]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn on-error
|
||||
|
@ -59,16 +60,15 @@
|
|||
(defmethod ptk/handle-error :validation
|
||||
[error]
|
||||
(ts/schedule
|
||||
(st/emitf
|
||||
(msg/show {:content "Unexpected validation error."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
#(st/emit! (msg/show {:content "Validation error"
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
|
||||
;; Print to the console some debug info.
|
||||
(js/console.group "Validation Error:")
|
||||
(ex/ignoring
|
||||
(js/console.info
|
||||
(with-out-str (fpp/pprint (dissoc error :explain)))))
|
||||
(pp/pprint-str (dissoc error :explain))))
|
||||
|
||||
(when-let [explain (:explain error)]
|
||||
(js/console.group "Spec explain:")
|
||||
|
@ -78,24 +78,46 @@
|
|||
(js/console.groupEnd "Validation Error:"))
|
||||
|
||||
|
||||
;; All the errors that happens on worker are handled here.
|
||||
(defmethod ptk/handle-error :worker-error
|
||||
[{:keys [code data hint] :as error}]
|
||||
(let [hint (or hint (:hint data) (:message data) (d/name code))
|
||||
info (pp/pprint-str (dissoc data :explain))
|
||||
msg (dm/str "Internal Worker Error: " hint)]
|
||||
|
||||
(ts/schedule
|
||||
#(st/emit!
|
||||
(msg/show {:content "Something wrong has happened (on worker)."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
|
||||
(js/console.group msg)
|
||||
(js/console.info info)
|
||||
|
||||
(when-let [explain (:explain data)]
|
||||
(js/console.group "Spec explain:")
|
||||
(js/console.log explain)
|
||||
(js/console.groupEnd "Spec explain:"))
|
||||
|
||||
(js/console.groupEnd msg)))
|
||||
|
||||
|
||||
;; Error on parsing an SVG
|
||||
;; TODO: looks unused and deprecated
|
||||
(defmethod ptk/handle-error :svg-parser
|
||||
[_]
|
||||
(ts/schedule
|
||||
(st/emitf
|
||||
(msg/show {:content "SVG is invalid or malformed"
|
||||
:type :error
|
||||
:timeout 3000}))))
|
||||
#(st/emit! (msg/show {:content "SVG is invalid or malformed"
|
||||
:type :error
|
||||
:timeout 3000}))))
|
||||
|
||||
;; TODO: should be handled in the event and not as general error handler
|
||||
(defmethod ptk/handle-error :comment-error
|
||||
[_]
|
||||
(ts/schedule
|
||||
(st/emitf
|
||||
(msg/show {:content "There was an error with the comment"
|
||||
:type :error
|
||||
:timeout 3000}))))
|
||||
#(st/emit! (msg/show {:content "There was an error with the comment"
|
||||
:type :error
|
||||
:timeout 3000}))))
|
||||
|
||||
;; This is a pure frontend error that can be caused by an active
|
||||
;; assertion (assertion that is preserved on production builds). From
|
||||
|
@ -110,10 +132,9 @@
|
|||
(dm/str cf/public-uri "js/cljs-runtime/" (:file error))
|
||||
(:line error))]
|
||||
(ts/schedule
|
||||
(st/emitf
|
||||
(msg/show {:content "Internal error: assertion."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
#(st/emit! (msg/show {:content "Internal error: assertion."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
|
||||
;; Print to the console some debugging info
|
||||
(js/console.group message)
|
||||
|
@ -139,7 +160,7 @@
|
|||
(defmethod ptk/handle-error :server-error
|
||||
[{:keys [data hint] :as error}]
|
||||
(let [hint (or hint (:hint data) (:message data))
|
||||
info (with-out-str (fpp/pprint (dissoc data :explain)))
|
||||
info (pp/pprint-str (dissoc data :explain))
|
||||
msg (dm/str "Internal Server Error: " hint)]
|
||||
|
||||
(ts/schedule
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
(= 200 status)
|
||||
(rx/of body)
|
||||
|
||||
(= 413 status)
|
||||
(rx/throw {:type :validation
|
||||
:code :request-body-too-large})
|
||||
|
||||
(and (>= status 400)
|
||||
(map? body))
|
||||
(rx/throw body)
|
||||
|
|
|
@ -54,9 +54,11 @@
|
|||
|
||||
(reply-error [cause]
|
||||
(if (map? cause)
|
||||
(post {:error cause})
|
||||
(post {:error {:type :unexpected
|
||||
:code :unhandled-error-on-worker
|
||||
(post {:error {:type :worker-error
|
||||
:code (or (:type cause) :wrapped)
|
||||
:data cause}})
|
||||
(post {:error {:type :worker-error
|
||||
:code :unhandled-error
|
||||
:hint (ex-message cause)
|
||||
:data (ex-data cause)}})))
|
||||
|
||||
|
|
|
@ -16,22 +16,35 @@
|
|||
[beicon.core :as rx]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn- not-found?
|
||||
[{:keys [type]}]
|
||||
(= :not-found type))
|
||||
|
||||
(defn- handle-response
|
||||
[response]
|
||||
[{:keys [body status] :as response}]
|
||||
(cond
|
||||
(http/success? response)
|
||||
(rx/of (:body response))
|
||||
|
||||
(http/client-error? response)
|
||||
(rx/throw (:body response))
|
||||
(= status 413)
|
||||
(rx/throw {:type :validation
|
||||
:code :request-body-too-large
|
||||
:hint "request body too large"})
|
||||
|
||||
(and (http/client-error? response)
|
||||
(map? body))
|
||||
(rx/throw body)
|
||||
|
||||
:else
|
||||
(rx/throw {:type :unexpected
|
||||
:code (:error response)})))
|
||||
(rx/throw {:type :unexpected-error
|
||||
:code :unhandled-http-response
|
||||
:http-status status
|
||||
:http-body body})))
|
||||
|
||||
(defn- not-found?
|
||||
[{:keys [type]}]
|
||||
(= :not-found type))
|
||||
|
||||
(defn- body-too-large?
|
||||
[{:keys [type code]}]
|
||||
(and (= :validation type)
|
||||
(= :request-body-too-large code)))
|
||||
|
||||
(defn- request-data-for-thumbnail
|
||||
[file-id revn]
|
||||
|
@ -88,6 +101,7 @@
|
|||
(->> (http/send! request)
|
||||
(rx/map http/conditional-decode-transit)
|
||||
(rx/mapcat handle-response)
|
||||
(rx/catch body-too-large? (constantly nil))
|
||||
(rx/map (constantly params)))))
|
||||
|
||||
(defmethod impl/handler :thumbnails/generate
|
||||
|
|
Loading…
Add table
Reference in a new issue