Simplify error handling on events that communicate with backend.

This commit is contained in:
Andrey Antukh 2016-04-13 20:09:23 +03:00
parent 47a70ad5c9
commit 6d2cd7da59
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
9 changed files with 63 additions and 121 deletions

View file

@ -51,14 +51,15 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [this state s] (-apply-watch [this state s]
(letfn [(on-error [err] (letfn [(on-error [{:keys [status payload]}]
(uum/error (tr "errors.auth")) (println status payload)
(uum/error (tr "errors.auth.unauthorized"))
(rx/empty))] (rx/empty))]
(let [params {:username username (let [params {:username username
:password password :password password
:scope "webapp"}] :scope "webapp"}]
(->> (rp/req :fetch/token params) (->> (rp/req :fetch/token params)
(rx/catch on-error) (rx/catch rp/client-error? on-error)
(rx/map :payload) (rx/map :payload)
(rx/mapcat #(rx/of (logged-in %) (rx/mapcat #(rx/of (logged-in %)
(dp/fetch-projects) (dp/fetch-projects)

View file

@ -40,14 +40,10 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-success [{history :payload}] (letfn [(on-success [{history :payload}]
(->PinnedPageHistoryFetched (into [] history))) (->PinnedPageHistoryFetched (into [] history)))]
(on-failure [e]
(uum/error (tr "errors.fetch-page-history"))
(rx/empty))]
(let [params {:page id :pinned true}] (let [params {:page id :pinned true}]
(->> (rp/req :fetch/page-history params) (->> (rp/req :fetch/page-history params)
(rx/map on-success) (rx/map on-success))))))
(rx/catch on-failure))))))
(defn fetch-pinned-page-history (defn fetch-pinned-page-history
[id] [id]
@ -82,16 +78,12 @@
(-apply-watch [this state s] (-apply-watch [this state s]
(letfn [(on-success [{history :payload}] (letfn [(on-success [{history :payload}]
(let [history (into [] history)] (let [history (into [] history)]
(->PageHistoryFetched history (not (nil? since))))) (->PageHistoryFetched history (not (nil? since)))))]
(on-failure [e]
(uum/error (tr "errors.fetch-page-history"))
(rx/empty))]
(let [params (merge (let [params (merge
{:page id :max (or max 15)} {:page id :max (or max 15)}
(when since {:since since}))] (when since {:since since}))]
(->> (rp/req :fetch/page-history params) (->> (rp/req :fetch/page-history params)
(rx/map on-success) (rx/map on-success))))))
(rx/catch on-failure))))))
(defn fetch-page-history (defn fetch-page-history
([id] ([id]
@ -219,14 +211,10 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-success [{item :payload}] (letfn [(on-success [{item :payload}]
(->HistoryItemUpdated item)) (->HistoryItemUpdated item))]
(on-failure [e]
(uum/error (tr "errors.page-history-update"))
(rx/empty))]
(rx/merge (rx/merge
(->> (rp/req :update/page-history item) (->> (rp/req :update/page-history item)
(rx/map on-success) (rx/map on-success))
(rx/catch on-failure))
(->> (rx/filter history-updated? s) (->> (rx/filter history-updated? s)
(rx/take 1) (rx/take 1)
(rx/map #(refres-page-history (:page item)))))))) (rx/map #(refres-page-history (:page item))))))))

View file

@ -43,14 +43,8 @@
(defrecord FetchPages [projectid] (defrecord FetchPages [projectid]
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-loaded [{pages :payload}] (->> (rp/req :fetch/pages-by-project {:project projectid})
(->PagesFetched pages)) (rx/map (comp ->PagesFetched :payload)))))
(on-error [err]
(js/console.error err)
(rx/empty))]
(->> (rp/req :fetch/pages-by-project {:project projectid})
(rx/map on-loaded)
(rx/catch on-error)))))
(defn fetch-pages (defn fetch-pages
[projectid] [projectid]
@ -64,15 +58,11 @@
(letfn [(on-created [{page :payload}] (letfn [(on-created [{page :payload}]
(rx/of (rx/of
#(stpr/unpack-page % page) #(stpr/unpack-page % page)
#(stpr/assoc-page % page))) #(stpr/assoc-page % page)))]
(on-failed [page]
(uum/error (tr "errors.auth"))
(rx/empty))]
(let [params (-> (into {} this) (let [params (-> (into {} this)
(assoc :data {}))] (assoc :data {}))]
(->> (rp/req :create/page params) (->> (rp/req :create/page params)
(rx/mapcat on-created) (rx/mapcat on-created))))))
(rx/catch on-failed))))))
(def ^:private create-page-schema (def ^:private create-page-schema
{:name [sc/required sc/string] {:name [sc/required sc/string]
@ -102,15 +92,9 @@
(defrecord SyncPage [id] (defrecord SyncPage [id]
rs/WatchEvent rs/WatchEvent
(-apply-watch [this state s] (-apply-watch [this state s]
(letfn [(on-success [{page :payload}] (let [page (stpr/pack-page state id)]
(->PageSynced page)) (->> (rp/req :update/page page)
(on-failure [e] (rx/map (comp ->PageSynced :payload))))))
(uum/error (tr "errors.page-update"))
(rx/empty))]
(let [page (stpr/pack-page state id)]
(->> (rp/req :update/page page)
(rx/map on-success)
(rx/catch on-failure))))))
(defn sync-page (defn sync-page
[id] [id]
@ -163,13 +147,9 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [this state s] (-apply-watch [this state s]
(letfn [(on-success [{page :payload}] (letfn [(on-success [{page :payload}]
#(assoc-in % [:pages-by-id id :version] (:version page))) #(assoc-in % [:pages-by-id id :version] (:version page)))]
(on-failure [e]
(uum/error (tr "errors.page-update"))
(rx/empty))]
(->> (rp/req :update/page-metadata (into {} this)) (->> (rp/req :update/page-metadata (into {} this))
(rx/map on-success) (rx/map on-success)))))
(rx/catch on-failure)))))
(def ^:private update-page-schema (def ^:private update-page-schema
{:id [sc/required] {:id [sc/required]
@ -192,15 +172,11 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-success [_] (letfn [(on-success [_]
(rs/swap #(stpr/purge-page % id))) (rs/swap #(stpr/purge-page % id)))]
(on-failure [e]
(uum/error (tr "errors.delete-page"))
(rx/empty))]
(->> (rp/req :delete/page id) (->> (rp/req :delete/page id)
(rx/map on-success) (rx/map on-success)
(rx/tap callback) (rx/tap callback)
(rx/filter identity) (rx/filter identity)))))
(rx/catch on-failure)))))
(defn delete-page (defn delete-page
([id] (DeletePage. id (constantly nil))) ([id] (DeletePage. id (constantly nil)))

View file

@ -17,31 +17,6 @@
[uxbox.data.pages :as udp] [uxbox.data.pages :as udp]
[uxbox.ui.messages :as uum])) [uxbox.ui.messages :as uum]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn sort-projects-by
[ordering projs]
(case ordering
:name (sort-by :name projs)
:created (reverse (sort-by :created-at projs))
projs))
(defn contains-term?
[phrase term]
(str/contains? (str/lower phrase) (str/trim (str/lower term))))
(defn filter-projects-by
[term projs]
(if (str/blank? term)
projs
(filter #(contains-term? (:name %) term) projs)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; --- Projects Fetched ;; --- Projects Fetched
(defrecord ProjectsFetched [projects] (defrecord ProjectsFetched [projects]
@ -59,12 +34,9 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-loaded [{projects :payload}] (letfn [(on-loaded [{projects :payload}]
#(reduce stpr/assoc-project % projects)) #(reduce stpr/assoc-project % projects))]
(on-error [err]
(rx/empty))]
(->> (rp/req :fetch/projects) (->> (rp/req :fetch/projects)
(rx/map on-loaded) (rx/map on-loaded)))))
(rx/catch on-error)))))
(defn fetch-projects (defn fetch-projects
[] []
@ -94,12 +66,8 @@
:layout layout :layout layout
:project (:id project) :project (:id project)
:name "Page 1" :name "Page 1"
:data nil}))) :data nil})))]
(on-failure [err]
(uum/error (tr "errors.create-project"))
(rx/empty))]
(->> (rp/req :create/project {:name name}) (->> (rp/req :create/project {:name name})
(rx/catch on-failure)
(rx/mapcat on-success))))) (rx/mapcat on-success)))))
(def ^:private create-project-schema (def ^:private create-project-schema
@ -119,12 +87,9 @@
rs/WatchEvent rs/WatchEvent
(-apply-watch [_ state s] (-apply-watch [_ state s]
(letfn [(on-success [_] (letfn [(on-success [_]
(rs/swap #(stpr/dissoc-project % id))) (rs/swap #(stpr/dissoc-project % id)))]
(on-failure [e]
(uum/error (tr "errors.delete-project")))]
(->> (rp/req :delete/project id) (->> (rp/req :delete/project id)
(rx/map on-success) (rx/map on-success)))))
(rx/catch on-failure)))))
(defn delete-project (defn delete-project
[id] [id]
@ -161,3 +126,23 @@
first page of the project." first page of the project."
([projectid] (GoTo. projectid)) ([projectid] (GoTo. projectid))
([projectid pageid] (GoToPage. projectid pageid))) ([projectid pageid] (GoToPage. projectid pageid)))
;; --- Helpers
(defn sort-projects-by
[ordering projs]
(case ordering
:name (sort-by :name projs)
:created (reverse (sort-by :created-at projs))
projs))
(defn contains-term?
[phrase term]
(str/contains? (str/lower phrase) (str/trim (str/lower term))))
(defn filter-projects-by
[term projs]
(if (str/blank? term)
projs
(filter #(contains-term? (:name %) term) projs)))

View file

@ -53,6 +53,7 @@
"errors.form.color" "Should be a valid color string" "errors.form.color" "Should be a valid color string"
"errors.form.password-not-match" "Password does not match" "errors.form.password-not-match" "Password does not match"
"errors.auth" "Username or passwords seems to be wrong." "errors.generic" "Something work has happened."
"errors.auth.unauthorized" "Username or passwords seems to be wrong."
"errors.profile.update-password" "Error updating password, probably your old password is wrong." "errors.profile.update-password" "Error updating password, probably your old password is wrong."
}) })

View file

@ -7,17 +7,21 @@
(ns uxbox.repo (ns uxbox.repo
"A main interface for access to remote resources." "A main interface for access to remote resources."
(:refer-clojure :exclude [do]) (:refer-clojure :exclude [do])
(:require [uxbox.repo.core :refer (request)] (:require [uxbox.repo.core :as core]
[uxbox.repo.auth] [uxbox.repo.auth]
[uxbox.repo.users] [uxbox.repo.users]
[uxbox.repo.projects] [uxbox.repo.projects]
[uxbox.repo.pages] [uxbox.repo.pages]
[httpurr.status :as status]
[beicon.core :as rx])) [beicon.core :as rx]))
(defn req (defn req
"Perform a side effectfull action accesing "Perform a side effectfull action accesing
remote resources." remote resources."
([type] ([type]
(request type nil)) (core/request type nil))
([type data] ([type data]
(request type data))) (core/request type data)))
(def client-error? status/client-error?)
(def server-error? status/server-error?)

View file

@ -28,25 +28,11 @@
(assoc response :body (t/decode body)) (assoc response :body (t/decode body))
response)) response))
(defrecord Ok [status payload])
(defrecord ServerError [status paylpad])
(defrecord ClientError [status payload])
(defrecord NotFound [payload])
(defn- handle-http-status (defn- handle-http-status
[{:keys [body status] :as response}] [{:keys [body status] :as response}]
(cond (if (http.status/success? response)
(http.status/success? response) (rx/of {:status status :payload body})
(rx/of (->Ok status body)) (rx/throw {:status status :payload body})))
(http.status/client-error? response)
(rx/throw
(if (= status 404)
(->NotFound body)
(->ClientError status body)))
(http.status/server-error? response)
(rx/throw (->ServerError status body))))
(def ^:private ^:const +headers+ (def ^:private ^:const +headers+
{"content-type" "application/transit+json"}) {"content-type" "application/transit+json"})

