From 7e44ae62a272c8ed45d8bb9e8ad04bf2172d793c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 27 Jun 2024 11:18:58 +0200 Subject: [PATCH 1/2] :bug: Fix profile deletion issue with 1 participant --- backend/src/app/rpc/commands/profile.clj | 22 +-- backend/src/app/tasks/orphan_teams_gc.clj | 1 - .../test/backend_tests/rpc_profile_test.clj | 157 +++++++++++++++++- 3 files changed, 167 insertions(+), 13 deletions(-) diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index fe33da10d..5bdf6943c 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -400,18 +400,18 @@ ;; --- HELPERS (def sql:owned-teams - "with owner_teams as ( - select tpr.team_id as id - from team_profile_rel as tpr - where tpr.is_owner is true - and tpr.profile_id = ? + "WITH owner_teams AS ( + SELECT tpr.team_id AS id + FROM team_profile_rel AS tpr + WHERE tpr.is_owner IS TRUE + AND tpr.profile_id = ? ) - select tpr.team_id as id, - count(tpr.profile_id) - 1 as participants - from team_profile_rel as tpr - where tpr.team_id in (select id from owner_teams) - and tpr.profile_id != ? - group by 1") + SELECT tpr.team_id AS id, + count(tpr.profile_id) AS participants + FROM team_profile_rel AS tpr + WHERE tpr.team_id IN (SELECT id from owner_teams) + AND tpr.profile_id != ? + GROUP BY 1") (defn- get-owned-teams-with-participants [conn profile-id] diff --git a/backend/src/app/tasks/orphan_teams_gc.clj b/backend/src/app/tasks/orphan_teams_gc.clj index c04123a83..bbc267e57 100644 --- a/backend/src/app/tasks/orphan_teams_gc.clj +++ b/backend/src/app/tasks/orphan_teams_gc.clj @@ -22,7 +22,6 @@ [_ cfg] (fn [params] (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] - (l/inf :hint "gc started" :rollback? (boolean (:rollback? params))) (let [total (delete-orphan-teams! cfg)] (l/inf :hint "task finished" :teams total diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index 00d2f2ac0..d52a0bac9 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -126,7 +126,7 @@ ;; (th/print-result! out) (t/is (nil? (:error out))))))) -(t/deftest profile-deletion-simple +(t/deftest profile-deletion-1 (let [prof (th/create-profile* 1) file (th/create-file* 1 {:profile-id (:id prof) :project-id (:default-project-id prof) @@ -177,6 +177,161 @@ (let [result (:result out)] (t/is (= uuid/zero (:id result))))))) + +(t/deftest profile-deletion-2 + (let [prof1 (th/create-profile* 1) + prof2 (th/create-profile* 2) + file1 (th/create-file* 1 {:profile-id (:id prof1) + :project-id (:default-project-id prof1) + :is-shared false}) + team1 (th/create-team* 1 {:profile-id (:id prof1)}) + + role1 (th/create-team-role* {:team-id (:id team1) + :profile-id (:id prof2) + + :role :editor})] + ;; Assert all roles for team + (let [roles (th/db-query :team-profile-rel {:team-id (:id team1)})] + (t/is (= 2 (count roles)))) + + ;; Request profile to be deleted + (let [params {::th/type :delete-profile + ::rpc/profile-id (:id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + + (let [error (:error out) + edata (ex-data error)] + (t/is (th/ex-info? error)) + (t/is (= (:type edata) :validation)) + (t/is (= (:code edata) :owner-teams-with-people)))))) + +(t/deftest profile-deletion-3 + (let [prof1 (th/create-profile* 1) + prof2 (th/create-profile* 2) + prof3 (th/create-profile* 3) + file1 (th/create-file* 1 {:profile-id (:id prof1) + :project-id (:default-project-id prof1) + :is-shared false}) + team1 (th/create-team* 1 {:profile-id (:id prof1)}) + + role1 (th/create-team-role* {:team-id (:id team1) + :profile-id (:id prof2) + :role :editor}) + role2 (th/create-team-role* {:team-id (:id team1) + :profile-id (:id prof3) + :role :editor})] + + ;; Assert all roles for team + (let [roles (th/db-query :team-profile-rel {:team-id (:id team1)})] + (t/is (= 3 (count roles)))) + + ;; Request profile to be deleted (it should fail) + (let [params {::th/type :delete-profile + ::rpc/profile-id (:id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + + (let [error (:error out) + edata (ex-data error)] + (t/is (th/ex-info? error)) + (t/is (= (:type edata) :validation)) + (t/is (= (:code edata) :owner-teams-with-people)))) + + ;; Leave team by role 1 + (let [params {::th/type :leave-team + ::rpc/profile-id (:id prof2) + :id (:id team1)} + out (th/command! params)] + + ;; (th/print-result! out) + (t/is (nil? (:result out))) + (t/is (nil? (:error out)))) + + ;; Request profile to be deleted (it should fail) + (let [params {::th/type :delete-profile + ::rpc/profile-id (:id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + (let [error (:error out) + edata (ex-data error)] + (t/is (th/ex-info? error)) + (t/is (= (:type edata) :validation)) + (t/is (= (:code edata) :owner-teams-with-people)))) + + + ;; Leave team by role 0 (the default) and reassing owner to role 3 + ;; without reassinging it (should fail) + (let [params {::th/type :leave-team + ::rpc/profile-id (:id prof1) + ;; :reassign-to (:id prof3) + :id (:id team1)} + out (th/command! params)] + + ;; (th/print-result! out) + + (let [error (:error out) + edata (ex-data error)] + (t/is (th/ex-info? error)) + (t/is (= (:type edata) :validation)) + (t/is (= (:code edata) :owner-cant-leave-team)))) + + ;; Leave team by role 0 (the default) and reassing owner to role 3 + (let [params {::th/type :leave-team + ::rpc/profile-id (:id prof1) + :reassign-to (:id prof3) + :id (:id team1)} + out (th/command! params)] + + ;; (th/print-result! out) + (t/is (nil? (:result out))) + (t/is (nil? (:error out)))) + + ;; Request profile to be deleted (it should fail) + (let [params {::th/type :delete-profile + ::rpc/profile-id (:id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + + (t/is (= {} (:result out))) + (t/is (nil? (:error out)))) + + ;; query files after profile soft deletion + (let [params {::th/type :get-project-files + ::rpc/profile-id (:id prof1) + :project-id (:default-project-id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + (t/is (nil? (:error out))) + (t/is (= 1 (count (:result out))))) + + ;; execute permanent deletion task + (let [result (th/run-task! :objects-gc {:min-age 0})] + (t/is (= 1 (:processed result)))) + + (let [row (th/db-get :team + {:id (:default-team-id prof1)} + {::db/remove-deleted false})] + (t/is (nil? (:deleted-at row)))) + + (let [result (th/run-task! :orphan-teams-gc {:min-age 0})] + (t/is (= 1 (:processed result)))) + + (let [row (th/db-get :team + {:id (:default-team-id prof1)} + {::db/remove-deleted false})] + (t/is (dt/instant? (:deleted-at row)))) + + ;; query profile after delete + (let [params {::th/type :get-profile + ::rpc/profile-id (:id prof1)} + out (th/command! params)] + ;; (th/print-result! out) + (let [result (:result out)] + (t/is (= uuid/zero (:id result))))))) + + + (t/deftest registration-domain-whitelist (let [whitelist #{"gmail.com" "hey.com" "ya.ru"}] (t/testing "allowed email domain" From 2cddbc8a3de54ef0659c64e8874ed19d13d528dd Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 27 Jun 2024 12:13:12 +0200 Subject: [PATCH 2/2] :bug: Fix error handling on account deletion process --- frontend/src/app/main/data/users.cljs | 6 ++---- frontend/src/app/main/ui/settings/delete_account.cljs | 11 ++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index 8a540317f..e82fee08f 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -537,10 +537,8 @@ on-success identity}} (meta params)] (->> (rp/cmd! :delete-profile {}) (rx/tap on-success) - (rx/delay-at-least 300) - (rx/catch (constantly (rx/of 1))) - (rx/map logged-out) - (rx/catch on-error)))))) + (rx/catch on-error) + (rx/delay-at-least 300)))))) ;; --- EVENT: request-profile-recovery diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index 384907dfe..3dd81a34b 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -18,11 +18,12 @@ [rumext.v2 :as mf])) (defn on-error - [{:keys [code] :as error}] - (if (= :owner-teams-with-people code) - (let [msg (tr "notifications.profile-deletion-not-allowed")] - (rx/of (msg/error msg))) - (rx/throw error))) + [cause] + (let [code (-> cause ex-data :code)] + (if (= :owner-teams-with-people code) + (let [msg (tr "notifications.profile-deletion-not-allowed")] + (rx/of (msg/error msg))) + (rx/throw cause)))) (mf/defc delete-account-modal {::mf/register modal/components