mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 22:10:34 +00:00
🐛 Absorb a deleted library into the files that used it
Deleting a shared library removes its links to the files using it at once, so they stop listing it, and queues the delete-object job, which absorbs the library into the files it finds through those links. By then there were none, so nothing was absorbed: the files lost the library and their copies kept pointing at the deleted file (PENPOT-1092). Unpublishing absorbs before it unlinks, so it was not affected. The delete reads the files using the library before unlinking them and gives them to the job (:absorb-file-ids), which absorbs into them; without them, as from the project and team deletions, the job still finds them through the links. rpc-file-test/delete-shared-library-absorbs-it-into-the-files-using-it deletes the library through the RPC and runs the job it queued. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
68bafacc9d
commit
0bffa600d8
@ -1004,16 +1004,21 @@
|
||||
:modified-at (ct/now)
|
||||
:has-media-trimmed false}))))
|
||||
|
||||
(defn- get-referenced-file-ids
|
||||
"The ids of the files that use the library `id`."
|
||||
[cfg id]
|
||||
(->> (db/exec! cfg [sql:get-referenced-files id])
|
||||
(into [] bfc/xf-map-id)))
|
||||
|
||||
(defn- absorb-library*
|
||||
"Find all files using a shared library, and absorb all library assets
|
||||
into the file local libraries"
|
||||
[cfg {:keys [id data] :as library}]
|
||||
"Absorb all library assets into the local libraries of the files
|
||||
`file-ids`, or, without them, of all the files using it."
|
||||
[cfg {:keys [id data] :as library} file-ids]
|
||||
|
||||
(assert (db/connection-map? cfg)
|
||||
"expected cfg with valid connection")
|
||||
|
||||
(let [ids (->> (db/exec! cfg [sql:get-referenced-files id])
|
||||
(sequence bfc/xf-map-id))]
|
||||
(let [ids (or file-ids (get-referenced-file-ids cfg id))]
|
||||
|
||||
(l/trc :hint "absorbing library"
|
||||
:library-id (str id)
|
||||
@ -1023,7 +1028,10 @@
|
||||
library))
|
||||
|
||||
(defn absorb-library
|
||||
[{:keys [::db/conn] :as cfg} id]
|
||||
"Absorb the assets of the library `id` into the files using it, or into
|
||||
the files `file-ids` when given (the users of a library that is already
|
||||
unlinked from them)."
|
||||
[{:keys [::db/conn] :as cfg} id & [file-ids]]
|
||||
(let [file (-> (bfc/get-file cfg id
|
||||
:realize? true
|
||||
:lock-for-update? true
|
||||
@ -1039,7 +1047,7 @@
|
||||
(-> (cfeat/get-team-enabled-features cf/flags team)
|
||||
(cfeat/check-file-features! (:features file)))
|
||||
|
||||
(absorb-library* cfg file)))
|
||||
(absorb-library* cfg file file-ids)))
|
||||
|
||||
(defn- set-file-shared
|
||||
[{:keys [::db/conn] :as cfg} {:keys [profile-id id] :as params}]
|
||||
@ -1112,7 +1120,10 @@
|
||||
{:deleted-at (ct/in-future delay)}
|
||||
{:id file-id}
|
||||
{::db/return-keys [:id :name :is-shared :deleted-at
|
||||
:project-id :created-at :modified-at]})]
|
||||
:project-id :created-at :modified-at]})
|
||||
;; read before the relations go, for the job that absorbs the library
|
||||
users (when (:is-shared file)
|
||||
(get-referenced-file-ids cfg file-id))]
|
||||
|
||||
;; Remove all possible relations for that file
|
||||
(db/delete! conn :file-library-rel
|
||||
@ -1120,9 +1131,11 @@
|
||||
|
||||
(jobs/submit cfg
|
||||
{::jobs/name :delete-object
|
||||
::jobs/params {:object :file
|
||||
:deleted-at (:deleted-at file)
|
||||
:id file-id}})
|
||||
::jobs/params (cond-> {:object :file
|
||||
:deleted-at (:deleted-at file)
|
||||
:id file-id}
|
||||
(seq users)
|
||||
(assoc :absorb-file-ids users))})
|
||||
file))
|
||||
|
||||
(def ^:private
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
{::db/return-keys false}))
|
||||
|
||||
(defmethod delete-object :file
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at]}]
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id deleted-at absorb-file-ids]}]
|
||||
(when-let [file (db/get* conn :file {:id id}
|
||||
{::db/remove-deleted false
|
||||
::sql/columns [:id :is-shared]})]
|
||||
@ -57,7 +57,7 @@
|
||||
(not *team-deletion*))
|
||||
;; NOTE: we don't prevent file deletion on absorb operation failure
|
||||
(try
|
||||
(db/tx-run! cfg files/absorb-library id)
|
||||
(db/tx-run! cfg files/absorb-library id absorb-file-ids)
|
||||
(catch Throwable cause
|
||||
(l/warn :hint "error on absorbing library"
|
||||
:file-id id
|
||||
@ -156,7 +156,9 @@
|
||||
[:object [:enum :snapshot :team :project :profile :file]]
|
||||
[:deleted-at ::ct/inst]
|
||||
[:id ::sm/uuid]
|
||||
[:file-id {:optional true} ::sm/uuid]])
|
||||
[:file-id {:optional true} ::sm/uuid]
|
||||
;; the users of a deleted library, already unlinked from it
|
||||
[:absorb-file-ids {:optional true} [:vector ::sm/uuid]]])
|
||||
|
||||
(defn execute-delete-object
|
||||
"Plain job handler: run the delete-object multimethod on the provided
|
||||
|
||||
@ -2214,6 +2214,16 @@
|
||||
:is-shared false})))
|
||||
(check-absorbed! setup)))
|
||||
|
||||
(t/deftest delete-shared-library-absorbs-it-into-the-files-using-it
|
||||
;; Deleting the file unlinks it from its users at once; the job that
|
||||
;; follows must still find them and give them the components they use.
|
||||
(let [{:keys [profile library] :as setup} (create-library-copy!)]
|
||||
(t/is (th/success? (th/command! {::th/type :delete-file
|
||||
::rpc/profile-id (:id profile)
|
||||
:id (:id library)})))
|
||||
(th/run-pending-jobs)
|
||||
(check-absorbed! setup)))
|
||||
|
||||
(t/deftest delete-shared-library-with-storage-file-data
|
||||
(binding [cf/config (assoc cf/config :file-data-backend "storage")]
|
||||
;; Keep this test focused on the storage-backed file-data fix: create
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user