1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-06 18:33:38 +02:00

Make login-flow more robust and add more tests

This commit is contained in:
Arne Schlüter 2018-07-16 17:20:33 +02:00
commit 852a3193ab
9 changed files with 123 additions and 65 deletions

View file

@ -1,5 +1,4 @@
(ns airsonic-ui.db
(:require [airsonic-ui.routes :as routes]))
(ns airsonic-ui.db)
(def default-db
{:notifications (sorted-map)})

View file

@ -12,6 +12,11 @@
(fn [params]
(apply println params)))
(defn noop
"An event handler that can be used for clarity; doesn't do anything, but might
give a name to an event"
[cofx _] cofx)
;; ---
;; app boot flow
;; * restoring a previous session
@ -31,10 +36,9 @@
troubles with our router."
[{:keys [db store]} _]
(let [credentials (:credentials store)]
{:db (assoc db :credentials credentials)
:dispatch-n [(if credentials
{:dispatch-n [(if credentials
[:init-flow/credentials-found credentials]
[:init-flow/credentials-missing])]
[:init-flow/credentials-not-found])]
:routes/start-routing nil}))
(re-frame/reg-event-fx
@ -45,19 +49,21 @@
(defn credentials-found [_ [_ {:keys [u p server]}]]
{:dispatch [:credentials/verification-request u p server]})
(re-frame/reg-event-fx
:init-flow/credentials-found credentials-found)
(re-frame/reg-event-fx :init-flow/credentials-found credentials-found)
(re-frame/reg-event-fx
:init-flow/credentials-missing
;; we don't do anything special here, it's just for the sake of clarity
(fn [_ _] {}))
;; we don't do anything special here, it's just for the sake of clarity
(defn credentials-not-found
[cofx _]
(assoc-in cofx [:db :credentials] :credentials/not-found))
(re-frame/reg-event-fx :init-flow/credentials-not-found credentials-not-found)
;; ---
;; auth logic
;; ---
(defn-traced credentials-verification-request
(defn credentials-verification-request
"Tries to authenticate a user by pinging the server with credentials, saving
them when the request was successful. Bypasses the request when a user saved
their credentials."
@ -66,21 +72,25 @@
:uri (api/url server "ping" {:u user :p pass})
:response-format (ajax/json-response-format {:keywords? true})
:on-success [:credentials/verification-response user pass server]
:on-failure [:api/bad-response]}})
:on-failure [:credentials/verification-failure]}})
(re-frame/reg-event-fx
:credentials/verification-request credentials-verification-request)
(re-frame/reg-event-fx :credentials/verification-request credentials-verification-request)
(defn credentials-verification-response
"Since we don't get real status codes, we have to look into the server's
response and see whether we actually sent the correct credentials"
[fx [_ user pass server response]]
{:dispatch (if (api/is-error? response)
[:notification/show :error (api/error-msg (api/->exception response))]
[:credentials/verification-failure response]
[:credentials/verified user pass server])})
(re-frame/reg-event-fx
:credentials/verification-response credentials-verification-response)
(re-frame/reg-event-fx :credentials/verification-response credentials-verification-response)
(defn credentials-verification-failure [fx [_ response]]
(-> (assoc-in fx [:db :credentials] :credentials/verification-failure)
(assoc :dispatch [:notification/show :error (api/error-msg (api/->exception response))])))
(re-frame/reg-event-fx :credentials/verification-failure credentials-verification-failure)
(defn credentials-verified
"Gets called after the server indicates that the credentials entered by a user
@ -92,8 +102,7 @@
:db (assoc db :credentials credentials)
:dispatch [::logged-in]}))
(re-frame/reg-event-fx
:credentials/verified credentials-verified)
(re-frame/reg-event-fx :credentials/verified credentials-verified)
;; TODO: We have to find another solution for this once we have routes that
;; don't require a login but have the bottom controls
@ -103,10 +112,10 @@
(fn [_]
(.. js/document -documentElement -classList (add "has-navbar-fixed-bottom"))))
(defn logged-in
[cofx _]
(let [redirect (or (get-in cofx [:routes/from-query-param :redirect])
[::routes/main])]
(let [redirect (or (get-in cofx [:routes/from-query-param :redirect]) [::routes/main])]
{:routes/navigate redirect
:show-nav-bar nil}))
@ -117,17 +126,16 @@
(defn logout
"Clears all credentials and redirects the user to the login page"
[_ [_ & args]]
[cofx [_ & args]]
(let [args (apply hash-map args)]
{:routes/navigate (if-let [redirect (:redirect-to args)]
[::routes/login {} {:redirect (routes/encode-route redirect)}]
[::routes/login])
:routes/unset-credentials nil
:store nil
:db db/default-db}))
:db (merge (:db cofx) db/default-db {:credentials :credentials/logged-out})}))
(re-frame/reg-event-fx
::logout logout)
(re-frame/reg-event-fx ::logout logout)
;; ---
;; api interaction
@ -144,8 +152,7 @@
:on-success [:api/good-response]
:on-failure [:api/bad-response]}})
(re-frame/reg-event-fx
:api/request api-request)
(re-frame/reg-event-fx :api/request api-request)
(defn good-api-response [fx [_ response]]
(try
@ -153,15 +160,13 @@
(catch ExceptionInfo e
{:dispatch [:notification/show :error (api/error-msg e)]})))
(re-frame/reg-event-fx
:api/good-response good-api-response)
(re-frame/reg-event-fx :api/good-response good-api-response)
(defn bad-api-response [db event]
{:log ["API call gone bad; are CORS headers missing? check for :status 0" event]
:dispatch [:notification/show :error "Communication with server failed. Check browser logs for details."]})
(re-frame/reg-event-fx
:api/bad-response bad-api-response)
(re-frame/reg-event-fx :api/bad-response bad-api-response)
;; ---
;; musique
@ -256,12 +261,10 @@
:message message})
(assoc :dispatch-later (hide-later level))))))
(re-frame/reg-event-fx
:notification/show show-notification)
(re-frame/reg-event-fx :notification/show show-notification)
(defn hide-notification
[db [_ notification-id]]
(update db :notifications dissoc notification-id))
(re-frame/reg-event-db
:notification/hide hide-notification)
(re-frame/reg-event-db :notification/hide hide-notification)

