From f62d2085e87eaeb823c6e59271ef168e86eaecef Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 12 Feb 2024 14:54:46 +0100 Subject: [PATCH] :sparkles: Add the ability to download a report on internal error page --- .../app/main/data/workspace/persistence.cljs | 13 +--- frontend/src/app/main/errors.cljs | 14 +--- frontend/src/app/main/repo.cljs | 19 +++-- frontend/src/app/main/store.cljs | 22 +++--- frontend/src/app/main/ui/static.cljs | 75 ++++++++++++++++++- 5 files changed, 98 insertions(+), 45 deletions(-) diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index 8cfc5a87f..bcfc60b58 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -16,7 +16,6 @@ [app.main.features :as features] [app.main.repo :as rp] [app.main.store :as st] - [app.util.router :as rt] [app.util.time :as dt] [beicon.v2.core :as rx] [okulary.core :as l] @@ -177,19 +176,11 @@ (rx/of (shapes-changes-persisted-finished)))))) (rx/catch (fn [cause] - (cond - (= :authentication (:type cause)) - (rx/throw cause) - - (instance? js/TypeError cause) + (if (instance? js/TypeError cause) (->> (rx/timer 2000) (rx/map (fn [_] (persist-changes file-id file-revn changes pending-commits)))) - - :else - (rx/concat - (rx/of (rt/assign-exception cause)) - (rx/throw cause)))))))))) + (rx/throw cause))))))))) ;; Event to be thrown after the changes have been persisted (defn shapes-changes-persisted-finished diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index 0d3576059..2c70a31cd 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -129,12 +129,7 @@ :timeout 3000}))) :else - (let [message (tr "errors.generic-validation")] - (st/async-emit! - (msg/show {:content message - :type :error - :timeout 3000}))))) - + (st/async-emit! (rt/assign-exception error)))) ;; This is a pure frontend error that can be caused by an active @@ -255,12 +250,7 @@ (defmethod ptk/handle-error :server-error [error] - (ts/schedule - #(st/emit! - (msg/show {:content "Something wrong has happened (on backend)." - :type :error - :timeout 3000}))) - + (st/async-emit! (rt/assign-exception error)) (print-group! "Server Error" (fn [] (print-data! (dissoc error :data)) diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index 56a49dd58..ed71b827a 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -23,28 +23,31 @@ (rx/of nil) (= 502 status) - (rx/throw {:type :bad-gateway}) + (rx/throw (ex-info "http error" {:type :bad-gateway})) (= 503 status) - (rx/throw {:type :service-unavailable}) + (rx/throw (ex-info "http error" {:type :service-unavailable})) (= 0 (:status response)) - (rx/throw {:type :offline}) + (rx/throw (ex-info "http error" {:type :offline})) (= 200 status) (rx/of body) (= 413 status) - (rx/throw {:type :validation - :code :request-body-too-large}) + (rx/throw (ex-info "http error" + {:type :validation + :code :request-body-too-large})) (and (>= status 400) (map? body)) - (rx/throw body) + (rx/throw (ex-info "http error" body)) :else - (rx/throw {:type :unexpected-error + (rx/throw + (ex-info "http error" + {:type :unexpected-error :status status - :data body}))) + :data body})))) (def default-options {:update-file {:query-params [:id]} diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs index 7c162d030..7b02335e7 100644 --- a/frontend/src/app/main/store.cljs +++ b/frontend/src/app/main/store.cljs @@ -58,22 +58,22 @@ (defonce last-events (let [buffer (atom []) - allowed #{:app.main.data.workspace/initialize-page - :app.main.data.workspace/finalize-page - :app.main.data.workspace/initialize-file - :app.main.data.workspace/finalize-file}] + omitset #{:potok.v2.core/undefined + :app.main.data.workspace.persistence/update-persistence-status + :app.main.data.websocket/send-message + :app.main.data.workspace.notifications/handle-pointer-send + :app.util.router/assign-exception}] (->> (rx/merge (->> stream (rx/filter (ptk/type? :app.main.data.workspace.changes/commit-changes)) - (rx/map #(-> % deref :hint-origin str)) - (rx/pipe (rxo/distinct-contiguous))) - (->> stream - (rx/map ptk/type) - (rx/filter #(contains? allowed %)) - (rx/map str))) + (rx/map #(-> % deref :hint-origin))) + (rx/map ptk/type stream)) + (rx/filter #(not (contains? omitset %))) + (rx/map str) + (rx/pipe (rxo/distinct-contiguous)) (rx/scan (fn [buffer event] (cond-> (conj buffer event) - (> (count buffer) 20) + (> (count buffer) 50) (pop))) #queue []) (rx/subs! #(reset! buffer (vec %)))) diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 91c017e46..efac4c3c1 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -7,11 +7,15 @@ (ns app.main.ui.static (:require-macros [app.main.style :as stl]) (:require + [app.common.data :as d] + [app.common.pprint :as pp] [app.main.store :as st] [app.main.ui.icons :as i] + [app.util.dom :as dom] [app.util.globals :as globals] [app.util.i18n :refer [tr]] [app.util.router :as rt] + [app.util.webapi :as wapi] [rumext.v2 :as mf])) (mf/defc error-container @@ -62,16 +66,81 @@ [:div {:class (stl/css :sign-info)} [:button {:on-click on-click} (tr "labels.retry")]]])) + +(defn generate-report + [data] + (let [team-id (:current-team-id @st/state) + profile-id (:profile-id @st/state) + + trace (:app.main.errors/trace data) + instance (:app.main.errors/instance data) + content (with-out-str + (println "Hint: " (or (:hint data) (ex-message instance) "--")) + (println "Prof ID:" (str (or profile-id "--"))) + (println "Team ID:" (str (or team-id "--"))) + + (when-let [file-id (:file-id data)] + (println "File ID:" (str file-id))) + + (println) + + (println "Data:") + (loop [data data] + (-> (d/without-qualified data) + (dissoc :explain) + (d/update-when :data (constantly "(...)")) + (pp/pprint {:level 8 :length 10})) + + (println) + + (when-let [explain (:explain data)] + (print explain)) + + (when (and (= :server-error (:type data)) + (contains? data :data)) + (recur (:data data)))) + + (println "Trace:") + (println trace) + (println) + + (println "Last events:") + (pp/pprint @st/last-events {:length 200}) + + (println))] + + (wapi/create-blob content "text/plain"))) + + (mf/defc internal-error - [] - (let [on-click (mf/use-fn #(st/emit! (rt/assign-exception nil)))] + {::mf/props :obj} + [{:keys [data]}] + (let [on-click (mf/use-fn #(st/emit! (rt/assign-exception nil))) + report-uri (mf/use-ref nil) + + on-download + (mf/use-fn + (fn [event] + (dom/prevent-default event) + (when-let [uri (mf/ref-val report-uri)] + (dom/trigger-download-uri "report" "text/plain" uri))))] + + (mf/with-effect [data] + (let [report (generate-report data) + uri (wapi/create-uri report)] + (mf/set-ref-val! report-uri uri) + (fn [] + (wapi/revoke-uri uri)))) + [:> error-container {} [:div {:class (stl/css :main-message)} (tr "labels.internal-error.main-message")] [:div {:class (stl/css :desc-message)} (tr "labels.internal-error.desc-message")] + [:a {:on-click on-download} "Download report.txt"] [:div {:class (stl/css :sign-info)} [:button {:on-click on-click} (tr "labels.retry")]]])) (mf/defc exception-page + {::mf/props :obj} [{:keys [data] :as props}] (case (:type data) :not-found @@ -83,4 +152,4 @@ :service-unavailable [:& service-unavailable] - [:& internal-error])) + [:> internal-error props]))