mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🐛 Fix incorrect project restoration on restoring file (#7778)
Some checks failed
_DEVELOP / build-bundle (push) Has been cancelled
_DEVELOP / build-docker (push) Has been cancelled
_STAGING / build-bundle (push) Has been cancelled
_STAGING / build-docker (push) Has been cancelled
Commit Message Check / Check Commit Message (push) Has been cancelled
CI: Tests / Integration Tests (push) Has been cancelled
Some checks failed
_DEVELOP / build-bundle (push) Has been cancelled
_DEVELOP / build-docker (push) Has been cancelled
_STAGING / build-bundle (push) Has been cancelled
_STAGING / build-docker (push) Has been cancelled
Commit Message Check / Check Commit Message (push) Has been cancelled
CI: Tests / Integration Tests (push) Has been cancelled
This commit is contained in:
@@ -1209,7 +1209,7 @@
|
|||||||
;; --- MUTATION COMMAND: restore-files-immediatelly
|
;; --- MUTATION COMMAND: restore-files-immediatelly
|
||||||
|
|
||||||
(def ^:private sql:resolve-editable-files
|
(def ^:private sql:resolve-editable-files
|
||||||
"SELECT f.id
|
"SELECT f.id, f.project_id
|
||||||
FROM file AS f
|
FROM file AS f
|
||||||
JOIN project AS p ON (p.id = f.project_id)
|
JOIN project AS p ON (p.id = f.project_id)
|
||||||
JOIN team AS t ON (t.id = p.team_id)
|
JOIN team AS t ON (t.id = p.team_id)
|
||||||
@@ -1250,18 +1250,38 @@
|
|||||||
{:file-id file-id}
|
{:file-id file-id}
|
||||||
{::db/return-keys false}))
|
{::db/return-keys false}))
|
||||||
|
|
||||||
|
(def ^:private sql:restore-projects
|
||||||
|
"UPDATE project SET deleted_at = null WHERE id = ANY(?::uuid[])")
|
||||||
|
|
||||||
|
(defn- restore-projects
|
||||||
|
[conn project-ids]
|
||||||
|
(let [project-ids (db/create-array conn "uuid" project-ids)]
|
||||||
|
(->> (db/exec-one! conn [sql:restore-projects project-ids])
|
||||||
|
(db/get-update-count))))
|
||||||
|
|
||||||
(defn- restore-deleted-team-files
|
(defn- restore-deleted-team-files
|
||||||
[{:keys [::db/conn]} {:keys [::rpc/profile-id team-id ids]}]
|
[{:keys [::db/conn]} {:keys [::rpc/profile-id team-id ids]}]
|
||||||
(teams/check-edition-permissions! conn profile-id team-id)
|
(teams/check-edition-permissions! conn profile-id team-id)
|
||||||
|
(let [total-files
|
||||||
|
(count ids)
|
||||||
|
|
||||||
(reduce (fn [affected {:keys [id]}]
|
{:keys [files projects]}
|
||||||
(let [index (inc (count affected))]
|
(reduce (fn [result {:keys [id project-id]}]
|
||||||
(events/tap :progress {:file-id id :index index :total (count ids)})
|
(let [index (-> result :files count)]
|
||||||
|
(events/tap :progress {:file-id id :index index :total total-files})
|
||||||
(restore-file conn id)
|
(restore-file conn id)
|
||||||
(conj affected id)))
|
|
||||||
#{}
|
(-> result
|
||||||
|
(update :files conj id)
|
||||||
|
(update :projects conj project-id))))
|
||||||
|
|
||||||
|
{:files #{} :projectes #{}}
|
||||||
(db/plan conn [sql:resolve-editable-files team-id
|
(db/plan conn [sql:resolve-editable-files team-id
|
||||||
(db/create-array conn "uuid" ids)])))
|
(db/create-array conn "uuid" ids)]))]
|
||||||
|
|
||||||
|
(restore-projects conn projects)
|
||||||
|
|
||||||
|
files))
|
||||||
|
|
||||||
(def ^:private schema:restore-deleted-team-files
|
(def ^:private schema:restore-deleted-team-files
|
||||||
[:map {:title "restore-deleted-team-files"}
|
[:map {:title "restore-deleted-team-files"}
|
||||||
@@ -1269,8 +1289,8 @@
|
|||||||
[:ids [::sm/set ::sm/uuid]]])
|
[:ids [::sm/set ::sm/uuid]]])
|
||||||
|
|
||||||
(sv/defmethod ::restore-deleted-team-files
|
(sv/defmethod ::restore-deleted-team-files
|
||||||
"Removes the deletion mark from the specified files (and respective projects)."
|
"Removes the deletion mark from the specified files (and respective
|
||||||
|
projects) on the specified team."
|
||||||
{::doc/added "2.12"
|
{::doc/added "2.12"
|
||||||
::sse/stream? true
|
::sse/stream? true
|
||||||
::sm/params schema:restore-deleted-team-files}
|
::sm/params schema:restore-deleted-team-files}
|
||||||
|
|||||||
@@ -169,12 +169,19 @@
|
|||||||
;; --- MUTATION: Create Project
|
;; --- MUTATION: Create Project
|
||||||
|
|
||||||
(defn- create-project
|
(defn- create-project
|
||||||
[{:keys [::db/conn] :as cfg} {:keys [profile-id team-id] :as params}]
|
[{:keys [::db/conn] :as cfg} {:keys [::rpc/request-at profile-id team-id] :as params}]
|
||||||
(let [project (teams/create-project conn params)]
|
(assert (ct/inst? request-at) "expect request-at assigned")
|
||||||
|
(let [params (-> params
|
||||||
|
(assoc :created-at request-at)
|
||||||
|
(assoc :modified-at request-at))
|
||||||
|
project (teams/create-project conn params)
|
||||||
|
timestamp (::rpc/request-at params)]
|
||||||
(teams/create-project-role conn profile-id (:id project) :owner)
|
(teams/create-project-role conn profile-id (:id project) :owner)
|
||||||
(db/insert! conn :team-project-profile-rel
|
(db/insert! conn :team-project-profile-rel
|
||||||
{:project-id (:id project)
|
{:project-id (:id project)
|
||||||
:profile-id profile-id
|
:profile-id profile-id
|
||||||
|
:created-at timestamp
|
||||||
|
:modified-at timestamp
|
||||||
:team-id team-id
|
:team-id team-id
|
||||||
:is-pinned false})
|
:is-pinned false})
|
||||||
(assoc project :is-pinned false)))
|
(assoc project :is-pinned false)))
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
[app.rpc.commands.files :as files]
|
[app.rpc.commands.files :as files]
|
||||||
[app.rpc.commands.files-create :as files.create]
|
[app.rpc.commands.files-create :as files.create]
|
||||||
[app.rpc.commands.files-update :as files.update]
|
[app.rpc.commands.files-update :as files.update]
|
||||||
|
[app.rpc.commands.projects :as projects]
|
||||||
[app.rpc.commands.teams :as teams]
|
[app.rpc.commands.teams :as teams]
|
||||||
[app.rpc.helpers :as rph]
|
[app.rpc.helpers :as rph]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
@@ -185,15 +186,17 @@
|
|||||||
(defn create-project*
|
(defn create-project*
|
||||||
([i params] (create-project* *system* i params))
|
([i params] (create-project* *system* i params))
|
||||||
([system i {:keys [profile-id team-id] :as params}]
|
([system i {:keys [profile-id team-id] :as params}]
|
||||||
(us/assert uuid? profile-id)
|
|
||||||
(us/assert uuid? team-id)
|
|
||||||
|
|
||||||
|
(assert (uuid? profile-id))
|
||||||
|
(assert (uuid? team-id))
|
||||||
|
(let [timestamp (ct/now)]
|
||||||
(db/run! system
|
(db/run! system
|
||||||
(fn [{:keys [::db/conn]}]
|
(fn [cfg]
|
||||||
(->> (merge {:id (mk-uuid "project" i)
|
(->> (merge {:id (mk-uuid "project" i)
|
||||||
:name (str "project" i)}
|
:name (str "project" i)}
|
||||||
params)
|
params
|
||||||
(#'teams/create-project conn))))))
|
{::rpc/request-at timestamp})
|
||||||
|
(#'projects/create-project cfg)))))))
|
||||||
|
|
||||||
(defn create-file*
|
(defn create-file*
|
||||||
([i params]
|
([i params]
|
||||||
|
|||||||
@@ -1925,7 +1925,7 @@
|
|||||||
(let [row (th/db-exec-one! ["select * from file where id = ?" file-id])]
|
(let [row (th/db-exec-one! ["select * from file where id = ?" file-id])]
|
||||||
(t/is (= (:deleted-at row) now)))))))
|
(t/is (= (:deleted-at row) now)))))))
|
||||||
|
|
||||||
(t/deftest deleted-files-restore
|
(t/deftest restore-deleted-files
|
||||||
(let [prof (th/create-profile* 1 {:is-active true})
|
(let [prof (th/create-profile* 1 {:is-active true})
|
||||||
team-id (:default-team-id prof)
|
team-id (:default-team-id prof)
|
||||||
proj-id (:default-project-id prof)
|
proj-id (:default-project-id prof)
|
||||||
@@ -1988,3 +1988,78 @@
|
|||||||
|
|
||||||
(let [row (th/db-exec-one! ["select * from file where id = ?" file-id])]
|
(let [row (th/db-exec-one! ["select * from file where id = ?" file-id])]
|
||||||
(t/is (nil? (:deleted-at row)))))))
|
(t/is (nil? (:deleted-at row)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(t/deftest restore-deleted-files-and-projets
|
||||||
|
(let [profile (th/create-profile* 1 {:is-active true})
|
||||||
|
team-id (:default-team-id profile)
|
||||||
|
now (ct/inst "2025-10-31T00:00:00Z")]
|
||||||
|
|
||||||
|
(binding [ct/*clock* (clock/fixed now)]
|
||||||
|
(let [project (th/create-project* 1 {:profile-id (:id profile)
|
||||||
|
:team-id team-id})
|
||||||
|
file (th/create-file* 1 {:profile-id (:id profile)
|
||||||
|
:project-id (:id project)})
|
||||||
|
|
||||||
|
data {::th/type :delete-project
|
||||||
|
:id (:id project)
|
||||||
|
::rpc/profile-id (:id profile)}
|
||||||
|
out (th/command! data)]
|
||||||
|
|
||||||
|
;; (th/print-result! out)
|
||||||
|
(t/is (nil? (:error out)))
|
||||||
|
(t/is (nil? (:result out)))
|
||||||
|
|
||||||
|
(th/run-pending-tasks!)
|
||||||
|
|
||||||
|
;; get deleted files
|
||||||
|
(let [data {::th/type :get-team-deleted-files
|
||||||
|
::rpc/profile-id (:id profile)
|
||||||
|
:team-id team-id}
|
||||||
|
out (th/command! data)]
|
||||||
|
;; (th/print-result! out)
|
||||||
|
(t/is (nil? (:error out)))
|
||||||
|
(let [[row1 :as result] (:result out)]
|
||||||
|
(t/is (= 1 (count result)))
|
||||||
|
(t/is (= (:will-be-deleted-at row1) #penpot/inst "2025-11-07T00:00:00Z"))
|
||||||
|
(t/is (= (:created-at row1) #penpot/inst "2025-10-31T00:00:00Z"))
|
||||||
|
(t/is (= (:modified-at row1) #penpot/inst "2025-10-31T00:00:00Z"))))
|
||||||
|
|
||||||
|
;; Check if project is deleted
|
||||||
|
(let [[row1 :as rows] (th/db-query :project {:id (:id project)})]
|
||||||
|
;; (pp/pprint rows)
|
||||||
|
(t/is (= 1 (count rows)))
|
||||||
|
(t/is (= (:deleted-at row1) #penpot/inst "2025-11-07T00:00:00Z"))
|
||||||
|
(t/is (= (:created-at row1) #penpot/inst "2025-10-31T00:00:00Z"))
|
||||||
|
(t/is (= (:modified-at row1) #penpot/inst "2025-10-31T00:00:00Z")))
|
||||||
|
|
||||||
|
;; Restore files
|
||||||
|
(let [data {::th/type :restore-deleted-team-files
|
||||||
|
::rpc/profile-id (:id profile)
|
||||||
|
:team-id team-id
|
||||||
|
:ids #{(:id file)}}
|
||||||
|
out (th/command! data)]
|
||||||
|
;; (th/print-result! out)
|
||||||
|
(t/is (nil? (:error out)))
|
||||||
|
(let [result (:result out)]
|
||||||
|
(t/is (fn? result))
|
||||||
|
(let [events (th/consume-sse result)]
|
||||||
|
;; (pp/pprint events)
|
||||||
|
(t/is (= 2 (count events)))
|
||||||
|
(t/is (= :end (first (last events))))
|
||||||
|
(t/is (= (:ids data) (last (last events)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(let [[row1 :as rows] (th/db-query :file {:project-id (:id project)})]
|
||||||
|
;; (pp/pprint rows)
|
||||||
|
(t/is (= 1 (count rows)))
|
||||||
|
(t/is (= (:created-at row1) #penpot/inst "2025-10-31T00:00:00Z"))
|
||||||
|
(t/is (nil? (:deleted-at row1))))
|
||||||
|
|
||||||
|
|
||||||
|
;; Check if project is restored
|
||||||
|
(let [[row1 :as rows] (th/db-query :project {:id (:id project)})]
|
||||||
|
;; (pp/pprint rows)
|
||||||
|
(t/is (= 1 (count rows)))
|
||||||
|
(t/is (= (:created-at row1) #penpot/inst "2025-10-31T00:00:00Z"))
|
||||||
|
(t/is (nil? (:deleted-at row1))))))))
|
||||||
|
|||||||
Reference in New Issue
Block a user