From ae5a0ac0ae3fcf6d98b5847ccc702ed4fb5e3f56 Mon Sep 17 00:00:00 2001 From: flynn Date: Tue, 15 Jan 2019 05:07:35 +0100 Subject: [PATCH] add cuddle requests with colored tables and accept/decline system --- README.md | 11 +++ resources/html/home.html | 93 +++++++++++++------ resources/html/parts/colored-status.html | 12 +++ ...114224209-add-request-status-type.down.sql | 1 + ...90114224209-add-request-status-type.up.sql | 2 + ...224926-add-relation-request-table.down.sql | 1 + ...14224926-add-relation-request-table.up.sql | 7 ++ resources/sql/queries.sql | 36 ++++++- src/clj/cat/db/core.clj | 40 +++++++- src/clj/cat/layout.clj | 9 +- src/clj/cat/routes/home.clj | 65 +++++++++---- 11 files changed, 230 insertions(+), 47 deletions(-) create mode 100644 resources/html/parts/colored-status.html create mode 100644 resources/migrations/20190114224209-add-request-status-type.down.sql create mode 100644 resources/migrations/20190114224209-add-request-status-type.up.sql create mode 100644 resources/migrations/20190114224926-add-relation-request-table.down.sql create mode 100644 resources/migrations/20190114224926-add-relation-request-table.up.sql diff --git a/README.md b/README.md index d8d85c2..8e33463 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,17 @@ To start the ui live rendering, run: lein figwheel +## Development +### Database +* ENUM TYPE + + Because of the lack of typing in clojure and the forced typing of the jdbc driver +we need to manually manage conversion of enum types to clojure keywords. + + When adding an enum to the database, make sure to add it to the '+schema-enums+' set [src/clj/cat/db/core.clj] + + + ## License Copyright © 2019 FIXME \ No newline at end of file diff --git a/resources/html/home.html b/resources/html/home.html index b793675..d7b2990 100644 --- a/resources/html/home.html +++ b/resources/html/home.html @@ -101,8 +101,8 @@ {% if user %}
-
-

Your cuddles

+
+

Your cuddles

@@ -121,11 +121,9 @@
-

Request cuddles!

