🐛 Fix on nitrate leave org default org team must be deleted if empty

This commit is contained in:
Pablo Alba 2026-04-16 11:45:37 +02:00 committed by GitHub
parent ac472c615a
commit 65a0fcb15b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 125 additions and 15 deletions

View File

@ -40,6 +40,13 @@
AND t.id = ANY(?)
AND t.deleted_at IS NULL")
(def ^:private sql:get-team-files-count
"SELECT count(*) AS total
FROM file AS f
JOIN project AS p ON (p.id = f.project_id)
WHERE p.team_id = ?
AND f.deleted_at IS NULL")
(def ^:private schema:leave-org
[:map
[:org-id ::sm/uuid]
@ -100,6 +107,21 @@
valid-teams-to-leave-ids (into valid-teams-to-transfer-ids valid-teams-to-exit-ids)
;; Get all the teams ids
all-teams-ids (into #{} d/xf:map-id (:teams org-summary))
;; Get all the ids of the teams that will be processed:
;; all the ids on teams-to-leave, teams-to-delete and default-team-id
selected-team-ids (-> (into #{default-team-id} teams-to-delete)
(into d/xf:map-id teams-to-leave))
;; Check that we are processing all the teams
all-teams-selected? (= all-teams-ids selected-team-ids)
default-team-files-count (-> (db/exec-one! conn [sql:get-team-files-count default-team-id])
:total)
delete-default-team? (= default-team-files-count 0)
;; for every team in teams-to-leave, check that:
;; - if it has a reassign-to, it belongs to valid-teams-to-transfer and
;; the reassign-to is a member of the team and not the current user;
@ -123,7 +145,8 @@
(when (or
(not valid-teams-to-delete?)
(not valid-teams-to-leave?)
(not valid-default-team-id?))
(not valid-default-team-id?)
(not all-teams-selected?))
(ex/raise :type :validation
:code :not-valid-teams))
@ -135,8 +158,12 @@
(doseq [{:keys [id reassign-to]} teams-to-leave]
(teams/leave-team cfg {:profile-id profile-id :id id :reassign-to reassign-to}))
;; Rename default-team-id
(db/exec! conn [sql:prefix-team-name-and-unset-default org-prefix default-team-id])
;; Delete default-team-id if empty; otherwise keep it and prefix the name.
(if delete-default-team?
(do
(db/update! conn :team {:is-default false} {:id default-team-id})
(teams/delete-team cfg {:profile-id profile-id :team-id default-team-id}))
(db/exec! conn [sql:prefix-team-name-and-unset-default org-prefix default-team-id]))
;; Api call to nitrate
(nitrate/call cfg :remove-profile-from-org {:profile-id profile-id :org-id org-id})

View File

@ -48,9 +48,15 @@
(let [profile-owner (th/create-profile* 1 {:is-active true})
profile-user (th/create-profile* 2 {:is-active true})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
project (th/create-project* 99 {:profile-id (:id profile-user)
:team-id (:id org-default-team)})
_ (th/create-file* 99 {:profile-id (:id profile-user)
:project-id (:id project)})
org-id (uuid/random)
;; The user's personal penpot team in the org context
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -78,14 +84,81 @@
(t/is (str/starts-with? (:name team) "[Test Org] "))
(t/is (false? (:is-default team))))))))
(t/deftest leave-org-deletes-org-default-team-when-empty
(let [profile-owner (th/create-profile* 1 {:is-active true})
profile-user (th/create-profile* 2 {:is-active true})
org-default-team (th/create-team* 98 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
:org-name "Test Org"
:owner-id (:id profile-owner)
:your-penpot-teams [your-penpot-id]
:org-teams [])]
(with-redefs [nitrate/call (nitrate-call-mock org-summary)]
(let [data {::th/type :leave-org
::rpc/profile-id (:id profile-user)
:org-id org-id
:default-team-id your-penpot-id
:teams-to-delete []
:teams-to-leave []}
out (th/command! data)]
(t/is (th/success? out))
;; Empty org default team should be soft-deleted.
(let [team (th/db-get :team {:id your-penpot-id} {::db/remove-deleted false})]
(t/is (some? (:deleted-at team))))))))
(t/deftest leave-org-keeps-and-renames-org-default-team-when-has-files
(let [profile-owner (th/create-profile* 1 {:is-active true})
profile-user (th/create-profile* 2 {:is-active true})
org-default-team (th/create-team* 97 {:profile-id (:id profile-user)})
project (th/create-project* 97 {:profile-id (:id profile-user)
:team-id (:id org-default-team)})
_ (th/create-file* 97 {:profile-id (:id profile-user)
:project-id (:id project)})
org-id (uuid/random)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
:org-name "Test Org"
:owner-id (:id profile-owner)
:your-penpot-teams [your-penpot-id]
:org-teams [])]
(with-redefs [nitrate/call (nitrate-call-mock org-summary)]
(let [data {::th/type :leave-org
::rpc/profile-id (:id profile-user)
:org-id org-id
:default-team-id your-penpot-id
:teams-to-delete []
:teams-to-leave []}
out (th/command! data)]
(t/is (th/success? out))
;; Non-empty org default team should remain and be renamed.
(let [team (th/db-get :team {:id your-penpot-id})]
(t/is (str/starts-with? (:name team) "[Test Org] "))
(t/is (false? (:is-default team)))
(t/is (nil? (:deleted-at team))))))))
(t/deftest leave-org-with-teams-to-delete
(let [profile-owner (th/create-profile* 1 {:is-active true})
profile-user (th/create-profile* 2 {:is-active true})
;; profile-user is the sole owner/member of team1
team1 (th/create-team* 1 {:profile-id (:id profile-user)})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -118,9 +191,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-owner)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -161,9 +235,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-user)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -196,8 +271,9 @@
(t/deftest leave-org-error-org-owner-cannot-leave
(let [profile-owner (th/create-profile* 1 {:is-active true})
org-default-team (th/create-team* 99 {:profile-id (:id profile-owner)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-owner)
your-penpot-id (:id org-default-team)
;; profile-owner IS the org owner in the org-summary
org-summary (make-org-summary
@ -223,8 +299,9 @@
(t/deftest leave-org-error-invalid-default-team-id
(let [profile-owner (th/create-profile* 1 {:is-active true})
profile-user (th/create-profile* 2 {:is-active true})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -253,9 +330,10 @@
;; profile-user is the sole owner/member of both team1 and team2
team1 (th/create-team* 1 {:profile-id (:id profile-user)})
team2 (th/create-team* 2 {:profile-id (:id profile-user)})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -286,9 +364,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-owner)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -319,9 +398,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-owner)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -351,9 +431,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-owner)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -385,9 +466,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-owner)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id
@ -418,9 +500,10 @@
_ (th/create-team-role* {:team-id (:id team1)
:profile-id (:id profile-user)
:role :editor})
org-default-team (th/create-team* 99 {:profile-id (:id profile-user)})
org-id (uuid/random)
your-penpot-id (:default-team-id profile-user)
your-penpot-id (:id org-default-team)
org-summary (make-org-summary
:org-id org-id