diff --git a/api_docs/changelog.md b/api_docs/changelog.md index 424a679c50..90e791731b 100644 --- a/api_docs/changelog.md +++ b/api_docs/changelog.md @@ -29,6 +29,8 @@ format used by the Zulip server that they are interacting with. the user group objects to identify deactivated user groups. * [`GET /events`](/api/get-events): When a user group is deactivated, a `user_group` event with `op=update` is sent to clients. +* [`GET /user_groups`](/api/get-user-groups): Added support for + excluding deactivated user groups from the response. **Feature level 289** diff --git a/version.py b/version.py index 95b21659f6..3a19db9b2b 100644 --- a/version.py +++ b/version.py @@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3" # new level means in api_docs/changelog.md, as well as "**Changes**" # entries in the endpoint's documentation in `zulip.yaml`. -API_FEATURE_LEVEL = 289 # return ID as key while subscribing to a stream. +API_FEATURE_LEVEL = 290 # Last bumped for UserGroup.deactivated # Bump the minor PROVISION_VERSION to indicate that folks should provision # only when going from an old version of the code to a newer version. Bump diff --git a/zerver/lib/events.py b/zerver/lib/events.py index 2a7c28f35c..fc4cebd27b 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -513,7 +513,7 @@ def fetch_initial_state_data( state["realm_playgrounds"] = get_realm_playgrounds(realm) if want("realm_user_groups"): - state["realm_user_groups"] = user_groups_in_realm_serialized(realm) + state["realm_user_groups"] = user_groups_in_realm_serialized(realm, allow_deactivated=True) if user_profile is not None: settings_user = user_profile diff --git a/zerver/lib/user_groups.py b/zerver/lib/user_groups.py index fdc4afc647..8f0cf64f50 100644 --- a/zerver/lib/user_groups.py +++ b/zerver/lib/user_groups.py @@ -472,7 +472,9 @@ def get_setting_value_for_user_group_object( ) -def user_groups_in_realm_serialized(realm: Realm) -> list[UserGroupDict]: +def user_groups_in_realm_serialized( + realm: Realm, *, allow_deactivated: bool +) -> list[UserGroupDict]: """This function is used in do_events_register code path so this code should be performant. We need to do 2 database queries because Django's ORM doesn't properly support the left join between @@ -485,6 +487,9 @@ def user_groups_in_realm_serialized(realm: Realm) -> list[UserGroupDict]: "can_mention_group__named_user_group", ).filter(realm=realm) + if not allow_deactivated: + realm_groups = realm_groups.filter(deactivated=False) + membership = UserGroupMembership.objects.filter(user_group__realm=realm).values_list( "user_group_id", "user_profile_id" ) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 65ffd0737a..22933eb9ce 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -19967,6 +19967,26 @@ paths: **Note**: This endpoint is only available to [members and administrators](/help/roles-and-permissions); bots and guests cannot use this endpoint. + requestBody: + required: false + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + allow_deactivated: + description: | + Whether to include deactivated user groups in the response. + + **Changes**: New in Zulip 10.0 (feature level 290). Previously, + deactivated user groups did not exist and thus would never be + included in the response. + type: boolean + example: true + default: false + encoding: + allow_deactivated: + contentType: application/json responses: "200": description: Success. diff --git a/zerver/tests/test_user_groups.py b/zerver/tests/test_user_groups.py index 0531bcf678..8dac697d7d 100644 --- a/zerver/tests/test_user_groups.py +++ b/zerver/tests/test_user_groups.py @@ -84,7 +84,7 @@ class UserGroupTestCase(ZulipTestCase): assert user_group is not None empty_user_group = check_add_user_group(realm, "newgroup", [], acting_user=None) - user_groups = user_groups_in_realm_serialized(realm) + user_groups = user_groups_in_realm_serialized(realm, allow_deactivated=False) self.assert_length(user_groups, 10) self.assertEqual(user_groups[0]["id"], user_group.id) self.assertEqual(user_groups[0]["name"], SystemGroups.NOBODY) @@ -141,7 +141,7 @@ class UserGroupTestCase(ZulipTestCase): }, acting_user=None, ) - user_groups = user_groups_in_realm_serialized(realm) + user_groups = user_groups_in_realm_serialized(realm, allow_deactivated=False) self.assertEqual(user_groups[10]["id"], new_user_group.id) self.assertEqual(user_groups[10]["name"], "newgroup2") self.assertEqual(user_groups[10]["description"], "") @@ -162,10 +162,33 @@ class UserGroupTestCase(ZulipTestCase): ) self.assertFalse(user_groups[0]["deactivated"]) - do_deactivate_user_group(new_user_group, acting_user=None) - user_groups = user_groups_in_realm_serialized(realm) + another_new_group = check_add_user_group( + realm, "newgroup3", [self.example_user("hamlet")], acting_user=None + ) + add_subgroups_to_user_group( + new_user_group, [another_new_group, owners_system_group], acting_user=None + ) + do_deactivate_user_group(another_new_group, acting_user=None) + user_groups = user_groups_in_realm_serialized(realm, allow_deactivated=True) + self.assert_length(user_groups, 12) self.assertEqual(user_groups[10]["id"], new_user_group.id) - self.assertTrue(user_groups[10]["deactivated"]) + self.assertEqual(user_groups[10]["name"], "newgroup2") + self.assertFalse(user_groups[10]["deactivated"]) + self.assertCountEqual( + user_groups[10]["direct_subgroup_ids"], [another_new_group.id, owners_system_group.id] + ) + self.assertEqual(user_groups[11]["id"], another_new_group.id) + self.assertEqual(user_groups[11]["name"], "newgroup3") + self.assertTrue(user_groups[11]["deactivated"]) + + user_groups = user_groups_in_realm_serialized(realm, allow_deactivated=False) + self.assert_length(user_groups, 11) + self.assertEqual(user_groups[10]["id"], new_user_group.id) + self.assertEqual(user_groups[10]["name"], "newgroup2") + self.assertFalse(user_groups[10]["deactivated"]) + self.assertCountEqual( + user_groups[10]["direct_subgroup_ids"], [another_new_group.id, owners_system_group.id] + ) def test_get_direct_user_groups(self) -> None: othello = self.example_user("othello") diff --git a/zerver/views/user_groups.py b/zerver/views/user_groups.py index bbbbccac51..84862f64bb 100644 --- a/zerver/views/user_groups.py +++ b/zerver/views/user_groups.py @@ -22,7 +22,7 @@ from zerver.decorator import require_member_or_admin, require_user_group_create_ from zerver.lib.exceptions import JsonableError from zerver.lib.mention import MentionBackend, silent_mention_syntax_for_user from zerver.lib.response import json_success -from zerver.lib.typed_endpoint import PathOnly, typed_endpoint, typed_endpoint_without_parameters +from zerver.lib.typed_endpoint import PathOnly, typed_endpoint from zerver.lib.user_groups import ( AnonymousSettingGroupDict, GroupSettingChangeRequest, @@ -93,9 +93,16 @@ def add_user_group( @require_member_or_admin -@typed_endpoint_without_parameters -def get_user_group(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: - user_groups = user_groups_in_realm_serialized(user_profile.realm) +@typed_endpoint +def get_user_group( + request: HttpRequest, + user_profile: UserProfile, + *, + allow_deactivated: Json[bool] = False, +) -> HttpResponse: + user_groups = user_groups_in_realm_serialized( + user_profile.realm, allow_deactivated=allow_deactivated + ) return json_success(request, data={"user_groups": user_groups})