mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	user_groups: Add API endpoint to get subgroups of a user group.
This commit is contained in:
		@@ -36,6 +36,8 @@ format used by the Zulip server that they are interacting with.
 | 
				
			|||||||
  given user group.
 | 
					  given user group.
 | 
				
			||||||
* [`GET /user_groups/{user_group_id}/members`](/api/get-user-group-members):
 | 
					* [`GET /user_groups/{user_group_id}/members`](/api/get-user-group-members):
 | 
				
			||||||
  Added new endpoint to get members of a user group.
 | 
					  Added new endpoint to get members of a user group.
 | 
				
			||||||
 | 
					* [`GET /user_groups/{user_group_id}/members`](/api/get-user-group-subgroups):
 | 
				
			||||||
 | 
					  Added new endpoint to get subgroups of a user group.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Feature level 126**
 | 
					**Feature level 126**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,7 @@
 | 
				
			|||||||
* [Update user group subgroups](/api/update-user-group-subgroups)
 | 
					* [Update user group subgroups](/api/update-user-group-subgroups)
 | 
				
			||||||
* [Get user group membership status](/api/get-is-user-group-member)
 | 
					* [Get user group membership status](/api/get-is-user-group-member)
 | 
				
			||||||
* [Get user group members](/api/get-user-group-members)
 | 
					* [Get user group members](/api/get-user-group-members)
 | 
				
			||||||
 | 
					* [Get subgroups of user group](/api/get-user-group-subgroups)
 | 
				
			||||||
* [Mute a user](/api/mute-user)
 | 
					* [Mute a user](/api/mute-user)
 | 
				
			||||||
* [Unmute a user](/api/unmute-user)
 | 
					* [Unmute a user](/api/unmute-user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
 | 
				
			|||||||
# Changes should be accompanied by documentation explaining what the
 | 
					# Changes should be accompanied by documentation explaining what the
 | 
				
			||||||
# new level means in templates/zerver/api/changelog.md, as well as
 | 
					# new level means in templates/zerver/api/changelog.md, as well as
 | 
				
			||||||
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
 | 
					# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
 | 
				
			||||||
API_FEATURE_LEVEL = 126
 | 
					API_FEATURE_LEVEL = 127
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
 | 
					# 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
 | 
					# only when going from an old version of the code to a newer version. Bump
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -179,6 +179,19 @@ def get_user_group_member_ids(
 | 
				
			|||||||
    return list(member_ids)
 | 
					    return list(member_ids)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_subgroup_ids(user_group: UserGroup, *, direct_subgroup_only: bool = False) -> List[int]:
 | 
				
			||||||
 | 
					    if direct_subgroup_only:
 | 
				
			||||||
 | 
					        subgroup_ids = user_group.direct_subgroups.all().values_list("id", flat=True)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        subgroup_ids = (
 | 
				
			||||||
 | 
					            get_recursive_subgroups(user_group)
 | 
				
			||||||
 | 
					            .exclude(id=user_group.id)
 | 
				
			||||||
 | 
					            .values_list("id", flat=True)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return list(subgroup_ids)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
 | 
					def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
 | 
				
			||||||
    """Any changes to this function likely require a migration to adjust
 | 
					    """Any changes to this function likely require a migration to adjust
 | 
				
			||||||
    existing realms.  See e.g. migration 0375_create_role_based_system_groups.py,
 | 
					    existing realms.  See e.g. migration 0375_create_role_based_system_groups.py,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14009,6 +14009,49 @@ paths:
 | 
				
			|||||||
      responses:
 | 
					      responses:
 | 
				
			||||||
        "200":
 | 
					        "200":
 | 
				
			||||||
          $ref: "#/components/responses/SimpleSuccess"
 | 
					          $ref: "#/components/responses/SimpleSuccess"
 | 
				
			||||||
 | 
					    get:
 | 
				
			||||||
 | 
					      operationId: get-user-group-subgroups
 | 
				
			||||||
 | 
					      summary: Get subgroups of the user group
 | 
				
			||||||
 | 
					      tags: ["users"]
 | 
				
			||||||
 | 
					      description: |
 | 
				
			||||||
 | 
					        Get the subgroups of a [user group](/help/user-groups).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        `GET {{ api_url }}/v1/user_groups/{user_group_id}/subgroups`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        **Changes**: New in Zulip 6.0 (feature level 127).
 | 
				
			||||||
 | 
					      parameters:
 | 
				
			||||||
 | 
					        - $ref: "#/components/parameters/UserGroupId"
 | 
				
			||||||
 | 
					        - name: direct_subgroup_only
 | 
				
			||||||
 | 
					          in: query
 | 
				
			||||||
 | 
					          description: |
 | 
				
			||||||
 | 
					            Whether to consider only direct subgroups of the user group
 | 
				
			||||||
 | 
					            or subgroups of subgroups also.
 | 
				
			||||||
 | 
					          schema:
 | 
				
			||||||
 | 
					            type: boolean
 | 
				
			||||||
 | 
					            default: false
 | 
				
			||||||
 | 
					          example: true
 | 
				
			||||||
 | 
					          required: false
 | 
				
			||||||
 | 
					      responses:
 | 
				
			||||||
 | 
					        "200":
 | 
				
			||||||
 | 
					          description: Success
 | 
				
			||||||
 | 
					          content:
 | 
				
			||||||
 | 
					            application/json:
 | 
				
			||||||
 | 
					              schema:
 | 
				
			||||||
 | 
					                allOf:
 | 
				
			||||||
 | 
					                  - $ref: "#/components/schemas/JsonSuccessBase"
 | 
				
			||||||
 | 
					                  - $ref: "#/components/schemas/SuccessDescription"
 | 
				
			||||||
 | 
					                  - additionalProperties: false
 | 
				
			||||||
 | 
					                    properties:
 | 
				
			||||||
 | 
					                      result: {}
 | 
				
			||||||
 | 
					                      msg: {}
 | 
				
			||||||
 | 
					                      subgroups:
 | 
				
			||||||
 | 
					                        type: array
 | 
				
			||||||
 | 
					                        items:
 | 
				
			||||||
 | 
					                          type: integer
 | 
				
			||||||
 | 
					                        description: |
 | 
				
			||||||
 | 
					                          A list containing the IDs of subgroups of the user group.
 | 
				
			||||||
 | 
					                    example:
 | 
				
			||||||
 | 
					                      {"msg": "", "result": "success", "subgroups": [2, 3]}
 | 
				
			||||||
  /user_groups/{user_group_id}/members/{user_id}:
 | 
					  /user_groups/{user_group_id}/members/{user_id}:
 | 
				
			||||||
    get:
 | 
					    get:
 | 
				
			||||||
      operationId: get-is-user-group-member
 | 
					      operationId: get-is-user-group-member
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -979,3 +979,46 @@ class UserGroupAPITestCase(UserGroupTestCase):
 | 
				
			|||||||
            self.client_get(f"/json/user_groups/{moderators_group.id}/members", info=params).content
 | 
					            self.client_get(f"/json/user_groups/{moderators_group.id}/members", info=params).content
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertCountEqual(result_dict["members"], [shiva.id])
 | 
					        self.assertCountEqual(result_dict["members"], [shiva.id])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_subgroups_of_user_group(self) -> None:
 | 
				
			||||||
 | 
					        realm = get_realm("zulip")
 | 
				
			||||||
 | 
					        owners_group = UserGroup.objects.get(name="@role:owners", realm=realm, is_system_group=True)
 | 
				
			||||||
 | 
					        admins_group = UserGroup.objects.get(
 | 
				
			||||||
 | 
					            name="@role:administrators", realm=realm, is_system_group=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        moderators_group = UserGroup.objects.get(
 | 
				
			||||||
 | 
					            name="@role:moderators", realm=realm, is_system_group=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.login("iago")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test invalid user group id
 | 
				
			||||||
 | 
					        result = self.client_get("/json/user_groups/25/subgroups")
 | 
				
			||||||
 | 
					        self.assert_json_error(result, "Invalid user group")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result_dict = orjson.loads(
 | 
				
			||||||
 | 
					            self.client_get(f"/json/user_groups/{moderators_group.id}/subgroups").content
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(result_dict["subgroups"], [admins_group.id, owners_group.id])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        params = {"direct_subgroup_only": orjson.dumps(True).decode()}
 | 
				
			||||||
 | 
					        result_dict = orjson.loads(
 | 
				
			||||||
 | 
					            self.client_get(
 | 
				
			||||||
 | 
					                f"/json/user_groups/{moderators_group.id}/subgroups", info=params
 | 
				
			||||||
 | 
					            ).content
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertCountEqual(result_dict["subgroups"], [admins_group.id])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # User not part of a group can also get its subgroups.
 | 
				
			||||||
 | 
					        self.login("hamlet")
 | 
				
			||||||
 | 
					        result_dict = orjson.loads(
 | 
				
			||||||
 | 
					            self.client_get(f"/json/user_groups/{moderators_group.id}/subgroups").content
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(result_dict["subgroups"], [admins_group.id, owners_group.id])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        params = {"direct_subgroup_only": orjson.dumps(True).decode()}
 | 
				
			||||||
 | 
					        result_dict = orjson.loads(
 | 
				
			||||||
 | 
					            self.client_get(
 | 
				
			||||||
 | 
					                f"/json/user_groups/{moderators_group.id}/subgroups", info=params
 | 
				
			||||||
 | 
					            ).content
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertCountEqual(result_dict["subgroups"], [admins_group.id])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ from zerver.lib.user_groups import (
 | 
				
			|||||||
    access_user_group_by_id,
 | 
					    access_user_group_by_id,
 | 
				
			||||||
    access_user_groups_as_potential_subgroups,
 | 
					    access_user_groups_as_potential_subgroups,
 | 
				
			||||||
    get_direct_memberships_of_users,
 | 
					    get_direct_memberships_of_users,
 | 
				
			||||||
 | 
					    get_subgroup_ids,
 | 
				
			||||||
    get_user_group_direct_members,
 | 
					    get_user_group_direct_members,
 | 
				
			||||||
    get_user_group_member_ids,
 | 
					    get_user_group_member_ids,
 | 
				
			||||||
    is_user_in_group,
 | 
					    is_user_in_group,
 | 
				
			||||||
@@ -259,3 +260,19 @@ def get_user_group_members(
 | 
				
			|||||||
            "members": get_user_group_member_ids(user_group, direct_member_only=direct_member_only)
 | 
					            "members": get_user_group_member_ids(user_group, direct_member_only=direct_member_only)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@require_member_or_admin
 | 
				
			||||||
 | 
					@has_request_variables
 | 
				
			||||||
 | 
					def get_subgroups_of_user_group(
 | 
				
			||||||
 | 
					    request: HttpRequest,
 | 
				
			||||||
 | 
					    user_profile: UserProfile,
 | 
				
			||||||
 | 
					    user_group_id: int = REQ(json_validator=check_int, path_only=True),
 | 
				
			||||||
 | 
					    direct_subgroup_only: bool = REQ(json_validator=check_bool, default=False),
 | 
				
			||||||
 | 
					) -> HttpResponse:
 | 
				
			||||||
 | 
					    user_group = access_user_group_by_id(user_group_id, user_profile, for_read=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return json_success(
 | 
				
			||||||
 | 
					        request,
 | 
				
			||||||
 | 
					        data={"subgroups": get_subgroup_ids(user_group, direct_subgroup_only=direct_subgroup_only)},
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,6 +177,7 @@ from zerver.views.user_groups import (
 | 
				
			|||||||
    delete_user_group,
 | 
					    delete_user_group,
 | 
				
			||||||
    edit_user_group,
 | 
					    edit_user_group,
 | 
				
			||||||
    get_is_user_group_member,
 | 
					    get_is_user_group_member,
 | 
				
			||||||
 | 
					    get_subgroups_of_user_group,
 | 
				
			||||||
    get_user_group,
 | 
					    get_user_group,
 | 
				
			||||||
    get_user_group_members,
 | 
					    get_user_group_members,
 | 
				
			||||||
    update_subgroups_of_user_group,
 | 
					    update_subgroups_of_user_group,
 | 
				
			||||||
@@ -379,7 +380,11 @@ v1_api_and_json_patterns = [
 | 
				
			|||||||
        GET=get_user_group_members,
 | 
					        GET=get_user_group_members,
 | 
				
			||||||
        POST=update_user_group_backend,
 | 
					        POST=update_user_group_backend,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    rest_path("user_groups/<int:user_group_id>/subgroups", POST=update_subgroups_of_user_group),
 | 
					    rest_path(
 | 
				
			||||||
 | 
					        "user_groups/<int:user_group_id>/subgroups",
 | 
				
			||||||
 | 
					        POST=update_subgroups_of_user_group,
 | 
				
			||||||
 | 
					        GET=get_subgroups_of_user_group,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    rest_path(
 | 
					    rest_path(
 | 
				
			||||||
        "user_groups/<int:user_group_id>/members/<int:user_id>", GET=get_is_user_group_member
 | 
					        "user_groups/<int:user_group_id>/members/<int:user_id>", GET=get_is_user_group_member
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user