diff --git a/src/clj/byf/api.clj b/src/clj/byf/api.clj index 2b98380..4ab4795 100644 --- a/src/clj/byf/api.clj +++ b/src/clj/byf/api.clj @@ -40,11 +40,16 @@ [{:keys [params]}] (notifications/notify-slack "A new game was added!") (let [validated (validate/conform-data :game params) - game-id (db/add-game! validated)] + last-game (-> (db/query db/last-game-sql) + first)] + (if (or + (:force validated) + (not (games/equal? validated last-game))) - (as-json - (resp/created "/api/games" - {:id game-id})))) + (as-json + (resp/created "/api/games" + {:id (db/add-game! (dissoc validated :force))})) + (resp/bad-request "Duplicate game")))) (defn add-player! "Adds a new user to the platform, authenticated with basic Auth" diff --git a/src/clj/byf/db.clj b/src/clj/byf/db.clj index c1c7f67..5152e34 100644 --- a/src/clj/byf/db.clj +++ b/src/clj/byf/db.clj @@ -70,6 +70,13 @@ (h/from :league) (h/where [:= :id league-id]))) +(defn last-game-sql + [] + (-> (h/select :*) + (h/from :game) + (h/order-by [:played_at :desc]) + (h/limit 1))) + (defn query [func & args] (jdbc/query (db-spec) diff --git a/src/cljc/byf/games.cljc b/src/cljc/byf/games.cljc index 561771e..d3d1b9f 100644 --- a/src/cljc/byf/games.cljc +++ b/src/cljc/byf/games.cljc @@ -155,3 +155,23 @@ (if (>= next prev) (recur rst (+ curr-increase (- next prev)) new-max) (recur rst 0 new-max)))))) + +(defn invert-game + [game] + (-> game + (assoc :p1 (:p2 game)) + (assoc :p2 (:p1 game)) + (assoc :p1_points (:p2_points game)) + (assoc :p2_points (:p1_points game)) + (assoc :p1_using (:p2_using game)) + (assoc :p2_using (:p1_using game)))) + +(defn equal? + [g1 g2] + (let [useful #(select-keys % [:p1 :p2 :p1_points :p2_points :p1_using :p2_using]) + g1_ (useful g1) + g2_ (useful g2) + g2_inv (invert-game g2_)] + + (or (= g1_ g2_) + (= g1_ g2_inv)))) diff --git a/src/cljs/byf/common/handlers.cljs b/src/cljs/byf/common/handlers.cljs index 9aee049..5d6bb96 100644 --- a/src/cljs/byf/common/handlers.cljs +++ b/src/cljs/byf/common/handlers.cljs @@ -58,18 +58,19 @@ :on-failure [:failed]}})) (defn writer - [page uri on-success transform-params-fn] - (fn [{:keys [db]} _] - {:db db - :http-xhrio {:method :post - :uri uri - :params (merge (transform-params-fn db) - {:league_id (get-league-id db)}) - - :format (ajax/json-request-format) - :response-format (ajax/json-response-format {:keywords? true}) - :on-success [on-success] - :on-failure [:failed]}})) + ([page uri on-success transform-params-fn] + (writer page uri on-success transform-params-fn :failed)) + ([page uri on-success transform-params-fn on-failure] + (fn [{:keys [db]} _] + {:http-xhrio {:method :post + :uri uri + :params (merge (transform-params-fn db) + {:league_id (get-league-id db)}) + + :format (ajax/json-request-format) + :response-format (ajax/json-response-format {:keywords? true}) + :on-success [on-success] + :on-failure [on-failure]}}))) (defn failed [page] diff --git a/src/cljs/byf/league_detail/handlers.cljs b/src/cljs/byf/league_detail/handlers.cljs index 132cc9f..b8043a5 100644 --- a/src/cljs/byf/league_detail/handlers.cljs +++ b/src/cljs/byf/league_detail/handlers.cljs @@ -45,7 +45,7 @@ :league_id nil :show-all? false :game-config shared/default-game-config - :show-notification false + :notification nil :loading? false :show-graph false}) @@ -63,14 +63,18 @@ [name-mapping vals] (medley/map-keys #(get name-mapping %) vals)) -(rf/reg-sub ::show-notification (getter [:show-notification])) -(rf/reg-event-db ::show-notification - (fn [db _] - (common/assoc-in* db page [:show-notification] true))) +(rf/reg-sub ::notification (getter [:notification])) +(rf/reg-event-db ::notification + (fn [db [_ type msg]] + (common/assoc-in* db + page + [:notification] + {:type type + :msg msg}))) (rf/reg-event-db ::clear-notification (fn [db _] - (common/assoc-in* db page [:show-notification] false))) + (common/assoc-in* db page [:notification] false))) (rf/reg-sub ::show-graph (getter [:show-graph])) (rf/reg-sub ::loading? (getter [:loading?])) @@ -258,14 +262,20 @@ (rf/reg-event-db ::played_at (setter [:game :played_at])) (defn- reload-fn-gen - [extra-signal] - (fn [{:keys [db]} _] - {:db db - :dispatch-n (cons extra-signal [[::show-notification] - [::players-handlers/load-players] - [::load-games]])})) + [{:keys [db] :as full} other] + (js/console.log "Other = " other ", full = " full) + {:db db + :dispatch-n [[::notification :success "Game added successfully"] + [::reset-game] + [::players-handlers/load-players] + [::load-games]]}) + +(rf/reg-event-fx ::add-game-success reload-fn-gen) -(rf/reg-event-fx ::add-game-success (reload-fn-gen [::reset-game])) +(rf/reg-event-fx ::add-game-failed + (fn [{:keys [db]}] + (js/console.log "inside game failed") + {:dispatch [::notification :failure "Duplicate game"]})) (rf/reg-event-db ::failed (common/failed page)) @@ -289,7 +299,9 @@ (rf/reg-event-fx ::add-game (common/writer page "/api/add-game" - ::add-game-success game-transform)) + ::add-game-success + game-transform + ::add-game-failed)) (rf/reg-sub ::hidden? (sets/in? page :hidden-players)) diff --git a/src/cljs/byf/league_detail/views.cljs b/src/cljs/byf/league_detail/views.cljs index 0b3ca59..4b15966 100644 --- a/src/cljs/byf/league_detail/views.cljs +++ b/src/cljs/byf/league_detail/views.cljs @@ -428,13 +428,18 @@ (defn notifications [] - (let [show-notification (rf/subscribe [::handlers/show-notification])] + (let [notification (rf/subscribe [::handlers/notification])] (fn [] - (when @show-notification - [:div.notification.is-success + (when @notification + [:div.notification + {:class (case (:type @notification) + :success "is-success" + :failure "is-failure")} + + [:button.delete {:on-click #(rf/dispatch [::handlers/clear-notification])}] - "Thank you, your game has been recorded"])))) + (:msg @notification)])))) (defn root [] diff --git a/test/clj/byf/api_test.clj b/test/clj/byf/api_test.clj index 3ebc888..f1d5d04 100644 --- a/test/clj/byf/api_test.clj +++ b/test/clj/byf/api_test.clj @@ -6,6 +6,7 @@ [clojure.test :refer [deftest is testing use-fixtures join-fixtures]] [byf.api :as sut] [byf.db :as db] + [byf.games :refer [invert-game]] [environ.core :refer [env]] [ring.mock.request :as mock]) (:import (java.util UUID))) @@ -58,7 +59,7 @@ authenticated-req sut/app)] - (assert (contains? #{201 401} status), "Invalid status code") + (assert (contains? #{201 401 400} status), "Invalid status code") response)) (defn- store-users! @@ -70,6 +71,28 @@ [p1-id p2-id])) +(deftest store-results-invariants-test + (testing "Can't add two equal games" + (let [[p1-id p2-id] (store-users!) + game1 {:p1 (:player-id p1-id) + :p2 (:player-id p2-id) + :league_id sample-league-id + :p1_using "RM" + :p2_using "Juv" + :p1_points 3 + :p2_points 0 + :played_at "2018-08-29+01:0021:50:32"} + game2 (invert-game game1) + game3 (assoc game2 :force true)] + + (let [first-call (write-api-call "/add-game" game1) + second-call (write-api-call "/add-game" game2) + third-call (write-api-call "/add-game" game3)] + + (is (= 201 (:status first-call))) + (is (= 400 (:status second-call))) + (is (= 201 (:status third-call))))))) + (deftest store-results-test (testing "Should be able to store results" (let [[p1-id p2-id] (store-users!) @@ -80,28 +103,26 @@ :p2_using "Juv" :p1_points 3 :p2_points 0 - :played_at "2018-08-29+01:0021:50:32"} - - _ (write-api-call "/add-game" sample) - games (read-api-call "/api/games" {:league_id sample-league-id}) - - desired {"p1" (str (:player-id p1-id)) - "p1_points" 3, - "p1_using" "RM", - "p2" (str (:player-id p2-id)), - "p2_points" 0, - "p2_using" "Juv" - "played_at" "2018-08-29T20:50:00Z"}] - - (is (= 200 (:status games))) - - (is (= desired - (select-keys - (first (json/read-str (:body games))) - ["p1_points" "p2_points" - "p1" "p2" - "p1_using" "p2_using" - "played_at"])))))) + :played_at "2018-08-29+01:0021:50:32"}] + (write-api-call "/add-game" sample) + (let [games (read-api-call "/api/games" {:league_id sample-league-id}) + desired {"p1" (str (:player-id p1-id)) + "p1_points" 3, + "p1_using" "RM", + "p2" (str (:player-id p2-id)), + "p2_points" 0, + "p2_using" "Juv" + "played_at" "2018-08-29T20:50:00Z"}] + + (is (= 200 (:status games))) + + (is (= desired + (select-keys + (first (json/read-str (:body games))) + ["p1_points" "p2_points" + "p1" "p2" + "p1_using" "p2_using" + "played_at"]))))))) (deftest get-players-test (testing "Fetching all the existing players"