diff --git a/backend/src/app/rpc/commands/nitrate.clj b/backend/src/app/rpc/commands/nitrate.clj index d16bd8cd22..b5a4c6064d 100644 --- a/backend/src/app/rpc/commands/nitrate.clj +++ b/backend/src/app/rpc/commands/nitrate.clj @@ -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}) diff --git a/backend/test/backend_tests/rpc_nitrate_test.clj b/backend/test/backend_tests/rpc_nitrate_test.clj index 0a0c35296f..d098013aa5 100644 --- a/backend/test/backend_tests/rpc_nitrate_test.clj +++ b/backend/test/backend_tests/rpc_nitrate_test.clj @@ -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