mirror of
https://github.com/penpot/penpot.git
synced 2025-01-26 00:19:07 -05:00
♻️ Move comments mutations to commands
This commit is contained in:
parent
0b3d25a890
commit
aceefc0485
5 changed files with 353 additions and 230 deletions
|
@ -6,14 +6,23 @@
|
||||||
|
|
||||||
(ns app.rpc.commands.comments
|
(ns app.rpc.commands.comments
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.exceptions :as ex]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
[app.rpc.queries.files :as files]
|
[app.rpc.queries.files :as files]
|
||||||
[app.rpc.queries.teams :as teams]
|
[app.rpc.queries.teams :as teams]
|
||||||
|
[app.rpc.retry :as retry]
|
||||||
|
[app.util.blob :as blob]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
|
[app.util.time :as dt]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; QUERY COMMANDS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn decode-row
|
(defn decode-row
|
||||||
[{:keys [participants position] :as row}]
|
[{:keys [participants position] :as row}]
|
||||||
(cond-> row
|
(cond-> row
|
||||||
|
@ -131,7 +140,7 @@
|
||||||
(decode-row)))))
|
(decode-row)))))
|
||||||
|
|
||||||
(defn get-comment-thread
|
(defn get-comment-thread
|
||||||
[conn {:keys [profile-id file-id id share-id] :as params}]
|
[conn {:keys [profile-id file-id id] :as params}]
|
||||||
(let [sql (str "with threads as (" sql:comment-threads ")"
|
(let [sql (str "with threads as (" sql:comment-threads ")"
|
||||||
"select * from threads where id = ?")]
|
"select * from threads where id = ?")]
|
||||||
(-> (db/exec-one! conn [sql profile-id file-id id])
|
(-> (db/exec-one! conn [sql profile-id file-id id])
|
||||||
|
@ -209,3 +218,317 @@
|
||||||
(defn get-file-comments-users
|
(defn get-file-comments-users
|
||||||
[conn file-id profile-id]
|
[conn file-id profile-id]
|
||||||
(db/exec! conn [sql:file-comment-users file-id profile-id]))
|
(db/exec! conn [sql:file-comment-users file-id profile-id]))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; MUTATION COMMANDS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; --- COMMAND: Create Comment Thread
|
||||||
|
|
||||||
|
(declare upsert-comment-thread-status!)
|
||||||
|
(declare create-comment-thread)
|
||||||
|
(declare retrieve-page-name)
|
||||||
|
|
||||||
|
(s/def ::page-id ::us/uuid)
|
||||||
|
(s/def ::file-id ::us/uuid)
|
||||||
|
(s/def ::share-id (s/nilable ::us/uuid))
|
||||||
|
(s/def ::profile-id ::us/uuid)
|
||||||
|
(s/def ::position ::gpt/point)
|
||||||
|
(s/def ::content ::us/string)
|
||||||
|
(s/def ::frame-id ::us/uuid)
|
||||||
|
|
||||||
|
(s/def ::create-comment-thread
|
||||||
|
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id ::frame-id]
|
||||||
|
:opt-un [::share-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::create-comment-thread
|
||||||
|
{::retry/max-retries 3
|
||||||
|
::retry/matches retry/conflict-db-insert?
|
||||||
|
::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||||
|
(create-comment-thread conn params)))
|
||||||
|
|
||||||
|
(defn- retrieve-next-seqn
|
||||||
|
[conn file-id]
|
||||||
|
(let [sql "select (f.comment_thread_seqn + 1) as next_seqn from file as f where f.id = ?"
|
||||||
|
res (db/exec-one! conn [sql file-id])]
|
||||||
|
(:next-seqn res)))
|
||||||
|
|
||||||
|
(defn create-comment-thread
|
||||||
|
[conn {:keys [profile-id file-id page-id position content frame-id] :as params}]
|
||||||
|
(let [seqn (retrieve-next-seqn conn file-id)
|
||||||
|
now (dt/now)
|
||||||
|
pname (retrieve-page-name conn params)
|
||||||
|
thread (db/insert! conn :comment-thread
|
||||||
|
{:file-id file-id
|
||||||
|
:owner-id profile-id
|
||||||
|
:participants (db/tjson #{profile-id})
|
||||||
|
:page-name pname
|
||||||
|
:page-id page-id
|
||||||
|
:created-at now
|
||||||
|
:modified-at now
|
||||||
|
:seqn seqn
|
||||||
|
:position (db/pgpoint position)
|
||||||
|
:frame-id frame-id})]
|
||||||
|
|
||||||
|
|
||||||
|
;; Create a comment entry
|
||||||
|
(db/insert! conn :comment
|
||||||
|
{:thread-id (:id thread)
|
||||||
|
:owner-id profile-id
|
||||||
|
:created-at now
|
||||||
|
:modified-at now
|
||||||
|
:content content})
|
||||||
|
|
||||||
|
;; Make the current thread as read.
|
||||||
|
(upsert-comment-thread-status! conn profile-id (:id thread))
|
||||||
|
|
||||||
|
;; Optimistic update of current seq number on file.
|
||||||
|
(db/update! conn :file
|
||||||
|
{:comment-thread-seqn seqn}
|
||||||
|
{:id file-id})
|
||||||
|
|
||||||
|
(select-keys thread [:id :file-id :page-id])))
|
||||||
|
|
||||||
|
(defn- retrieve-page-name
|
||||||
|
[conn {:keys [file-id page-id]}]
|
||||||
|
(let [{:keys [data]} (db/get-by-id conn :file file-id)
|
||||||
|
data (blob/decode data)]
|
||||||
|
(get-in data [:pages-index page-id :name])))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- COMMAND: Update Comment Thread Status
|
||||||
|
|
||||||
|
(s/def ::id ::us/uuid)
|
||||||
|
(s/def ::share-id (s/nilable ::us/uuid))
|
||||||
|
|
||||||
|
(s/def ::update-comment-thread-status
|
||||||
|
(s/keys :req-un [::profile-id ::id]
|
||||||
|
:opt-un [::share-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::update-comment-thread-status
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [cthr (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
|
(when-not cthr
|
||||||
|
(ex/raise :type :not-found))
|
||||||
|
|
||||||
|
(files/check-comment-permissions! conn profile-id (:file-id cthr) share-id)
|
||||||
|
(upsert-comment-thread-status! conn profile-id (:id cthr)))))
|
||||||
|
|
||||||
|
(def sql:upsert-comment-thread-status
|
||||||
|
"insert into comment_thread_status (thread_id, profile_id)
|
||||||
|
values (?, ?)
|
||||||
|
on conflict (thread_id, profile_id)
|
||||||
|
do update set modified_at = clock_timestamp()
|
||||||
|
returning modified_at;")
|
||||||
|
|
||||||
|
(defn upsert-comment-thread-status!
|
||||||
|
[conn profile-id thread-id]
|
||||||
|
(db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id]))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- COMMAND: Update Comment Thread
|
||||||
|
|
||||||
|
(s/def ::is-resolved ::us/boolean)
|
||||||
|
(s/def ::update-comment-thread
|
||||||
|
(s/keys :req-un [::profile-id ::id ::is-resolved]
|
||||||
|
:opt-un [::share-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::update-comment-thread
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id is-resolved share-id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
|
(when-not thread
|
||||||
|
(ex/raise :type :not-found))
|
||||||
|
|
||||||
|
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||||
|
|
||||||
|
(db/update! conn :comment-thread
|
||||||
|
{:is-resolved is-resolved}
|
||||||
|
{:id id})
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- COMMAND: Add Comment
|
||||||
|
|
||||||
|
(declare create-comment)
|
||||||
|
|
||||||
|
(s/def ::create-comment
|
||||||
|
(s/keys :req-un [::profile-id ::thread-id ::content]
|
||||||
|
:opt-un [::share-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::create-comment
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} params]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(create-comment conn params)))
|
||||||
|
|
||||||
|
(defn create-comment
|
||||||
|
[conn {:keys [profile-id thread-id content share-id] :as params}]
|
||||||
|
(let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true})
|
||||||
|
(decode-row))
|
||||||
|
pname (retrieve-page-name conn thread)]
|
||||||
|
|
||||||
|
;; Standard Checks
|
||||||
|
(when-not thread (ex/raise :type :not-found))
|
||||||
|
|
||||||
|
;; Permission Checks
|
||||||
|
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||||
|
|
||||||
|
;; Update the page-name cachedattribute on comment thread table.
|
||||||
|
(when (not= pname (:page-name thread))
|
||||||
|
(db/update! conn :comment-thread
|
||||||
|
{:page-name pname}
|
||||||
|
{:id thread-id}))
|
||||||
|
|
||||||
|
;; NOTE: is important that all timestamptz related fields are
|
||||||
|
;; created or updated on the database level for avoid clock
|
||||||
|
;; inconsistencies (some user sees something read that is not
|
||||||
|
;; read, etc...)
|
||||||
|
(let [ppants (:participants thread #{})
|
||||||
|
comment (db/insert! conn :comment
|
||||||
|
{:thread-id thread-id
|
||||||
|
:owner-id profile-id
|
||||||
|
:content content})]
|
||||||
|
|
||||||
|
;; NOTE: this is done in SQL instead of using db/update!
|
||||||
|
;; helper because currently the helper does not allow pass raw
|
||||||
|
;; function call parameters to the underlying prepared
|
||||||
|
;; statement; in a future when we fix/improve it, this can be
|
||||||
|
;; changed to use the helper.
|
||||||
|
|
||||||
|
;; Update thread modified-at attribute and assoc the current
|
||||||
|
;; profile to the participant set.
|
||||||
|
(let [ppants (conj ppants profile-id)
|
||||||
|
sql "update comment_thread
|
||||||
|
set modified_at = clock_timestamp(),
|
||||||
|
participants = ?
|
||||||
|
where id = ?"]
|
||||||
|
(db/exec-one! conn [sql (db/tjson ppants) thread-id]))
|
||||||
|
|
||||||
|
;; Update the current profile status in relation to the
|
||||||
|
;; current thread.
|
||||||
|
(upsert-comment-thread-status! conn profile-id thread-id)
|
||||||
|
|
||||||
|
;; Return the created comment object.
|
||||||
|
comment)))
|
||||||
|
|
||||||
|
;; --- COMMAND: Update Comment
|
||||||
|
|
||||||
|
(declare update-comment)
|
||||||
|
|
||||||
|
(s/def ::update-comment
|
||||||
|
(s/keys :req-un [::profile-id ::id ::content]
|
||||||
|
:opt-un [::share-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::update-comment
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} params]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(update-comment conn params)))
|
||||||
|
|
||||||
|
(defn update-comment
|
||||||
|
[conn {:keys [profile-id id content share-id] :as params}]
|
||||||
|
(let [comment (db/get-by-id conn :comment id {:for-update true})
|
||||||
|
_ (when-not comment (ex/raise :type :not-found))
|
||||||
|
thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true})
|
||||||
|
_ (when-not thread (ex/raise :type :not-found))
|
||||||
|
pname (retrieve-page-name conn thread)]
|
||||||
|
|
||||||
|
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||||
|
|
||||||
|
;; Don't allow edit comments to not owners
|
||||||
|
(when-not (= (:owner-id thread) profile-id)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-allowed))
|
||||||
|
|
||||||
|
(db/update! conn :comment
|
||||||
|
{:content content
|
||||||
|
:modified-at (dt/now)}
|
||||||
|
{:id (:id comment)})
|
||||||
|
|
||||||
|
(db/update! conn :comment-thread
|
||||||
|
{:modified-at (dt/now)
|
||||||
|
:page-name pname}
|
||||||
|
{:id (:id thread)})
|
||||||
|
nil))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- COMMAND: Delete Comment Thread
|
||||||
|
|
||||||
|
(s/def ::delete-comment-thread
|
||||||
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::delete-comment-thread
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
|
(when-not (= (:owner-id thread) profile-id)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-allowed))
|
||||||
|
(db/delete! conn :comment-thread {:id id})
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- COMMAND: Delete comment
|
||||||
|
|
||||||
|
(s/def ::delete-comment
|
||||||
|
(s/keys :req-un [::profile-id ::id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::delete-comment
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [comment (db/get-by-id conn :comment id {:for-update true})]
|
||||||
|
(when-not (= (:owner-id comment) profile-id)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-allowed))
|
||||||
|
|
||||||
|
(db/delete! conn :comment {:id id}))))
|
||||||
|
|
||||||
|
;; --- COMMAND: Update comment thread position
|
||||||
|
|
||||||
|
(s/def ::update-comment-thread-position
|
||||||
|
(s/keys :req-un [::profile-id ::id ::position ::frame-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::update-comment-thread-position
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id position frame-id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
|
(when-not (= (:owner-id thread) profile-id)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-allowed))
|
||||||
|
(db/update! conn :comment-thread
|
||||||
|
{:modified-at (dt/now)
|
||||||
|
:position (db/pgpoint position)
|
||||||
|
:frame-id frame-id}
|
||||||
|
{:id (:id thread)})
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
;; --- COMMAND: Update comment frame
|
||||||
|
|
||||||
|
(s/def ::update-comment-thread-frame
|
||||||
|
(s/keys :req-un [::profile-id ::id ::frame-id]))
|
||||||
|
|
||||||
|
(sv/defmethod ::update-comment-thread-frame
|
||||||
|
{::doc/added "1.15"}
|
||||||
|
[{:keys [pool] :as cfg} {:keys [profile-id id frame-id] :as params}]
|
||||||
|
(db/with-atomic [conn pool]
|
||||||
|
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
|
(when-not (= (:owner-id thread) profile-id)
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-allowed))
|
||||||
|
(db/update! conn :comment-thread
|
||||||
|
{:modified-at (dt/now)
|
||||||
|
:frame-id frame-id}
|
||||||
|
{:id (:id thread)})
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
|
|
@ -7,35 +7,18 @@
|
||||||
(ns app.rpc.mutations.comments
|
(ns app.rpc.mutations.comments
|
||||||
(:require
|
(:require
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.geom.point :as gpt]
|
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
|
[app.rpc.commands.comments :as cmd.comments]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
[app.rpc.queries.comments :as comments]
|
|
||||||
[app.rpc.queries.files :as files]
|
[app.rpc.queries.files :as files]
|
||||||
[app.rpc.retry :as retry]
|
[app.rpc.retry :as retry]
|
||||||
[app.util.blob :as blob]
|
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
;; --- Mutation: Create Comment Thread
|
;; --- Mutation: Create Comment Thread
|
||||||
|
|
||||||
(declare upsert-comment-thread-status!)
|
(s/def ::create-comment-thread ::cmd.comments/create-comment-thread)
|
||||||
(declare create-comment-thread)
|
|
||||||
(declare retrieve-page-name)
|
|
||||||
|
|
||||||
(s/def ::page-id ::us/uuid)
|
|
||||||
(s/def ::file-id ::us/uuid)
|
|
||||||
(s/def ::share-id (s/nilable ::us/uuid))
|
|
||||||
(s/def ::profile-id ::us/uuid)
|
|
||||||
(s/def ::position ::gpt/point)
|
|
||||||
(s/def ::content ::us/string)
|
|
||||||
(s/def ::frame-id ::us/uuid)
|
|
||||||
|
|
||||||
(s/def ::create-comment-thread
|
|
||||||
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id ::frame-id]
|
|
||||||
:opt-un [::share-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::create-comment-thread
|
(sv/defmethod ::create-comment-thread
|
||||||
{::retry/max-retries 3
|
{::retry/max-retries 3
|
||||||
|
@ -45,65 +28,14 @@
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||||
(create-comment-thread conn params)))
|
(cmd.comments/create-comment-thread conn params)))
|
||||||
|
|
||||||
(defn- retrieve-next-seqn
|
|
||||||
[conn file-id]
|
|
||||||
(let [sql "select (f.comment_thread_seqn + 1) as next_seqn from file as f where f.id = ?"
|
|
||||||
res (db/exec-one! conn [sql file-id])]
|
|
||||||
(:next-seqn res)))
|
|
||||||
|
|
||||||
(defn- create-comment-thread
|
|
||||||
[conn {:keys [profile-id file-id page-id position content frame-id] :as params}]
|
|
||||||
(let [seqn (retrieve-next-seqn conn file-id)
|
|
||||||
now (dt/now)
|
|
||||||
pname (retrieve-page-name conn params)
|
|
||||||
thread (db/insert! conn :comment-thread
|
|
||||||
{:file-id file-id
|
|
||||||
:owner-id profile-id
|
|
||||||
:participants (db/tjson #{profile-id})
|
|
||||||
:page-name pname
|
|
||||||
:page-id page-id
|
|
||||||
:created-at now
|
|
||||||
:modified-at now
|
|
||||||
:seqn seqn
|
|
||||||
:position (db/pgpoint position)
|
|
||||||
:frame-id frame-id})]
|
|
||||||
|
|
||||||
|
|
||||||
;; Create a comment entry
|
|
||||||
(db/insert! conn :comment
|
|
||||||
{:thread-id (:id thread)
|
|
||||||
:owner-id profile-id
|
|
||||||
:created-at now
|
|
||||||
:modified-at now
|
|
||||||
:content content})
|
|
||||||
|
|
||||||
;; Make the current thread as read.
|
|
||||||
(upsert-comment-thread-status! conn profile-id (:id thread))
|
|
||||||
|
|
||||||
;; Optimistic update of current seq number on file.
|
|
||||||
(db/update! conn :file
|
|
||||||
{:comment-thread-seqn seqn}
|
|
||||||
{:id file-id})
|
|
||||||
|
|
||||||
(select-keys thread [:id :file-id :page-id])))
|
|
||||||
|
|
||||||
(defn- retrieve-page-name
|
|
||||||
[conn {:keys [file-id page-id]}]
|
|
||||||
(let [{:keys [data]} (db/get-by-id conn :file file-id)
|
|
||||||
data (blob/decode data)]
|
|
||||||
(get-in data [:pages-index page-id :name])))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Comment Thread Status
|
;; --- Mutation: Update Comment Thread Status
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::share-id (s/nilable ::us/uuid))
|
(s/def ::share-id (s/nilable ::us/uuid))
|
||||||
|
|
||||||
(s/def ::update-comment-thread-status
|
(s/def ::update-comment-thread-status ::cmd.comments/update-comment-thread-status)
|
||||||
(s/keys :req-un [::profile-id ::id]
|
|
||||||
:opt-un [::share-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::update-comment-thread-status
|
(sv/defmethod ::update-comment-thread-status
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
|
@ -111,30 +43,14 @@
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [cthr (db/get-by-id conn :comment-thread id {:for-update true})]
|
(let [cthr (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
(when-not cthr
|
(when-not cthr (ex/raise :type :not-found))
|
||||||
(ex/raise :type :not-found))
|
|
||||||
|
|
||||||
(files/check-comment-permissions! conn profile-id (:file-id cthr) share-id)
|
(files/check-comment-permissions! conn profile-id (:file-id cthr) share-id)
|
||||||
(upsert-comment-thread-status! conn profile-id (:id cthr)))))
|
(cmd.comments/upsert-comment-thread-status! conn profile-id (:id cthr)))))
|
||||||
|
|
||||||
(def sql:upsert-comment-thread-status
|
|
||||||
"insert into comment_thread_status (thread_id, profile_id)
|
|
||||||
values (?, ?)
|
|
||||||
on conflict (thread_id, profile_id)
|
|
||||||
do update set modified_at = clock_timestamp()
|
|
||||||
returning modified_at;")
|
|
||||||
|
|
||||||
(defn- upsert-comment-thread-status!
|
|
||||||
[conn profile-id thread-id]
|
|
||||||
(db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id]))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Comment Thread
|
;; --- Mutation: Update Comment Thread
|
||||||
|
|
||||||
(s/def ::is-resolved ::us/boolean)
|
(s/def ::update-comment-thread ::cmd.comments/update-comment-thread)
|
||||||
(s/def ::update-comment-thread
|
|
||||||
(s/keys :req-un [::profile-id ::id ::is-resolved]
|
|
||||||
:opt-un [::share-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::update-comment-thread
|
(sv/defmethod ::update-comment-thread
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
|
@ -146,7 +62,6 @@
|
||||||
(ex/raise :type :not-found))
|
(ex/raise :type :not-found))
|
||||||
|
|
||||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
||||||
|
|
||||||
(db/update! conn :comment-thread
|
(db/update! conn :comment-thread
|
||||||
{:is-resolved is-resolved}
|
{:is-resolved is-resolved}
|
||||||
{:id id})
|
{:id id})
|
||||||
|
@ -155,104 +70,31 @@
|
||||||
|
|
||||||
;; --- Mutation: Add Comment
|
;; --- Mutation: Add Comment
|
||||||
|
|
||||||
(s/def ::add-comment
|
(s/def ::add-comment ::cmd.comments/create-comment)
|
||||||
(s/keys :req-un [::profile-id ::thread-id ::content]
|
|
||||||
:opt-un [::share-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::add-comment
|
(sv/defmethod ::add-comment
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
::doc/deprecated "1.15"}
|
::doc/deprecated "1.15"}
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id thread-id content share-id] :as params}]
|
[{:keys [pool] :as cfg} params]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true})
|
(cmd.comments/create-comment conn params)))
|
||||||
(comments/decode-row))
|
|
||||||
pname (retrieve-page-name conn thread)]
|
|
||||||
|
|
||||||
;; Standard Checks
|
|
||||||
(when-not thread (ex/raise :type :not-found))
|
|
||||||
|
|
||||||
;; Permission Checks
|
|
||||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
|
||||||
|
|
||||||
;; Update the page-name cachedattribute on comment thread table.
|
|
||||||
(when (not= pname (:page-name thread))
|
|
||||||
(db/update! conn :comment-thread
|
|
||||||
{:page-name pname}
|
|
||||||
{:id thread-id}))
|
|
||||||
|
|
||||||
;; NOTE: is important that all timestamptz related fields are
|
|
||||||
;; created or updated on the database level for avoid clock
|
|
||||||
;; inconsistencies (some user sees something read that is not
|
|
||||||
;; read, etc...)
|
|
||||||
(let [ppants (:participants thread #{})
|
|
||||||
comment (db/insert! conn :comment
|
|
||||||
{:thread-id thread-id
|
|
||||||
:owner-id profile-id
|
|
||||||
:content content})]
|
|
||||||
|
|
||||||
;; NOTE: this is done in SQL instead of using db/update!
|
|
||||||
;; helper because currently the helper does not allow pass raw
|
|
||||||
;; function call parameters to the underlying prepared
|
|
||||||
;; statement; in a future when we fix/improve it, this can be
|
|
||||||
;; changed to use the helper.
|
|
||||||
|
|
||||||
;; Update thread modified-at attribute and assoc the current
|
|
||||||
;; profile to the participant set.
|
|
||||||
(let [ppants (conj ppants profile-id)
|
|
||||||
sql "update comment_thread
|
|
||||||
set modified_at = clock_timestamp(),
|
|
||||||
participants = ?
|
|
||||||
where id = ?"]
|
|
||||||
(db/exec-one! conn [sql (db/tjson ppants) thread-id]))
|
|
||||||
|
|
||||||
;; Update the current profile status in relation to the
|
|
||||||
;; current thread.
|
|
||||||
(upsert-comment-thread-status! conn profile-id thread-id)
|
|
||||||
|
|
||||||
;; Return the created comment object.
|
|
||||||
comment))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Update Comment
|
;; --- Mutation: Update Comment
|
||||||
|
|
||||||
(s/def ::update-comment
|
(s/def ::update-comment ::cmd.comments/update-comment)
|
||||||
(s/keys :req-un [::profile-id ::id ::content]
|
|
||||||
:opt-un [::share-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::update-comment
|
(sv/defmethod ::update-comment
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
::doc/deprecated "1.15"}
|
::doc/deprecated "1.15"}
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id id content share-id] :as params}]
|
[{:keys [pool] :as cfg} params]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [comment (db/get-by-id conn :comment id {:for-update true})
|
(cmd.comments/update-comment conn params)))
|
||||||
_ (when-not comment (ex/raise :type :not-found))
|
|
||||||
thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true})
|
|
||||||
_ (when-not thread (ex/raise :type :not-found))
|
|
||||||
pname (retrieve-page-name conn thread)]
|
|
||||||
|
|
||||||
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
|
|
||||||
|
|
||||||
;; Don't allow edit comments to not owners
|
|
||||||
(when-not (= (:owner-id thread) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-allowed))
|
|
||||||
|
|
||||||
(db/update! conn :comment
|
|
||||||
{:content content
|
|
||||||
:modified-at (dt/now)}
|
|
||||||
{:id (:id comment)})
|
|
||||||
|
|
||||||
(db/update! conn :comment-thread
|
|
||||||
{:modified-at (dt/now)
|
|
||||||
:page-name pname}
|
|
||||||
{:id (:id thread)})
|
|
||||||
nil)))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Delete Comment Thread
|
;; --- Mutation: Delete Comment Thread
|
||||||
|
|
||||||
(s/def ::delete-comment-thread
|
(s/def ::delete-comment-thread ::cmd.comments/delete-comment-thread)
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::delete-comment-thread
|
(sv/defmethod ::delete-comment-thread
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
|
@ -261,16 +103,14 @@
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
||||||
(when-not (= (:owner-id thread) profile-id)
|
(when-not (= (:owner-id thread) profile-id)
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation :code :not-allowed))
|
||||||
:code :not-allowed))
|
|
||||||
(db/delete! conn :comment-thread {:id id})
|
(db/delete! conn :comment-thread {:id id})
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
|
|
||||||
;; --- Mutation: Delete comment
|
;; --- Mutation: Delete comment
|
||||||
|
|
||||||
(s/def ::delete-comment
|
(s/def ::delete-comment ::cmd.comments/delete-comment)
|
||||||
(s/keys :req-un [::profile-id ::id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::delete-comment
|
(sv/defmethod ::delete-comment
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
|
@ -279,44 +119,5 @@
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(let [comment (db/get-by-id conn :comment id {:for-update true})]
|
(let [comment (db/get-by-id conn :comment id {:for-update true})]
|
||||||
(when-not (= (:owner-id comment) profile-id)
|
(when-not (= (:owner-id comment) profile-id)
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation :code :not-allowed))
|
||||||
:code :not-allowed))
|
|
||||||
|
|
||||||
(db/delete! conn :comment {:id id}))))
|
(db/delete! conn :comment {:id id}))))
|
||||||
|
|
||||||
;; --- Mutation: Update comment thread position
|
|
||||||
|
|
||||||
(s/def ::update-comment-thread-position
|
|
||||||
(s/keys :req-un [::profile-id ::id ::position ::frame-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::update-comment-thread-position
|
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id id position frame-id] :as params}]
|
|
||||||
(db/with-atomic [conn pool]
|
|
||||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
|
||||||
(when-not (= (:owner-id thread) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-allowed))
|
|
||||||
(db/update! conn :comment-thread
|
|
||||||
{:modified-at (dt/now)
|
|
||||||
:position (db/pgpoint position)
|
|
||||||
:frame-id frame-id}
|
|
||||||
{:id (:id thread)})
|
|
||||||
nil)))
|
|
||||||
|
|
||||||
;; --- Mutation: Update comment frame
|
|
||||||
|
|
||||||
(s/def ::update-comment-thread-frame
|
|
||||||
(s/keys :req-un [::profile-id ::id ::frame-id]))
|
|
||||||
|
|
||||||
(sv/defmethod ::update-comment-thread-frame
|
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id id frame-id] :as params}]
|
|
||||||
(db/with-atomic [conn pool]
|
|
||||||
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
|
|
||||||
(when-not (= (:owner-id thread) profile-id)
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :not-allowed))
|
|
||||||
(db/update! conn :comment-thread
|
|
||||||
{:modified-at (dt/now)
|
|
||||||
:frame-id frame-id}
|
|
||||||
{:id (:id thread)})
|
|
||||||
nil)))
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
(ns app.rpc.queries.comments
|
(ns app.rpc.queries.comments
|
||||||
(:require
|
(:require
|
||||||
[app.common.spec :as us]
|
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.rpc.commands.comments :as cmd.comments]
|
[app.rpc.commands.comments :as cmd.comments]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
|
@ -51,7 +50,7 @@
|
||||||
(sv/defmethod ::comment-thread
|
(sv/defmethod ::comment-thread
|
||||||
{::doc/added "1.0"
|
{::doc/added "1.0"
|
||||||
::doc/deprecated "1.15"}
|
::doc/deprecated "1.15"}
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id id share-id] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}]
|
||||||
(with-open [conn (db/open pool)]
|
(with-open [conn (db/open pool)]
|
||||||
(files/check-comment-permissions! conn profile-id file-id share-id)
|
(files/check-comment-permissions! conn profile-id file-id share-id)
|
||||||
(cmd.comments/get-comment-thread conn params)))
|
(cmd.comments/get-comment-thread conn params)))
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
frame-id (cph/frame-id-by-position objects (:position params))
|
frame-id (cph/frame-id-by-position objects (:position params))
|
||||||
params (assoc params :frame-id frame-id)]
|
params (assoc params :frame-id frame-id)]
|
||||||
(->> (rp/mutation! :create-comment-thread params)
|
(->> (rp/cmd! :create-comment-thread params)
|
||||||
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %)}))
|
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %)}))
|
||||||
(rx/map created-thread-on-workspace)
|
(rx/map created-thread-on-workspace)
|
||||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
(let [share-id (-> state :viewer-local :share-id)
|
(let [share-id (-> state :viewer-local :share-id)
|
||||||
frame-id (:frame-id params)
|
frame-id (:frame-id params)
|
||||||
params (assoc params :share-id share-id :frame-id frame-id)]
|
params (assoc params :share-id share-id :frame-id frame-id)]
|
||||||
(->> (rp/mutation! :create-comment-thread params)
|
(->> (rp/cmd! :create-comment-thread params)
|
||||||
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %) :share-id share-id}))
|
(rx/mapcat #(rp/cmd! :get-comment-thread {:file-id (:file-id %) :id (:id %) :share-id share-id}))
|
||||||
(rx/map created-thread-on-viewer)
|
(rx/map created-thread-on-viewer)
|
||||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)
|
(let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)
|
||||||
share-id (-> state :viewer-local :share-id)]
|
share-id (-> state :viewer-local :share-id)]
|
||||||
(->> (rp/mutation! :update-comment-thread-status {:id id :share-id share-id})
|
(->> (rp/cmd! :update-comment-thread-status {:id id :share-id share-id})
|
||||||
(rx/map (constantly done))
|
(rx/map (constantly done))
|
||||||
(rx/catch #(rx/throw {:type :comment-error})))))))
|
(rx/catch #(rx/throw {:type :comment-error})))))))
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
(->> (rp/mutation! :update-comment-thread {:id id :is-resolved is-resolved :share-id share-id})
|
(->> (rp/cmd! :update-comment-thread {:id id :is-resolved is-resolved :share-id share-id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error}))
|
(rx/catch #(rx/throw {:type :comment-error}))
|
||||||
(rx/ignore))))))
|
(rx/ignore))))))
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(->> (rp/mutation! :add-comment {:thread-id (:id thread) :content content :share-id share-id})
|
(->> (rp/cmd! :create-comment {:thread-id (:id thread) :content content :share-id share-id})
|
||||||
(rx/map #(partial created %))
|
(rx/map #(partial created %))
|
||||||
(rx/catch #(rx/throw {:type :comment-error})))
|
(rx/catch #(rx/throw {:type :comment-error})))
|
||||||
(rx/of (refresh-comment-thread thread))))))))
|
(rx/of (refresh-comment-thread thread))))))))
|
||||||
|
@ -184,7 +184,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
(->> (rp/mutation! :update-comment {:id id :content content :share-id share-id})
|
(->> (rp/cmd! :update-comment {:id id :content content :share-id share-id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error}))
|
(rx/catch #(rx/throw {:type :comment-error}))
|
||||||
(rx/ignore))))))
|
(rx/ignore))))))
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(->> (rp/mutation! :delete-comment-thread {:id id})
|
(->> (rp/cmd! :delete-comment-thread {:id id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error}))
|
(rx/catch #(rx/throw {:type :comment-error}))
|
||||||
(rx/ignore)))))
|
(rx/ignore)))))
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
(->> (rp/mutation! :delete-comment-thread {:id id :share-id share-id})
|
(->> (rp/cmd! :delete-comment-thread {:id id :share-id share-id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error}))
|
(rx/catch #(rx/throw {:type :comment-error}))
|
||||||
(rx/ignore))))))
|
(rx/ignore))))))
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)]
|
(let [share-id (-> state :viewer-local :share-id)]
|
||||||
(->> (rp/mutation! :delete-comment {:id id :share-id share-id})
|
(->> (rp/cmd! :delete-comment {:id id :share-id share-id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error}))
|
(rx/catch #(rx/throw {:type :comment-error}))
|
||||||
(rx/ignore))))))
|
(rx/ignore))))))
|
||||||
|
|
||||||
|
@ -426,7 +426,7 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(let [thread-id (:id thread)]
|
(let [thread-id (:id thread)]
|
||||||
(->> (rp/mutation! :update-comment-thread-frame {:id thread-id :frame-id frame-id})
|
(->> (rp/cmd! :update-comment-thread-frame {:id thread-id :frame-id frame-id})
|
||||||
(rx/catch #(rx/throw {:type :comment-error :code :update-comment-thread-frame}))
|
(rx/catch #(rx/throw {:type :comment-error :code :update-comment-thread-frame}))
|
||||||
(rx/ignore)))))))
|
(rx/ignore)))))))
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
|
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(rx/of (dwc/commit-changes changes))
|
(rx/of (dwc/commit-changes changes))
|
||||||
(->> (rp/mutation :update-comment-thread-position thread)
|
(->> (rp/cmd! :update-comment-thread-position thread)
|
||||||
(rx/catch #(rx/throw {:type :update-comment-thread-position}))
|
(rx/catch #(rx/throw {:type :update-comment-thread-position}))
|
||||||
(rx/ignore))))))))
|
(rx/ignore))))))))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue