diff --git a/frontend_tests/node_tests/lib/events.js b/frontend_tests/node_tests/lib/events.js index 98d3a26e38..a5d412033d 100644 --- a/frontend_tests/node_tests/lib/events.js +++ b/frontend_tests/node_tests/lib/events.js @@ -721,6 +721,7 @@ exports.fixtures = { description: "mobile folks", members: [1], is_system_group: false, + subgroups: [2], }, }, diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md index a889aeb491..f690aa4f12 100644 --- a/templates/zerver/api/changelog.md +++ b/templates/zerver/api/changelog.md @@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with. ## Changes in Zulip 6.0 +**Feature level 127** + +* [`GET /user_groups`](/api/get-user-groups),[`POST + /register`](/api/register-queue): Added `subgroups` field, + which is a list of IDs of all the subgroups of the user group, to + user group objects. + **Feature level 126** * `POST /invites`, `POST /invites/multiuse`: Replaced `invite_expires_in_days` diff --git a/zerver/actions/user_groups.py b/zerver/actions/user_groups.py index 8373e682a3..2aedb106be 100644 --- a/zerver/actions/user_groups.py +++ b/zerver/actions/user_groups.py @@ -81,7 +81,9 @@ def promote_new_full_members() -> None: update_users_in_full_members_system_group(realm) -def do_send_create_user_group_event(user_group: UserGroup, members: List[UserProfile]) -> None: +def do_send_create_user_group_event( + user_group: UserGroup, members: List[UserProfile], subgroups: Sequence[UserGroup] = [] +) -> None: event = dict( type="user_group", op="add", @@ -91,6 +93,7 @@ def do_send_create_user_group_event(user_group: UserGroup, members: List[UserPro description=user_group.description, id=user_group.id, is_system_group=user_group.is_system_group, + subgroups=[subgroup.id for subgroup in subgroups], ), ) send_event(user_group.realm, event, active_user_ids(user_group.realm_id)) diff --git a/zerver/lib/event_schema.py b/zerver/lib/event_schema.py index ce0c21c044..1e98865a78 100644 --- a/zerver/lib/event_schema.py +++ b/zerver/lib/event_schema.py @@ -1676,6 +1676,7 @@ group_type = DictType( ("id", int), ("name", str), ("members", ListType(int)), + ("subgroups", ListType(int)), ("description", str), ("is_system_group", bool), ] diff --git a/zerver/lib/user_groups.py b/zerver/lib/user_groups.py index eedf1ea7d2..ec44b38c52 100644 --- a/zerver/lib/user_groups.py +++ b/zerver/lib/user_groups.py @@ -40,6 +40,7 @@ def user_groups_in_realm_serialized(realm: Realm) -> List[Dict[str, Any]]: name=user_group.name, description=user_group.description, members=[], + subgroups=[], is_system_group=user_group.is_system_group, ) @@ -48,8 +49,16 @@ def user_groups_in_realm_serialized(realm: Realm) -> List[Dict[str, Any]]: ) for (user_group_id, user_profile_id) in membership: group_dicts[user_group_id]["members"].append(user_profile_id) + + group_membership = GroupGroupMembership.objects.filter(subgroup__realm=realm).values_list( + "subgroup_id", "supergroup_id" + ) + for (subgroup_id, supergroup_id) in group_membership: + group_dicts[supergroup_id]["subgroups"].append(subgroup_id) + for group_dict in group_dicts.values(): group_dict["members"] = sorted(group_dict["members"]) + group_dict["subgroups"] = sorted(group_dict["subgroups"]) return sorted(group_dicts.values(), key=lambda group_dict: group_dict["id"]) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 0667c07769..1c61a21900 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -13807,6 +13807,14 @@ paths: The integer user IDs of the user group members. items: type: integer + subgroups: + type: array + description: | + The integer user group IDs of the subgroups. + + **Changes**: New in Zulip 6.0 (feature level 127). + items: + type: integer name: type: string description: | @@ -13832,6 +13840,7 @@ paths: "id": 1, "name": "hamletcharacters", "members": [3, 4], + "subgroups": [], "is_system_group": false, }, { @@ -13839,6 +13848,7 @@ paths: "id": 2, "name": "other users", "members": [1, 2], + "subgroups": [1, 2], "is_system_group": true, }, ], @@ -14650,6 +14660,15 @@ components: description: | Array containing the id of the users who are members of this user group. + subgroups: + type: array + items: + type: integer + description: | + Array containing the id of the subgroups of this + user group. + + **Changes**: New in Zulip 6.0 (feature level 127). id: type: integer description: | diff --git a/zerver/tests/test_event_system.py b/zerver/tests/test_event_system.py index 433caa2f0a..4ce2fd0e8d 100644 --- a/zerver/tests/test_event_system.py +++ b/zerver/tests/test_event_system.py @@ -1004,7 +1004,7 @@ class FetchQueriesTest(ZulipTestCase): with mock.patch("zerver.lib.events.always_want") as want_mock: fetch_initial_state_data(user) - self.assert_length(queries, 35) + self.assert_length(queries, 36) expected_counts = dict( alert_words=1, @@ -1027,7 +1027,7 @@ class FetchQueriesTest(ZulipTestCase): realm_linkifiers=1, realm_playgrounds=1, realm_user=3, - realm_user_groups=2, + realm_user_groups=3, realm_user_settings_defaults=1, recent_private_conversations=1, starred_messages=1, diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 5808171f0e..5edc3d9c6f 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -247,7 +247,7 @@ class HomeTest(ZulipTestCase): set(result["Cache-Control"].split(", ")), {"must-revalidate", "no-store", "no-cache"} ) - self.assert_length(queries, 44) + self.assert_length(queries, 45) self.assert_length(cache_mock.call_args_list, 5) html = result.content.decode() @@ -420,7 +420,7 @@ class HomeTest(ZulipTestCase): result = self._get_home_page() self.check_rendered_logged_in_app(result) self.assert_length(cache_mock.call_args_list, 6) - self.assert_length(queries, 41) + self.assert_length(queries, 42) def test_num_queries_with_streams(self) -> None: main_user = self.example_user("hamlet") @@ -451,7 +451,7 @@ class HomeTest(ZulipTestCase): with queries_captured() as queries2: result = self._get_home_page() - self.assert_length(queries2, 39) + self.assert_length(queries2, 40) # Do a sanity check that our new streams were in the payload. html = result.content.decode() diff --git a/zerver/tests/test_user_groups.py b/zerver/tests/test_user_groups.py index db277b4a59..c5c55b170a 100644 --- a/zerver/tests/test_user_groups.py +++ b/zerver/tests/test_user_groups.py @@ -50,6 +50,12 @@ class UserGroupTestCase(ZulipTestCase): self.assertEqual(user_groups[0]["name"], "@role:owners") self.assertEqual(user_groups[0]["description"], "Owners of this organization") self.assertEqual(set(user_groups[0]["members"]), set(membership)) + self.assertEqual(user_groups[0]["subgroups"], []) + + admins_system_group = UserGroup.objects.get(name="@role:administrators", realm=realm) + self.assertEqual(user_groups[1]["id"], admins_system_group.id) + # Check that owners system group is present in "subgroups" + self.assertEqual(user_groups[1]["subgroups"], [user_group.id]) self.assertEqual(user_groups[8]["id"], empty_user_group.id) self.assertEqual(user_groups[8]["name"], "newgroup")