Skip to content

Commit a42e2a9

Browse files
committed
refactor: update endpoint responses
1 parent b6c0002 commit a42e2a9

1 file changed

Lines changed: 44 additions & 75 deletions

File tree

openedx_authz/rest_api/v1/views.py

Lines changed: 44 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,27 @@
1616
from rest_framework.views import APIView
1717

1818
from openedx_authz.api.data import ContentLibraryData
19-
from openedx_authz.api.roles import (
20-
get_all_roles_and_subjects_in_scope,
21-
get_role_definitions_in_scope,
22-
get_all_users_by_role,
23-
)
19+
from openedx_authz.api.roles import get_all_users_by_role, get_role_definitions_in_scope
2420
from openedx_authz.api.users import (
2521
assign_role_to_user_in_scope,
26-
get_user_role_assignments_for_role_in_scope,
22+
get_all_user_role_assignments_in_scope_v2,
2723
unassign_role_from_user,
2824
user_has_permission,
29-
get_all_user_role_assignments_in_scope,
3025
)
26+
from openedx_authz.rest_api.v1.paginators import RoleUserAPIViewPagination
3127
from openedx_authz.rest_api.v1.serializers import (
3228
AddUserToRoleWithScopeSerializer,
3329
ListRolesWithScopeResponseSerializer,
3430
ListRolesWithScopeSerializer,
35-
ListUsersInRoleWithScopeResponseSerializer,
3631
ListUsersInRoleWithScopeSerializer,
3732
PermissionValidationResponseSerializer,
3833
PermissionValidationSerializer,
3934
RemoveUserFromRoleWithScopeSerializer,
35+
RoleAssignmentSerializer,
4036
)
4137

4238
try:
43-
from common.djangoapps.student.models.user import get_user_by_username_or_email
39+
from common.djangoapps.student.models.user import get_user_by_username_or_email # type: ignore
4440
except ImportError:
4541
get_user_by_username_or_email = None
4642

