♻️ Refactor services (for add the project-file concept.

And fix many tests.
This commit is contained in:
Andrey Antukh 2019-12-09 16:27:01 +01:00
parent af62d949d8
commit 183f0a5400
40 changed files with 1279 additions and 1006 deletions

View file

@ -1,92 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.pages
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
[uxbox.db :as db]
[uxbox.services.queries :as sq]
[uxbox.util.blob :as blob]
[uxbox.util.spec :as us]
[uxbox.util.sql :as sql]))
;; --- Helpers & Specs
(declare decode-row)
(s/def ::id ::us/uuid)
(s/def ::user ::us/uuid)
(s/def ::project-id ::us/uuid)
;; --- Query: Pages by Project
(s/def ::pages-by-project
(s/keys :req-un [::user ::project-id]))
(sq/defquery ::pages-by-project
[{:keys [user project-id] :as params}]
(let [sql "select pg.*,
pg.data,
pg.metadata
from pages as pg
where pg.user_id = $2
and pg.project_id = $1
and pg.deleted_at is null
order by pg.created_at asc;"]
(-> (db/query db/pool [sql project-id user])
(p/then #(mapv decode-row %)))))
;; --- Query: Page by Id
(s/def ::page
(s/keys :req-un [::user ::id]))
(sq/defquery ::page
[{:keys [user id] :as params}]
(let [sql "select pg.*,
pg.data,
pg.metadata
from pages as pg
where pg.user_id = $2
and pg.id = $1
and pg.deleted_at is null"]
(-> (db/query-one db/pool [sql id user])
(p/then' decode-row))))
;; --- Query: Page History
(s/def ::page-id ::us/uuid)
(s/def ::max ::us/integer)
(s/def ::pinned ::us/boolean)
(s/def ::since ::us/integer)
(s/def ::page-history
(s/keys :req-un [::page-id ::user]
:opt-un [::max ::pinned ::since]))
(sq/defquery ::page-history
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
(let [sql (-> (sql/from ["pages_history" "ph"])
(sql/select "ph.*")
(sql/where ["ph.user_id = ?" user]
["ph.page_id = ?" page-id]
["ph.version < ?" since]
(when pinned
["ph.pinned = ?" true]))
(sql/order "ph.version desc")
(sql/limit max))]
(-> (db/query db/pool (sql/fmt sql))
(p/then (partial mapv decode-row)))))
;; --- Helpers
(defn decode-row
[{:keys [data metadata] :as row}]
(when row
(cond-> row
data (assoc :data (blob/decode data))
metadata (assoc :metadata (blob/decode metadata)))))

View file

@ -0,0 +1,55 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.project-files
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
[uxbox.db :as db]
[uxbox.services.queries :as sq]
[uxbox.util.blob :as blob]
[uxbox.util.spec :as us]))
(declare decode-row)
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
(s/def ::name ::us/string)
(s/def ::project-id ::us/uuid)
(s/def ::user ::us/uuid)
;; --- Query: Project Files
(def ^:private sql:project-files
"select pf.*,
array_agg(pp.id) as pages
from project_files as pf
inner join projects as p on (pf.project_id = p.id)
inner join project_users as pu on (p.id = pu.project_id)
left join project_pages as pp on (pf.id = pp.file_id)
where pu.user_id = $1
and pu.project_id = $2
and pu.can_edit = true
group by pf.id
order by pf.created_at asc;")
(s/def ::project-files
(s/keys :req-un [::user ::project-id]))
(sq/defquery ::project-files
[{:keys [user project-id] :as params}]
(-> (db/query db/pool [sql:project-files user project-id])
(p/then' (partial mapv decode-row))))
;; --- Helpers
(defn decode-row
[{:keys [metadata pages] :as row}]
(when row
(cond-> row
pages (assoc :pages (vec (remove nil? pages)))
metadata (assoc :metadata (blob/decode metadata)))))

View file

@ -0,0 +1,145 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.project-pages
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
[uxbox.db :as db]
[uxbox.services.queries :as sq]
[uxbox.services.util :as su]
[uxbox.util.blob :as blob]
[uxbox.util.spec :as us]
[uxbox.util.sql :as sql]))
;; --- Helpers & Specs
(declare decode-row)
(s/def ::id ::us/uuid)
(s/def ::user ::us/uuid)
(s/def ::project-id ::us/uuid)
(s/def ::file-id ::us/uuid)
(def ^:private sql:generic-project-pages
"select pp.*
from project_pages as pp
inner join project_files as pf on (pf.id = pp.file_id)
inner join projects as p on (p.id = pf.project_id)
left join project_users as pu on (pu.project_id = p.id)
left join project_file_users as pfu on (pfu.file_id = pf.id)
where ((pfu.user_id = $1 and pfu.can_edit = true) or
(pu.user_id = $1 and pu.can_edit = true))
order by pp.created_at")
;; --- Query: Project Pages (By File ID)
(def ^:private sql:project-pages
(str "with pages as (" sql:generic-project-pages ")"
" select * from pages where file_id = $2"))
;; (defn project-pages-sql
;; [user]
;; (-> (sql/from ["project_pages" "pp"])
;; (sql/join ["project_files" "pf"] "pf.id = pp.file_id")
;; (sql/join ["projects" "p"] "p.id = pf.project_id")
;; (sql/ljoin ["project_users", "pu"] "pu.project_id = p.id")
;; (sql/ljoin ["project_file_users", "pfu"] "pfu.file_id = pf.id")
;; (sql/select "pp.*")
;; (sql/where ["((pfu.user_id = ? and pfu.can_edit = true) or
;; (pu.user_id = ? and pu.can_edit = true))" user user])
;; (sql/order "pp.created_at")))
;; (let [sql (-> (project-pages-sql user)
;; (sql/where ["pp.file_id = ?" file-id])
;; (sql/fmt))]
;; (-> (db/query db/pool sql)
;; (p/then #(mapv decode-row %)))))
(s/def ::project-pages
(s/keys :req-un [::user ::file-id]))
(sq/defquery ::project-pages
[{:keys [user file-id] :as params}]
(let [sql sql:project-pages]
(-> (db/query db/pool [sql user file-id])
(p/then #(mapv decode-row %)))))
;; --- Query: Project Page (By ID)
(def ^:private sql:project-page
(str "with pages as (" sql:generic-project-pages ")"
" select * from pages where id = $2"))
(defn retrieve-page
[conn {:keys [user id] :as params}]
(let [sql sql:project-page]
(-> (db/query-one conn [sql user id])
(p/then' su/raise-not-found-if-nil)
(p/then' decode-row))))
(s/def ::project-page
(s/keys :req-un [::user ::id]))
(sq/defquery ::project-page
[{:keys [user id] :as params}]
(retrieve-page db/pool params))
;; --- Query: Project Page History (by Page ID)
;; (def ^:private sql:generic-page-history
;; "select pph.*
;; from project_page_history as pph
;; where pph.page_id = $2
;; and pph.version < $3
;; order by pph.version < desc")
;; (def ^:private sql:page-history
;; (str "with history as (" sql:generic-page-history ")"
;; " select * from history limit $4"))
;; (def ^:private sql:pinned-page-history
;; (str "with history as (" sql:generic-page-history ")"
;; " select * from history where pinned = true limit $4"))
(s/def ::page-id ::us/uuid)
(s/def ::max ::us/integer)
(s/def ::pinned ::us/boolean)
(s/def ::since ::us/integer)
(s/def ::page-history
(s/keys :req-un [::page-id ::user]
:opt-un [::max ::pinned ::since]))
(defn retrieve-page-history
[{:keys [page-id user since max pinned] :or {since Long/MAX_VALUE max 10}}]
(let [sql (-> (sql/from ["pages_history" "ph"])
(sql/select "ph.*")
(sql/where ["ph.user_id = ?" user]
["ph.page_id = ?" page-id]
["ph.version < ?" since]
(when pinned
["ph.pinned = ?" true]))
(sql/order "ph.version desc")
(sql/limit max))]
(-> (db/query db/pool (sql/fmt sql))
(p/then (partial mapv decode-row)))))
(sq/defquery ::page-history
[{:keys [page-id user] :as params}]
(db/with-atomic [conn db/pool]
(p/do! (retrieve-page conn {:id page-id :user user})
(retrieve-page-history conn params))))
;; --- Helpers
(defn decode-row
[{:keys [data metadata] :as row}]
(when row
(cond-> row
data (assoc :data (blob/decode data))
metadata (assoc :metadata (blob/decode metadata)))))

View file

@ -13,6 +13,8 @@
[uxbox.util.blob :as blob]
[uxbox.util.spec :as us]))
(declare decode-row)
;; --- Helpers & Specs
(s/def ::id ::us/uuid)
@ -22,19 +24,26 @@
;; --- Query: Projects
;; (def ^:private projects-sql
;; "select distinct on (p.id, p.created_at)
;; p.*,
;; array_agg(pg.id) over (
;; partition by p.id
;; order by pg.created_at
;; range between unbounded preceding and unbounded following
;; ) as pages
;; from projects as p
;; left join pages as pg
;; on (pg.project_id = p.id)
;; where p.user_id = $1
;; order by p.created_at asc")
(def ^:private projects-sql
"select distinct on (p.id, p.created_at)
p.*,
array_agg(pg.id) over (
partition by p.id
order by pg.created_at
range between unbounded preceding and unbounded following
) as pages
from projects as p
left join pages as pg
on (pg.project_id = p.id)
where p.user_id = $1
order by p.created_at asc")
"select p.*
from project_users as pu
inner join projects as p on (p.id = pu.project_id)
where pu.can_edit = true
and pu.user_id = $1;")
(s/def ::projects
(s/keys :req-un [::user]))
@ -42,5 +51,12 @@
(sq/defquery ::projects
[{:keys [user] :as params}]
(-> (db/query db/pool [projects-sql user])
(p/then (fn [rows]
(mapv #(update % :pages vec) rows)))))
(p/then' (partial mapv decode-row))))
;; --- Helpers
(defn decode-row
[{:keys [metadata] :as row}]
(when row
(cond-> row
metadata (assoc :metadata (blob/decode metadata)))))

View file

@ -4,7 +4,7 @@
;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.user-storage
(ns uxbox.services.queries.user-attrs
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
@ -20,13 +20,13 @@
(cond-> row
val (assoc :val (blob/decode val)))))
(s/def ::user-storage-entry
(s/def ::user-attr
(s/keys :req-un [::key ::user]))
(sq/defquery ::user-storage-entry
(sq/defquery ::user-attr
[{:keys [key user]}]
(let [sql "select kv.*
from user_storage as kv
from user_attrs as kv
where kv.user_id = $2
and kv.key = $1"]
(-> (db/query-one db/pool [sql key user])

View file

@ -4,7 +4,7 @@
;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.services.queries.profiles
(ns uxbox.services.queries.users
(:require
[clojure.spec.alpha :as s]
[promesa.core :as p]
@ -51,9 +51,7 @@
(s/def ::profile
(s/keys :req-un [::user]))
(sq/defquery :profile
{:doc "Retrieve the user profile."
:spec ::profile}
(sq/defquery ::profile
[{:keys [user] :as params}]
(-> (get-profile db/pool user)
(p/then' strip-private-attrs)))