fix(user): restore admin users list contract
Some checks failed
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 3m18s
CI/CD Pipeline / Run Tests (pull_request) Successful in 4m9s
CI/CD Pipeline / Run API Inventory E2E Tests (pull_request) Successful in 1m30s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
Some checks failed
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 3m18s
CI/CD Pipeline / Run Tests (pull_request) Successful in 4m9s
CI/CD Pipeline / Run API Inventory E2E Tests (pull_request) Successful in 1m30s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from apps.core.openapi import CommonResponses, ErrorResponses, swagger_tag
|
from apps.core.openapi import CommonResponses, ErrorResponses, swagger_tag
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.hashers import check_password
|
from django.contrib.auth.hashers import check_password
|
||||||
|
from django.core.paginator import Paginator
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import generics, status
|
from rest_framework import generics, status
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
|
from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@@ -105,6 +109,87 @@ ADMIN_USER_VALIDATION_ERROR_RESPONSE = openapi.Response(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ADMIN_USER_LIST_RESPONSE = openapi.Response(
|
||||||
|
description="Список пользователей в формате frontend-контракта",
|
||||||
|
schema=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
"count": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||||
|
"next": openapi.Schema(type=openapi.TYPE_STRING, format="uri", nullable=True),
|
||||||
|
"previous": openapi.Schema(
|
||||||
|
type=openapi.TYPE_STRING, format="uri", nullable=True
|
||||||
|
),
|
||||||
|
"results": openapi.Schema(
|
||||||
|
type=openapi.TYPE_ARRAY,
|
||||||
|
items=openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
"id": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||||
|
"username": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"email": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"phone": openapi.Schema(
|
||||||
|
type=openapi.TYPE_STRING, nullable=True
|
||||||
|
),
|
||||||
|
"is_active": openapi.Schema(type=openapi.TYPE_BOOLEAN),
|
||||||
|
"role": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"role_label": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"profile": openapi.Schema(
|
||||||
|
type=openapi.TYPE_OBJECT,
|
||||||
|
properties={
|
||||||
|
"first_name": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"middle_name": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"last_name": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
"full_name": openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_page_url(request, page_number: int) -> str:
|
||||||
|
query_params = request.query_params.copy()
|
||||||
|
query_params["page"] = page_number
|
||||||
|
encoded_query = urlencode(query_params, doseq=True)
|
||||||
|
return request.build_absolute_uri(f"{request.path}?{encoded_query}")
|
||||||
|
|
||||||
|
|
||||||
|
def _paginate_user_queryset(request, queryset):
|
||||||
|
page_size_raw = request.query_params.get("page_size", "20")
|
||||||
|
page_raw = request.query_params.get("page", "1")
|
||||||
|
try:
|
||||||
|
page_size = max(1, min(int(page_size_raw), 100))
|
||||||
|
page_number = max(1, int(page_raw))
|
||||||
|
except (TypeError, ValueError) as exc:
|
||||||
|
raise ValidationError(
|
||||||
|
{
|
||||||
|
"detail": (
|
||||||
|
"Параметры page и page_size должны быть положительными "
|
||||||
|
"целыми числами."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
paginator = Paginator(queryset, page_size)
|
||||||
|
page_obj = paginator.get_page(page_number)
|
||||||
|
return {
|
||||||
|
"count": paginator.count,
|
||||||
|
"next": (
|
||||||
|
_build_page_url(request, page_obj.next_page_number())
|
||||||
|
if page_obj.has_next()
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"previous": (
|
||||||
|
_build_page_url(request, page_obj.previous_page_number())
|
||||||
|
if page_obj.has_previous()
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"results": page_obj.object_list,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RegisterView(APIView):
|
class RegisterView(APIView):
|
||||||
"""
|
"""
|
||||||
@@ -261,9 +346,23 @@ class AdminUserListCreateView(APIView):
|
|||||||
"middle_name, last_name, role. Для обратной сортировки используйте префикс -"
|
"middle_name, last_name, role. Для обратной сортировки используйте префикс -"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
name="page",
|
||||||
|
in_=openapi.IN_QUERY,
|
||||||
|
type=openapi.TYPE_INTEGER,
|
||||||
|
required=False,
|
||||||
|
description="Номер страницы, начиная с 1",
|
||||||
|
),
|
||||||
|
openapi.Parameter(
|
||||||
|
name="page_size",
|
||||||
|
in_=openapi.IN_QUERY,
|
||||||
|
type=openapi.TYPE_INTEGER,
|
||||||
|
required=False,
|
||||||
|
description="Размер страницы, по умолчанию 20, максимум 100",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
responses={
|
responses={
|
||||||
200: FrontendManagedUserSerializer(many=True),
|
200: ADMIN_USER_LIST_RESPONSE,
|
||||||
**ErrorResponses.ADMIN,
|
**ErrorResponses.ADMIN,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -272,8 +371,18 @@ class AdminUserListCreateView(APIView):
|
|||||||
search=request.query_params.get("search", ""),
|
search=request.query_params.get("search", ""),
|
||||||
ordering=request.query_params.get("ordering", ""),
|
ordering=request.query_params.get("ordering", ""),
|
||||||
)
|
)
|
||||||
serializer = FrontendManagedUserSerializer(queryset, many=True)
|
paginated = _paginate_user_queryset(request, queryset)
|
||||||
return Response(serializer.data)
|
serializer = FrontendUserWithProfileSerializer(
|
||||||
|
paginated["results"], many=True
|
||||||
|
)
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"count": paginated["count"],
|
||||||
|
"next": paginated["next"],
|
||||||
|
"previous": paginated["previous"],
|
||||||
|
"results": serializer.data,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
tags=[USER_ADMIN_TAG],
|
tags=[USER_ADMIN_TAG],
|
||||||
|
|||||||
@@ -254,7 +254,11 @@ class AdminUserManagementViewTest(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
set(response.data[0].keys()),
|
set(response.data.keys()),
|
||||||
|
{"count", "next", "previous", "results"},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
set(response.data["results"][0].keys()),
|
||||||
{
|
{
|
||||||
"id",
|
"id",
|
||||||
"username",
|
"username",
|
||||||
@@ -263,9 +267,14 @@ class AdminUserManagementViewTest(APITestCase):
|
|||||||
"is_active",
|
"is_active",
|
||||||
"role",
|
"role",
|
||||||
"role_label",
|
"role_label",
|
||||||
|
"profile",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
usernames = {item["username"] for item in response.data}
|
self.assertEqual(
|
||||||
|
set(response.data["results"][0]["profile"].keys()),
|
||||||
|
{"first_name", "middle_name", "last_name", "full_name"},
|
||||||
|
)
|
||||||
|
usernames = {item["username"] for item in response.data["results"]}
|
||||||
self.assertIn(self.admin.username, usernames)
|
self.assertIn(self.admin.username, usernames)
|
||||||
self.assertIn(self.user.username, usernames)
|
self.assertIn(self.user.username, usernames)
|
||||||
|
|
||||||
@@ -282,7 +291,7 @@ class AdminUserManagementViewTest(APITestCase):
|
|||||||
response = self.client.get(self.list_url, {"search": "Петрович"})
|
response = self.client.get(self.list_url, {"search": "Петрович"})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
usernames = [item["username"] for item in response.data]
|
usernames = [item["username"] for item in response.data["results"]]
|
||||||
self.assertEqual(usernames, [self.user.username])
|
self.assertEqual(usernames, [self.user.username])
|
||||||
|
|
||||||
def test_admin_can_order_users(self):
|
def test_admin_can_order_users(self):
|
||||||
@@ -294,7 +303,7 @@ class AdminUserManagementViewTest(APITestCase):
|
|||||||
response = self.client.get(self.list_url, {"ordering": "first_name"})
|
response = self.client.get(self.list_url, {"ordering": "first_name"})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
ordered_ids = [item["id"] for item in response.data]
|
ordered_ids = [item["id"] for item in response.data["results"]]
|
||||||
self.assertLess(ordered_ids.index(second.id), ordered_ids.index(first.id))
|
self.assertLess(ordered_ids.index(second.id), ordered_ids.index(first.id))
|
||||||
|
|
||||||
def test_admin_can_create_user_with_role(self):
|
def test_admin_can_create_user_with_role(self):
|
||||||
|
|||||||
@@ -245,6 +245,12 @@ class UserApiInventoryE2ETest(AuthenticatedApiMixin, APITestCase):
|
|||||||
self.authenticate(self.admin)
|
self.authenticate(self.admin)
|
||||||
list_response = self.client.get(reverse("api_v1:user:admin-users"))
|
list_response = self.client.get(reverse("api_v1:user:admin-users"))
|
||||||
self.assertEqual(list_response.status_code, status.HTTP_200_OK)
|
self.assertEqual(list_response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(
|
||||||
|
set(list_response.data.keys()),
|
||||||
|
{"count", "next", "previous", "results"},
|
||||||
|
)
|
||||||
|
self.assertTrue(list_response.data["results"])
|
||||||
|
self.assertIn("profile", list_response.data["results"][0])
|
||||||
|
|
||||||
create_payload = {
|
create_payload = {
|
||||||
"email": fake.unique.email(),
|
"email": fake.unique.email(),
|
||||||
|
|||||||
Reference in New Issue
Block a user