View file

@ -7,7 +7,9 @@
(ns uxbox.rstore (ns uxbox.rstore
"Reactive storage management architecture helpers." "Reactive storage management architecture helpers."
(:require [beicon.core :as rx])) (:require [beicon.core :as rx]
[uxbox.locales :refer (tr)]
[uxbox.ui.messages :as uum]))
;; An abstraction for implement a simple state ;; An abstraction for implement a simple state
;; transition. The `-apply-update` function receives ;; transition. The `-apply-update` function receives
@ -89,8 +91,9 @@
(defn- on-error (defn- on-error
"A default error handler." "A default error handler."
[e] [e]
(uum/error (tr "errors.generic"))
(println "Unexpected error: " e) (println "Unexpected error: " e)
(js/console.error e.stack) (js/console.log (.-stack e))
(rx/throw e)) (rx/throw e))
(defn init (defn init

View file

@ -1,9 +1,7 @@
(ns uxbox.ui.messages (ns uxbox.ui.messages
(:require [sablono.core :as html :refer-macros [html]] (:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum] [rum.core :as rum]
[promesa.core :as p]
[cuerdas.core :as str] [cuerdas.core :as str]
[uxbox.rstore :as rs]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.data :refer (classnames)] [uxbox.util.data :refer (classnames)]