From 82b10ecb87284014033981c33fdc84e8008712b5 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 26 Jan 2024 14:49:23 +0100 Subject: [PATCH] :sparkles: Refactor comments RPC methods to use schema instead of spec --- backend/src/app/rpc/commands/comments.clj | 457 ++++++++++++---------- 1 file changed, 241 insertions(+), 216 deletions(-) diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj index 9e1a9d436..4949f1a43 100644 --- a/backend/src/app/rpc/commands/comments.clj +++ b/backend/src/app/rpc/commands/comments.clj @@ -9,7 +9,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.geom.point :as gpt] - [app.common.spec :as us] + [app.common.schema :as sm] [app.common.uuid :as uuid] [app.db :as db] [app.db.sql :as sql] @@ -24,18 +24,21 @@ [app.rpc.retry :as rtry] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.util.time :as dt] - [clojure.spec.alpha :as s])) + [app.util.time :as dt])) ;; --- GENERAL PURPOSE INTERNAL HELPERS -(defn decode-row +(defn- decode-row [{:keys [participants position] :as row}] (cond-> row (db/pgpoint? position) (assoc :position (db/decode-pgpoint position)) (db/pgobject? participants) (assoc :participants (db/decode-transit-pgobject participants)))) -(def sql:get-file +(def xf-decode-row + (map decode-row)) + +(def ^:privateqpage-name + sql:get-file "select f.id, f.modified_at, f.revn, f.features, f.project_id, p.team_id, f.data from file as f @@ -45,17 +48,19 @@ (defn- get-file "A specialized version of get-file for comments module." - [{:keys [::db/conn] :as cfg} file-id page-id] - (if-let [{:keys [data] :as file} (some-> (db/exec-one! conn [sql:get-file file-id]) - (files/decode-row))] - (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] - (-> file - (assoc :page-name (dm/get-in data [:pages-index page-id :name])) - (assoc :page-id page-id))) + [cfg file-id page-id] + (let [file (db/exec-one! cfg [sql:get-file file-id])] + (when-not file + (ex/raise :type :not-found + :code :object-not-found + :hint "file not found")) - (ex/raise :type :not-found - :code :object-not-found - :hint "file not found"))) + (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] + (let [{:keys [data] :as file} (files/decode-row file)] + (-> file + (assoc :page-name (dm/get-in data [:pages-index page-id :name])) + (assoc :page-id page-id) + (dissoc :data)))))) (defn- get-comment-thread [conn thread-id & {:as opts}] @@ -93,23 +98,25 @@ (declare ^:private get-comment-threads) -(s/def ::team-id ::us/uuid) -(s/def ::file-id ::us/uuid) -(s/def ::share-id (s/nilable ::us/uuid)) - -(s/def ::get-comment-threads - (s/and (s/keys :req [::rpc/profile-id] - :opt-un [::file-id ::share-id ::team-id]) - #(or (:file-id %) (:team-id %)))) +(def ^:private + schema:get-comment-threads + [:and + [:map {:title "get-comment-threads"} + [:file-id {:optional true} ::sm/uuid] + [:team-id {:optional true} ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]] + [::sm/contains-any #{:file-id :team-id}]]) (sv/defmethod ::get-comment-threads - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id share-id] :as params}] - (dm/with-open [conn (db/open pool)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (get-comment-threads conn profile-id file-id))) + {::doc/added "1.15" + ::sm/params schema:get-comment-threads} + [cfg {:keys [::rpc/profile-id file-id share-id] :as params}] -(def sql:comment-threads + (db/run! cfg (fn [{:keys [::db/conn]}] + (files/check-comment-permissions! conn profile-id file-id share-id) + (get-comment-threads conn profile-id file-id)))) + +(def ^:private sql:comment-threads "select distinct on (ct.id) ct.*, f.name as file_name, @@ -134,23 +141,24 @@ (defn- get-comment-threads [conn profile-id file-id] (->> (db/exec! conn [sql:comment-threads profile-id file-id]) - (into [] (map decode-row)))) + (into [] xf-decode-row))) ;; --- COMMAND: Get Unread Comment Threads (declare ^:private get-unread-comment-threads) -(s/def ::team-id ::us/uuid) -(s/def ::get-unread-comment-threads - (s/keys :req [::rpc/profile-id] - :req-un [::team-id])) +(def ^:private + schema:get-unread-comment-threads + [:map {:title "get-unread-comment-threads"} + [:team-id ::sm/uuid]]) (sv/defmethod ::get-unread-comment-threads - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id] :as params}] - (dm/with-open [conn (db/open pool)] - (teams/check-read-permissions! conn profile-id team-id) - (get-unread-comment-threads conn profile-id team-id))) + {::doc/added "1.15" + ::sm/params schema:get-unread-comment-threads} + [cfg {:keys [::rpc/profile-id team-id] :as params}] + (db/run! cfg (fn [{:keys [::db/conn]}] + (teams/check-read-permissions! conn profile-id team-id) + (get-unread-comment-threads conn profile-id team-id)))) (def sql:comment-threads-by-team "select distinct on (ct.id) @@ -182,62 +190,60 @@ (defn- get-unread-comment-threads [conn profile-id team-id] (->> (db/exec! conn [sql:unread-comment-threads-by-team profile-id team-id]) - (into [] (map decode-row)))) - + (into [] xf-decode-row))) ;; --- COMMAND: Get Single Comment Thread -(s/def ::get-comment-thread - (s/keys :req [::rpc/profile-id] - :req-un [::file-id ::us/id] - :opt-un [::share-id])) +(def ^:private + schema:get-comment-thread + [:map {:title "get-comment-thread"} + [:file-id ::sm/uuid] + [:id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::get-comment-thread - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id id share-id] :as params}] - (dm/with-open [conn (db/open pool)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (let [sql (str "with threads as (" sql:comment-threads ")" - "select * from threads where id = ?")] - (-> (db/exec-one! conn [sql profile-id file-id id]) - (decode-row))))) + {::doc/added "1.15" + ::sm/params schema:get-comment-thread} + [cfg {:keys [::rpc/profile-id file-id id share-id] :as params}] + (db/run! cfg (fn [{:keys [::db/conn]}] + (files/check-comment-permissions! conn profile-id file-id share-id) + (let [sql (str "with threads as (" sql:comment-threads ")" + "select * from threads where id = ?")] + (-> (db/exec-one! conn [sql profile-id file-id id]) + (decode-row)))))) ;; --- COMMAND: Retrieve Comments (declare ^:private get-comments) -(s/def ::thread-id ::us/uuid) -(s/def ::get-comments - (s/keys :req [::rpc/profile-id] - :req-un [::thread-id] - :opt-un [::share-id])) +(def ^:private + schema:get-comments + [:map {:title "get-comments"} + [:thread-id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::get-comments - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id thread-id share-id] :as params}] - (dm/with-open [conn (db/open pool)] - (let [{:keys [file-id] :as thread} (get-comment-thread conn thread-id)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (get-comments conn thread-id)))) - -(def sql:comments - "select c.* from comment as c - where c.thread_id = ? - order by c.created_at asc") + {::doc/added "1.15" + ::sm/params schema:get-comments} + [cfg {:keys [::rpc/profile-id thread-id share-id]}] + (db/run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [file-id] :as thread} (get-comment-thread conn thread-id)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (get-comments conn thread-id))))) (defn- get-comments [conn thread-id] (->> (db/query conn :comment {:thread-id thread-id} {:order-by [[:created-at :asc]]}) - (into [] (map decode-row)))) + (into [] xf-decode-row))) ;; --- COMMAND: Get file comments users ;; All the profiles that had comment the file, plus the current ;; profile. -(def sql:file-comment-users +(def ^:private sql:file-comment-users "WITH available_profiles AS ( SELECT DISTINCT owner_id AS id FROM comment @@ -256,20 +262,22 @@ [conn file-id profile-id] (db/exec! conn [sql:file-comment-users file-id profile-id])) -(s/def ::get-profiles-for-file-comments - (s/keys :req [::rpc/profile-id] - :req-un [::file-id] - :opt-un [::share-id])) +(def ^:private + schema:get-profiles-for-file-comments + [:map {:title "get-profiles-for-file-comments"} + [:file-id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::get-profiles-for-file-comments "Retrieves a list of profiles with limited set of properties of all participants on comment threads of the file." {::doc/added "1.15" - ::doc/changes ["1.15" "Imported from queries and renamed."]} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id share-id]}] - (dm/with-open [conn (db/open pool)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (get-file-comments-users conn file-id profile-id))) + ::doc/changes ["1.15" "Imported from queries and renamed."] + ::sm/params schema:get-profiles-for-file-comments} + [cfg {:keys [::rpc/profile-id file-id share-id]}] + (db/run! cfg (fn [{:keys [::db/conn]}] + (files/check-comment-permissions! conn profile-id file-id share-id) + (get-file-comments-users conn file-id profile-id)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; MUTATION COMMANDS @@ -279,52 +287,52 @@ ;; --- COMMAND: Create Comment Thread -(s/def ::page-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 [::rpc/profile-id] - :req-un [::file-id ::position ::content ::page-id ::frame-id] - :opt-un [::share-id])) +(def ^:private + schema:create-comment-thread + [:map {:title "create-comment-thread"} + [:file-id ::sm/uuid] + [:position ::gpt/point] + [:content :string] + [:page-id ::sm/uuid] + [:frame-id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::create-comment-thread {::doc/added "1.15" - ::webhooks/event? true} + ::webhooks/event? true + ::rtry/enabled true + ::rtry/when rtry/conflict-exception? + ::sm/params schema:create-comment-thread} [cfg {:keys [::rpc/profile-id ::rpc/request-at file-id page-id share-id position content frame-id]}] - (db/tx-run! cfg - (fn [{:keys [::db/conn] :as cfg}] - (files/check-comment-permissions! conn profile-id file-id share-id) - (let [{:keys [team-id project-id page-name] :as file} (get-file cfg file-id page-id)] - (run! (partial quotes/check-quote! conn) - (list {::quotes/id ::quotes/comment-threads-per-file - ::quotes/profile-id profile-id - ::quotes/team-id team-id - ::quotes/project-id project-id - ::quotes/file-id file-id} - {::quotes/id ::quotes/comments-per-file - ::quotes/profile-id profile-id - ::quotes/team-id team-id - ::quotes/project-id project-id - ::quotes/file-id file-id})) + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] + (files/check-comment-permissions! cfg profile-id file-id share-id) + (let [{:keys [team-id project-id page-name]} (get-file conn file-id page-id)] + (run! (partial quotes/check-quote! cfg) + (list {::quotes/id ::quotes/comment-threads-per-file + ::quotes/profile-id profile-id + ::quotes/team-id team-id + ::quotes/project-id project-id + ::quotes/file-id file-id} + {::quotes/id ::quotes/comments-per-file + ::quotes/profile-id profile-id + ::quotes/team-id team-id + ::quotes/project-id project-id + ::quotes/file-id file-id})) - (-> cfg - (assoc ::rtry/when rtry/conflict-exception?) - (assoc ::rtry/label "create-comment-thread") - (rtry/invoke create-comment-thread {:created-at request-at - :profile-id profile-id - :file-id file-id - :page-id page-id - :page-name page-name - :position position - :content content - :frame-id frame-id})))))) + (create-comment-thread conn {:created-at request-at + :profile-id profile-id + :file-id file-id + :page-id page-id + :page-name page-name + :position position + :content content + :frame-id frame-id}))))) (defn- create-comment-thread - [{:keys [::db/conn]} {:keys [profile-id file-id page-id page-name created-at position content frame-id]}] + [conn {:keys [profile-id file-id page-id page-name created-at position content frame-id]}] + (let [;; NOTE: we take the next seq number from a separate query because the whole ;; operation can be retried on conflict, and in this case the new seq shold be ;; retrieved from the database. @@ -364,68 +372,72 @@ ;; --- 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 [::rpc/profile-id] - :req-un [::id] - :opt-un [::share-id])) +(def ^:private + schema:update-comment-thread-status + [:map {:title "update-comment-thread-status"} + [:id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::update-comment-thread-status - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}] - (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (upsert-comment-thread-status! conn profile-id id)))) + {::doc/added "1.15" + ::sm/params schema:update-comment-thread-status} + [cfg {:keys [::rpc/profile-id id share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (upsert-comment-thread-status! conn profile-id id))))) ;; --- COMMAND: Update Comment Thread -(s/def ::is-resolved ::us/boolean) -(s/def ::update-comment-thread - (s/keys :req [::rpc/profile-id] - :req-un [::id ::is-resolved] - :opt-un [::share-id])) +(def ^:private + schema:update-comment-thread + [:map {:title "update-comment-thread"} + [:id ::sm/uuid] + [:is-resolved :boolean] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::update-comment-thread - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id is-resolved share-id] :as params}] - (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (db/update! conn :comment-thread - {:is-resolved is-resolved} - {:id id}) - nil))) + {::doc/added "1.15" + ::sm/params schema:update-comment-thread} + [cfg {:keys [::rpc/profile-id id is-resolved share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (db/update! conn :comment-thread + {:is-resolved is-resolved} + {:id id}) + nil)))) ;; --- COMMAND: Add Comment (declare ^:private get-comment-thread) -(s/def ::create-comment - (s/keys :req [::rpc/profile-id] - :req-un [::thread-id ::content] - :opt-un [::share-id])) +(def ^:private + schema:create-comment + [:map {:title "create-comment"} + [:thread-id ::sm/uuid] + [:content :string] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::create-comment {::doc/added "1.15" - ::webhooks/event? true} + ::webhooks/event? true + ::sm/params schema:create-comment} [cfg {:keys [::rpc/profile-id ::rpc/request-at thread-id share-id content]}] (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (let [{:keys [file-id page-id] :as thread} (get-comment-thread conn thread-id ::sql/for-update true) {:keys [team-id project-id page-name] :as file} (get-file cfg file-id page-id)] - (files/check-comment-permissions! conn profile-id (:id file) share-id) + (files/check-comment-permissions! conn profile-id file-id share-id) (quotes/check-quote! conn {::quotes/id ::quotes/comments-per-file ::quotes/profile-id profile-id ::quotes/team-id team-id ::quotes/project-id project-id - ::quotes/file-id (:id file)}) + ::quotes/file-id file-id}) ;; Update the page-name cached attribute on comment thread table. (when (not= page-name (:page-name thread)) @@ -461,15 +473,17 @@ ;; --- COMMAND: Update Comment -(s/def ::update-comment - (s/keys :req [::rpc/profile-id] - :req-un [::id ::content] - :opt-un [::share-id])) +(def ^:private + schema:update-comment + [:map {:title "update-comment"} + [:id ::sm/uuid] + [:content :string] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::update-comment - {::doc/added "1.15"} + {::doc/added "1.15" + ::sm/params schema:update-comment} [cfg {:keys [::rpc/profile-id ::rpc/request-at id share-id content]}] - (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (let [{:keys [thread-id owner-id] :as comment} (get-comment conn id ::sql/for-update true) @@ -482,7 +496,7 @@ (ex/raise :type :validation :code :not-allowed)) - (let [{:keys [page-name] :as file} (get-file cfg file-id page-id)] + (let [{:keys [page-name]} (get-file cfg file-id page-id)] (db/update! conn :comment {:content content :modified-at request-at} @@ -496,79 +510,90 @@ ;; --- COMMAND: Delete Comment Thread -(s/def ::delete-comment-thread - (s/keys :req [::rpc/profile-id] - :req-un [::id] - :opt-un [::share-id])) +(def ^:private + schema:delete-comment-thread + [:map {:title "delete-comment-thread"} + [:id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::delete-comment-thread - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id]}] - (db/with-atomic [conn pool] - (let [{:keys [owner-id file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (when-not (= owner-id profile-id) - (ex/raise :type :validation - :code :not-allowed)) + {::doc/added "1.15" + ::sm/params schema:delete-comment-thread} + [cfg {:keys [::rpc/profile-id id share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [owner-id file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (when-not (= owner-id profile-id) + (ex/raise :type :validation + :code :not-allowed)) - (db/delete! conn :comment-thread {:id id}) - nil))) + (db/delete! conn :comment-thread {:id id}) + nil)))) ;; --- COMMAND: Delete comment -(s/def ::delete-comment - (s/keys :req [::rpc/profile-id] - :req-un [::id] - :opt-un [::share-id])) +(def ^:private + schema:delete-comment + [:map {:title "delete-comment"} + [:id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::delete-comment - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}] - (db/with-atomic [conn pool] - (let [{:keys [owner-id thread-id] :as comment} (get-comment conn id ::sql/for-update true) - {:keys [file-id] :as thread} (get-comment-thread conn thread-id)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (when-not (= owner-id profile-id) - (ex/raise :type :validation - :code :not-allowed)) - (db/delete! conn :comment {:id id}) - nil))) + {::doc/added "1.15" + ::sm/params schema:delete-comment} + [cfg {:keys [::rpc/profile-id id share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [owner-id thread-id] :as comment} (get-comment conn id ::sql/for-update true) + {:keys [file-id] :as thread} (get-comment-thread conn thread-id)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (when-not (= owner-id profile-id) + (ex/raise :type :validation + :code :not-allowed)) + (db/delete! conn :comment {:id id}) + nil)))) ;; --- COMMAND: Update comment thread position -(s/def ::update-comment-thread-position - (s/keys :req [::rpc/profile-id] - :req-un [::id ::position ::frame-id] - :opt-un [::share-id])) +(def ^:private + schema:update-comment-thread-position + [:map {:title "update-comment-thread-position"} + [:id ::sm/uuid] + [:position ::gpt/point] + [:frame-id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::update-comment-thread-position - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id ::rpc/request-at id position frame-id share-id]}] - (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (db/update! conn :comment-thread - {:modified-at request-at - :position (db/pgpoint position) - :frame-id frame-id} - {:id (:id thread)}) - nil))) + {::doc/added "1.15" + ::sm/params schema:update-comment-thread-position} + [cfg {:keys [::rpc/profile-id ::rpc/request-at id position frame-id share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (db/update! conn :comment-thread + {:modified-at request-at + :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 [::rpc/profile-id] - :req-un [::id ::frame-id] - :opt-un [::share-id])) +(def ^:private + schema:update-comment-thread-frame + [:map {:title "update-comment-thread-frame"} + [:id ::sm/uuid] + [:frame-id ::sm/uuid] + [:share-id {:optional true} [:maybe ::sm/uuid]]]) (sv/defmethod ::update-comment-thread-frame - {::doc/added "1.15"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id ::rpc/request-at id frame-id share-id]}] - (db/with-atomic [conn pool] - (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] - (files/check-comment-permissions! conn profile-id file-id share-id) - (db/update! conn :comment-thread - {:modified-at request-at - :frame-id frame-id} - {:id id}) - nil))) + {::doc/added "1.15" + ::sm/params schema:update-comment-thread-frame} + [cfg {:keys [::rpc/profile-id ::rpc/request-at id frame-id share-id]}] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (let [{:keys [file-id] :as thread} (get-comment-thread conn id ::sql/for-update true)] + (files/check-comment-permissions! conn profile-id file-id share-id) + (db/update! conn :comment-thread + {:modified-at request-at + :frame-id frame-id} + {:id id}) + nil))))