diff --git a/apps/wallet/src/components/requests/operations/RemoveUserGroupOperation.vue b/apps/wallet/src/components/requests/operations/RemoveUserGroupOperation.vue
index bc72404e8..c2a771b80 100644
--- a/apps/wallet/src/components/requests/operations/RemoveUserGroupOperation.vue
+++ b/apps/wallet/src/components/requests/operations/RemoveUserGroupOperation.vue
@@ -8,15 +8,24 @@
-
+
+
+
+
diff --git a/apps/wallet/src/locales/en.locale.ts b/apps/wallet/src/locales/en.locale.ts
index f8b25e398..ebd0af182 100644
--- a/apps/wallet/src/locales/en.locale.ts
+++ b/apps/wallet/src/locales/en.locale.ts
@@ -894,6 +894,14 @@ export default {
external_canisters: 'Canisters',
approval_rules: 'Approval Rules',
},
+ user_groups: {
+ members_removal_warning:
+ 'Removing this group will affect user {users} who is still a member of it. | Removing this group will affect users {users} who are still members of it.',
+ members_removal_warning_more:
+ 'Removing this group will affect users {users} and {count} more who are still members of it.',
+ members_removal_warning_unknown:
+ 'Removing this group will also remove it from any users who are still members of it.',
+ },
pages: {
dashboard: {
title: 'Dashboard',
diff --git a/apps/wallet/src/locales/fr.locale.ts b/apps/wallet/src/locales/fr.locale.ts
index dc05ebf91..20aff362c 100644
--- a/apps/wallet/src/locales/fr.locale.ts
+++ b/apps/wallet/src/locales/fr.locale.ts
@@ -904,6 +904,14 @@ export default {
external_canisters: 'Canisters',
approval_rules: 'Règles d approbation',
},
+ user_groups: {
+ members_removal_warning:
+ "La suppression de ce groupe affectera l'usager {users} qui en est encore membre. | La suppression de ce groupe affectera les usagers {users} qui en sont encore membres.",
+ members_removal_warning_more:
+ 'La suppression de ce groupe affectera les usagers {users} et {count} autre(s) qui en sont encore membres.',
+ members_removal_warning_unknown:
+ 'La suppression de ce groupe le retirera également de tous les usagers qui en sont encore membres.',
+ },
pages: {
dashboard: {
title: 'Tableau de Bord',
diff --git a/apps/wallet/src/locales/pt.locale.ts b/apps/wallet/src/locales/pt.locale.ts
index 896d83671..b703c6d49 100644
--- a/apps/wallet/src/locales/pt.locale.ts
+++ b/apps/wallet/src/locales/pt.locale.ts
@@ -900,6 +900,14 @@ export default {
external_canisters: 'Canisters',
approval_rules: 'Regras de aprovação',
},
+ user_groups: {
+ members_removal_warning:
+ 'Remover este grupo afetará o usuário {users}, que ainda é membro dele. | Remover este grupo afetará os usuários {users}, que ainda são membros dele.',
+ members_removal_warning_more:
+ 'Remover este grupo afetará os usuários {users} e mais {count}, que ainda são membros dele.',
+ members_removal_warning_unknown:
+ 'Remover este grupo também o removerá de todos os usuários que ainda são membros dele.',
+ },
pages: {
dashboard: {
title: 'Dashboard',
diff --git a/apps/wallet/src/pages/UserGroupsPage.vue b/apps/wallet/src/pages/UserGroupsPage.vue
index 797dbc536..1ace00cdc 100644
--- a/apps/wallet/src/pages/UserGroupsPage.vue
+++ b/apps/wallet/src/pages/UserGroupsPage.vue
@@ -76,7 +76,10 @@
:submit="id => station.service.removeUserGroup({ user_group_id: id })"
@failed="useOnFailedOperation"
@submitted="useOnSuccessfulOperation"
- />
+ >
+ {{ $t('app.dialog_confirmation_question') }}
+
+
,
user_group_repository: Arc,
+ user_repository: Arc,
}
impl UserGroupService {
@@ -155,6 +158,21 @@ impl UserGroupService {
}
}
+ // Removes the group from all of its members, so no user record is left referencing a
+ // group that no longer exists.
+ let members = self.user_repository.find_where(UserWhereClause {
+ search_term: None,
+ statuses: None,
+ groups: Some(vec![user_group.id]),
+ });
+
+ for mut user in members {
+ user.groups.retain(|group_id| *group_id != user_group.id);
+ user.last_modification_timestamp = next_time();
+
+ self.user_repository.insert(user.to_key(), user);
+ }
+
self.user_group_repository.remove(&user_group.id);
Ok(())
@@ -165,8 +183,10 @@ impl UserGroupService {
mod test {
use crate::{
core::write_system_info,
- models::{AddUserGroupOperationInput, SystemInfo},
+ models::{user_test_utils::mock_user, AddUserGroupOperationInput, SystemInfo, UserStatus},
+ repositories::USER_REPOSITORY,
};
+ use orbit_essentials::repository::Repository;
use super::USER_GROUP_SERVICE;
@@ -198,4 +218,49 @@ mod test {
assert_eq!(error.code, "CANNOT_DELETE_DISASTER_RECOVERY_COMMITTEE");
}
+
+ #[tokio::test]
+ async fn test_deleting_user_group_removes_it_from_members() {
+ let user_group = USER_GROUP_SERVICE
+ .create(AddUserGroupOperationInput {
+ name: "Test".to_string(),
+ })
+ .await
+ .expect("Failed to create user group");
+ let other_group = USER_GROUP_SERVICE
+ .create(AddUserGroupOperationInput {
+ name: "Other".to_string(),
+ })
+ .await
+ .expect("Failed to create user group");
+
+ write_system_info(SystemInfo::default());
+
+ // Inactive members are cleaned up as well.
+ let mut active_member = mock_user();
+ active_member.groups = vec![user_group.id, other_group.id];
+ USER_REPOSITORY.insert(active_member.to_key(), active_member.to_owned());
+
+ let mut inactive_member = mock_user();
+ inactive_member.groups = vec![user_group.id];
+ inactive_member.status = UserStatus::Inactive;
+ USER_REPOSITORY.insert(inactive_member.to_key(), inactive_member.to_owned());
+
+ USER_GROUP_SERVICE
+ .remove(&user_group.id)
+ .await
+ .expect("Failed to remove user group");
+
+ let active_member = USER_REPOSITORY.get(&active_member.to_key()).unwrap();
+ let inactive_member = USER_REPOSITORY.get(&inactive_member.to_key()).unwrap();
+
+ assert_eq!(active_member.groups, vec![other_group.id]);
+ assert!(inactive_member.groups.is_empty());
+
+ for status in [UserStatus::Active, UserStatus::Inactive] {
+ assert!(USER_REPOSITORY
+ .find_by_group_and_status(&user_group.id, &status)
+ .is_empty());
+ }
+ }
}