{% csrf-field %} -
- +
-
-
- +
- - - - - - - {% for rel_req in user-relation-requests %} - - - - {% endfor %} -
Outgoing requests
{{rel_req.name}}
+
+
+ + + + + + + + {% for rr in rel-requests-out %} + + + + + {% endfor %} +
Outgoing requestsStatus
{{rr.to_name}} + + {% include "parts/colored-status.html" %} +
+
+
+ + + + + + + + {% for rr in rel-requests-in %} + + + + {% ifequal rr.status "open" %} + + {% endifequal %} + + {% endfor %} +
Incoming requestsStatus
+ {{rr.from_name}} + + {% include "parts/colored-status.html" %} + +
+ {% csrf-field %} +
+
+ +
+
+ +
+
+
+
+
+
@@ -182,7 +226,7 @@
- +
@@ -222,8 +266,7 @@ -
- +
-
-
- +
diff --git a/resources/html/parts/colored-status.html b/resources/html/parts/colored-status.html new file mode 100644 index 0000000..c71ddba --- /dev/null +++ b/resources/html/parts/colored-status.html @@ -0,0 +1,12 @@ + + {{rr.status}} + \ No newline at end of file diff --git a/resources/migrations/20190114224209-add-request-status-type.down.sql b/resources/migrations/20190114224209-add-request-status-type.down.sql new file mode 100644 index 0000000..9cb9323 --- /dev/null +++ b/resources/migrations/20190114224209-add-request-status-type.down.sql @@ -0,0 +1 @@ +DROP TYPE STATUS; \ No newline at end of file diff --git a/resources/migrations/20190114224209-add-request-status-type.up.sql b/resources/migrations/20190114224209-add-request-status-type.up.sql new file mode 100644 index 0000000..2eb45b0 --- /dev/null +++ b/resources/migrations/20190114224209-add-request-status-type.up.sql @@ -0,0 +1,2 @@ +CREATE TYPE STATUS as ENUM +('open', 'accepted', 'declined'); \ No newline at end of file diff --git a/resources/migrations/20190114224926-add-relation-request-table.down.sql b/resources/migrations/20190114224926-add-relation-request-table.down.sql new file mode 100644 index 0000000..633d6fd --- /dev/null +++ b/resources/migrations/20190114224926-add-relation-request-table.down.sql @@ -0,0 +1 @@ +DROP TABLE relation_requests; \ No newline at end of file diff --git a/resources/migrations/20190114224926-add-relation-request-table.up.sql b/resources/migrations/20190114224926-add-relation-request-table.up.sql new file mode 100644 index 0000000..deb1ce1 --- /dev/null +++ b/resources/migrations/20190114224926-add-relation-request-table.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE relation_requests +(id SERIAL PRIMARY KEY, + from_id INTEGER NOT NULL, + to_id INTEGER NOT NULL, + status STATUS NOT NULL, + FOREIGN KEY (from_id) REFERENCES users (id), + FOREIGN KEY (to_id) REFERENCES users (id)); \ No newline at end of file diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index b9a04b2..4011d75 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -36,4 +36,38 @@ VALUES (:from_id, :to_id) -- :doc retrieves all relations SELECT * FROM relations JOIN users u_from on relations.from_id = u_from.id -JOIN users u_to on relations.to_id = u_to.id \ No newline at end of file +JOIN users u_to on relations.to_id = u_to.id + +/* + ------------------------------- + RELATION REQUESTS + */ + +-- :name create-relation-request! :! +-- :doc adds a request for a relation from a user to another user +INSERT INTO relation_requests +(from_id, to_id, status) +VALUES (:from_id, :to_id, :status) + +-- :name update-relation-request-status! :! :n +-- :doc updates an existing relation record +UPDATE relation_requests +SET status = :status +WHERE id = :id + +-- :name get-relation-request :? :1 +-- :doc retrieves one relation request on id +SELECT * FROM relation_requests +WHERE id = :id + +-- :name get-relation-requests-from-user :? :* +-- :doc retrieves all relations requests that a user made +SELECT rr.id as rr_id, rr.status, u_to.name as to_name FROM relation_requests as rr +JOIN users u_to on rr.to_id = u_to.id +WHERE from_id = :from_id + +-- :name get-relation-requests-to-user :? :* +-- :doc retrieves all relations requests send to a user +SELECT rr.id as rr_id, rr.status, u_from.name as from_name FROM relation_requests as rr +JOIN users u_from on rr.from_id = u_from.id +WHERE to_id = :to_id \ No newline at end of file diff --git a/src/clj/cat/db/core.clj b/src/clj/cat/db/core.clj index 404b9c3..5a12dea 100644 --- a/src/clj/cat/db/core.clj +++ b/src/clj/cat/db/core.clj @@ -6,7 +6,8 @@ [conman.core :as conman] [java-time :as jt] [cat.config :refer [env]] - [mount.core :refer [defstate]]) + [mount.core :refer [defstate]] + [clojure.string :as s]) (:import org.postgresql.util.PGobject java.sql.Array clojure.lang.IPersistentMap @@ -83,3 +84,40 @@ IPersistentVector (sql-value [value] (to-pg-json value))) + +; postgres enum type <--> clojure namespaces keywords + +; https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png +; Handle Inserting of keywords as enums into the db +; Usage: (insert! pg-db :files {:name "my-file.txt", :status :processing-status/pending}) +(defn kw->pgenum + "Convert clj keyword to equivalent PGobject" + [kw] + (let [type (-> (namespace kw) + (s/replace "-" "_")) + value (name kw)] + (doto (PGobject.) + (.setType type) + (.setValue value)))) + +(extend-type clojure.lang.Keyword + jdbc/ISQLValue + (sql-value [kw] + (kw->pgenum kw))) + +; Handle extracting keywords from the db enum type +; Usage: +; (query (:db user/system) ["SELECT * FROM files"]) +; => ({:status :processing-status/pending, :name "my-file.txt"}) +(def +schema-enums+ + "A set of all PostgreSQL enums in schema.sql. Used to convert + enum-values back into Clojure keywords." + #{"status"}) + +(extend-type String + jdbc/IResultSetReadColumn + (result-set-read-column [val rsmeta idx] + (let [type (.getColumnTypeName rsmeta idx)] + (if (contains? +schema-enums+ type) + (keyword (s/replace type "_" "-") val) + val)))) \ No newline at end of file diff --git a/src/clj/cat/layout.clj b/src/clj/cat/layout.clj index a8200f9..72f004f 100644 --- a/src/clj/cat/layout.clj +++ b/src/clj/cat/layout.clj @@ -4,13 +4,18 @@ [markdown.core :refer [md-to-html-string]] [ring.util.http-response :refer [content-type ok]] [ring.util.anti-forgery :refer [anti-forgery-field]] - [ring.middleware.anti-forgery :refer [*anti-forgery-token*]])) + [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] + [clojure.string :as s])) -(parser/set-resource-path! (clojure.java.io/resource "html")) +(parser/set-resource-path! (clojure.java.io/resource "html")) (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) +(filters/add-filter! :print (fn [x] + (println "Selmer var: " x) + x)) + (defn render "renders the HTML template located relative to resources/html" [template & [params]] diff --git a/src/clj/cat/routes/home.clj b/src/clj/cat/routes/home.clj index 34440b6..87bc699 100644 --- a/src/clj/cat/routes/home.clj +++ b/src/clj/cat/routes/home.clj @@ -4,7 +4,9 @@ [compojure.core :refer [defroutes GET POST]] [ring.util.http-response :as response] [struct.core :as st] - [clojure.tools.logging :as log])) + [clojure.tools.logging :as log] + [cat.layout :refer [error-page]] + [clojure.string :as s])) (def user-schema [[:name st/required st/string] @@ -28,6 +30,15 @@ (defn get-users [] (db/get-users)) +(defn response-wrong-parameters [] + (error-page {:status 400 + :title "Wrong request parameters" + :message "Please contact your system administrator to fix this issue"})) + +(defn map-status-to-value + [relation-requests] + (map (fn [rr] (cond-> rr (:status rr) (assoc :status (name (:status rr))))) relation-requests)) + (defroutes home-routes (GET "/" req (let [users (get-users) @@ -36,20 +47,25 @@ user-relations (when user (seq (filter (fn [rel] (or - (= (:name rel) (:username user)) - (= (:name_2 rel) (:username user)))) + (= (:name rel) (:name user)) + (= (:name_2 rel) (:name user)))) relations))) other_users (when user (seq (filter (fn [usr] (not (= (:id usr) (:id user)))) - users)))] + users))) + rel-requests-out (seq (map-status-to-value (db/get-relation-requests-from-user {:from_id (:id user)}))) + rel-requests-in (seq (map-status-to-value (db/get-relation-requests-to-user {:to_id (:id user)})))] (log/info (str "Session: " (:session req))) + (log/info (str "Relation requests: \n OUTGOING: " rel-requests-out "\n INCOMING: " rel-requests-in)) ;(log/info (str "User relations: " user-relations)) ;(log/info (str "Other Users: " other_users)) - (home-page {:relations relations - :users users - :user user - :user-relations user-relations - :other_users other_users}))) + (home-page {:relations relations + :users users + :user user + :user-relations user-relations + :other_users other_users + :rel-requests-out rel-requests-out + :rel-requests-in rel-requests-in}))) ;(GET "/docs" [] ; (-> (response/ok (-> "docs/docs.md" io/resource slurp)) ; (response/header "Content-Type" "text/plain; charset=utf-8"))) @@ -79,26 +95,41 @@ (assoc :group (rand-int 5))))))] (response/ok {:nodes nodes-indexed :links rels-indexed}))) + + ; TODO make next 2 user protected + (POST "/relation_request/:id/status" [id & body] + (let [rr_id_map {:id id} + success (cond + (contains? body :accept) (do + (let [rr (db/get-relation-request rr_id_map)] + (db/create-relation! {:from_id (:from_id rr) :to_id (:to_id rr)})) + (db/update-relation-request-status! (assoc rr_id_map :status :status/accepted))) + (contains? body :decline) (db/update-relation-request-status! (assoc rr_id_map :status :status/declined)) + :else false)] + (if success + (response/found "/") + (response-wrong-parameters)))) + ; STATUS ENUM: (open, accepted, rejected) (POST "/request_relation" req (let [data (:params req) [err result] (st/validate data request_relation-schema)] (log/info "Post to " (:uri req) "\n with data " result) (if (nil? err) (do - () - (response/no-content) - ;TODO add a request to the db - ) + (db/create-relation-request! {:from_id (get-in req [:session :user :id]) + :to_id (:to_id result) + :status :status/open}) + (response/found "/")) (do (response/bad-request "Incorrect input"))))) - ; TODO make bottom 2 protected + ; TODO make bottom 2 admin protected (POST "/relations" req (let [data (:params req) [err result] (st/validate data relation-schema)] (log/info "Post to " (:uri req)) (if (nil? err) (do (db/create-relation! result) - (response/no-content)) + (response/found "/")) (do (response/bad-request "Incorrect input"))))) (POST "/users" req @@ -107,8 +138,8 @@ (println data) (if (st/valid? data user-schema) (do - (db/create-user! data) - (response/no-content)) + (db/create-user! (assoc data :zeusid nil)) + (response/found "/")) (do (response/bad-request "Incorrect input"))))))