@@ -101,65 +97,33 @@ class RoleUserAPIView(APIView):
10197
"""
10298

10399
permission_classes = [IsAuthenticated]
100+
pagination_class = RoleUserAPIViewPagination
104101

105102
@apidocs.schema(
106103
parameters=[
107104
apidocs.query_parameter("role", str, description="The name of the role to query"),
108105
apidocs.query_parameter("scope", str, description="The authorization scope for the role"),
106+
apidocs.query_parameter("page", int, description="Page number for pagination"),
107+
apidocs.query_parameter("page_size", int, description="Number of items per page"),
109108
],
110109
responses={
111-
status.HTTP_200_OK: ListUsersInRoleWithScopeResponseSerializer(many=True),
110+
status.HTTP_200_OK: "The users were retrieved successfully",
112111
status.HTTP_400_BAD_REQUEST: "The request parameters are invalid",
113112
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
114113
},
115114
)
116115
def get(self, request: HttpRequest) -> Response:
117-
"""Retrieve all users assigned to a specific role within a scope."""
116+
"""Retrieve all users with role assignments within a specific scope."""
117+
# TODO: Filter by role
118118
serializer = ListUsersInRoleWithScopeSerializer(data=request.query_params)
119119
serializer.is_valid(raise_exception=True)
120+
user_role_assignments = get_all_user_role_assignments_in_scope_v2(serializer.validated_data["scope"])
120121

121-
role_name = serializer.validated_data.get("role")
122-
scope = serializer.validated_data["scope"]
123-
124-
response_data = []
125-
126-
scope_data = ContentLibraryData(external_key=scope)
127-
128-
# TODO: Should this be another endpoint?
129-
if not role_name:
130-
roles = get_all_roles_and_subjects_in_scope(scope_data)
131-
final_roles = []
132-
for role in roles:
133-
role_data = {
134-
"role": role["role"],
135-
"users": [],
136-
}
137-
for user_identifier in role["users"]:
138-
user = get_user_by_username_or_email(user_identifier)
139-
role_data["users"].append(
140-
{
141-
"username": user.username,
142-
"full_name": user.profile.name,
143-
"email": user.email,
144-
}
145-
)
146-
final_roles.append(role_data)
147-
return Response(final_roles, status=status.HTTP_200_OK)
148-
149-
role_assignments = get_user_role_assignments_for_role_in_scope(role_name, scope)
150-
for assignment in role_assignments:
151-
# TODO: Should we get all users at once instead of one by one?
152-
user = get_user_by_username_or_email(assignment.subject.username)
153-
response_data.append(
154-
{
155-
"username": assignment.subject.username,
156-
"full_name": user.profile.name,
157-
"email": user.email,
158-
}
159-
)
122+
paginator = self.pagination_class()
123+
paginated_assignments = paginator.paginate_queryset(user_role_assignments, request)
160124

161-
serializer = ListUsersInRoleWithScopeResponseSerializer(response_data, many=True)
162-
return Response(serializer.data, status=status.HTTP_200_OK)
125+
serializer = RoleAssignmentSerializer(paginated_assignments, many=True)
126+
return paginator.get_paginated_response(serializer.data)
163127

164128
@apidocs.schema(
165129
body=AddUserToRoleWithScopeSerializer,
@@ -174,57 +138,62 @@ def put(self, request: HttpRequest) -> Response:
174138
serializer = AddUserToRoleWithScopeSerializer(data=request.data)
175139
serializer.is_valid(raise_exception=True)
176140

177-
completed, errors = [], []
141+
# TODO: Should we validate that the role or scope exists?
178142
role_name = serializer.validated_data["role"]
179143
scope = serializer.validated_data["scope"]
180144

145+
completed, errors = [], []
181146
for user_identifier in serializer.validated_data["users"]:
182147
try:
183148
user = get_user_by_username_or_email(user_identifier)
184149
assign_role_to_user_in_scope(user.username, role_name, scope)
185-
completed.append({"user": user_identifier, "status": "role_added"})
150+
completed.append({"user_identifier": user_identifier, "status": "role_added"})
186151
except User.DoesNotExist:
187-
errors.append({"user": user_identifier, "error": "user_not_found"})
152+
errors.append({"user_identifier": user_identifier, "error": "user_not_found"})
188153
except Exception as e: # pylint: disable=broad-exception-caught
189154
logger.error(f"Error assigning role to user {user_identifier}: {e}")
190-
errors.append({"user": user_identifier, "error": "assignment_failed"})
155+
errors.append({"user_identifier": user_identifier, "error": "assignment_failed"})
191156

192157
response_data = {"completed": completed, "errors": errors}
193158
return Response(response_data, status=status.HTTP_207_MULTI_STATUS)
194159

195160
@apidocs.schema(
196161
parameters=[
197-
apidocs.query_parameter("user", str, description="The user to remove from the role"),
198-
apidocs.query_parameter("role", str, description="The role to remove the user from"),
199-
apidocs.query_parameter("scope", str, description="The scope to remove the user from"),
162+
apidocs.query_parameter(
163+
"users", str, description="List of user identifiers (username or email) separated by a comma"
164+
),
165+
apidocs.query_parameter("role", str, description="The role to remove the users from"),
166+
apidocs.query_parameter("scope", str, description="The scope to remove the users from"),
200167
],
201168
responses={
202-
status.HTTP_200_OK: "The user was removed from the role",
203-
status.HTTP_400_BAD_REQUEST: "The request data is invalid",
169+
status.HTTP_207_MULTI_STATUS: "The users were removed from the role",
170+
status.HTTP_400_BAD_REQUEST: "The request parameters are invalid",
204171
status.HTTP_401_UNAUTHORIZED: "The user is not authenticated",
205-
status.HTTP_404_NOT_FOUND: "The user was not found",
206-
status.HTTP_500_INTERNAL_SERVER_ERROR: "The user was not removed from the role",
207172
},
208173
)
209174
def delete(self, request: HttpRequest) -> Response:
210-
"""Remove a user from a specific role within a scope."""
175+
"""Remove multiple users from a specific role within a scope."""
211176
serializer = RemoveUserFromRoleWithScopeSerializer(data=request.query_params)
212177
serializer.is_valid(raise_exception=True)
213178

214-
# Should we allow a list of users separated by a comma?
215-
user_identifier = serializer.validated_data["user"]
179+
user_identifiers = serializer.validated_data["users"]
216180
role_name = serializer.validated_data["role"]
217181
scope = serializer.validated_data["scope"]
218182

219-
try:
220-
user = get_user_by_username_or_email(user_identifier)
221-
unassign_role_from_user(user.username, role_name, scope)
222-
return Response({"message": "Role successfully removed from user"}, status=status.HTTP_200_OK)
223-
except User.DoesNotExist:
224-
return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)
225-
except Exception as e: # pylint: disable=broad-exception-caught
226-
logger.error(f"Error removing role from user {user_identifier}: {e}")
227-
return Response({"error": "Internal server error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
183+
completed, errors = [], []
184+
for user_identifier in user_identifiers:
185+
try:
186+
user = get_user_by_username_or_email(user_identifier)
187+
unassign_role_from_user(user.username, role_name, scope)
188+
completed.append({"user_identifier": user_identifier, "status": "role_removed"})
189+
except User.DoesNotExist:
190+
errors.append({"user_identifier": user_identifier, "error": "user_not_found"})
191+
except Exception as e: # pylint: disable=broad-exception-caught
192+
logger.error(f"Error removing role from user {user_identifier}: {e}")
193+
errors.append({"user_identifier": user_identifier, "error": "removal_failed"})
194+
195+
response_data = {"completed": completed, "errors": errors}
196+
return Response(response_data, status=status.HTTP_207_MULTI_STATUS)
228197

229198

230199
class RoleListView(APIView):

0 commit comments

Comments
 (0)