View file

@ -100,7 +100,7 @@
;; this allows us to encode a complete route in a url fragment; useful for
;; doing redirects
(let [[_ _ query] (current-route)
from-param (decode-route (get query param))]
from-param (some-> (get query param) (decode-route))]
(assoc-in coeffects [:routes/from-query-param param] from-param))))
(defn start-routing!

View file

@ -2,6 +2,14 @@
(:require [re-frame.core :as re-frame :refer [subscribe]]
[airsonic-ui.utils.api :as api]))
(defn is-booting?
"Predicate to tell whether our app is still in the process of initialization"
[{:keys [credentials]} _]
(and (not (map? credentials))
(not (#{:credentials/not-found :credentials/verification-failure :credentials/logged-out} credentials))))
(re-frame/reg-sub ::is-booting? is-booting?)
;; can be used to query the user's credentials
(re-frame/reg-sub

View file

@ -68,10 +68,11 @@
[bottom-bar]])))
(defn main-panel []
(let [[route params query] @(subscribe [::subs/current-route])
notifications @(subscribe [::subs/notifications])]
(let [notifications @(subscribe [::subs/notifications])
is-booting? @(subscribe [::subs/is-booting?])
[route params query] @(subscribe [::subs/current-route])]
[:div
[notification-list notifications]
(if route
[app route params query]
[:div.app-loading>div.loader])]))
(if is-booting?
[:div.app-loading>div.loader]
[app route params query] )]))