[OND211-2329]: Updated user list pagination response.
This commit is contained in:
parent
9f4e0f48ed
commit
d247aabfae
2 changed files with 107 additions and 70 deletions
|
|
@ -1260,25 +1260,28 @@ def list_users() -> Response:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
type: array
|
type: object
|
||||||
items:
|
properties:
|
||||||
type: object
|
users:
|
||||||
properties:
|
type: array
|
||||||
id:
|
items:
|
||||||
type: string
|
type: object
|
||||||
description: User ID.
|
properties:
|
||||||
email:
|
id:
|
||||||
type: string
|
type: string
|
||||||
description: User email.
|
description: User ID.
|
||||||
nickname:
|
email:
|
||||||
type: string
|
type: string
|
||||||
description: User nickname.
|
description: User email.
|
||||||
is_superuser:
|
nickname:
|
||||||
type: boolean
|
type: string
|
||||||
description: Whether the user is a superuser.
|
description: User nickname.
|
||||||
total:
|
is_superuser:
|
||||||
type: integer
|
type: boolean
|
||||||
description: Total number of users.
|
description: Whether the user is a superuser.
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
description: Total number of users.
|
||||||
401:
|
401:
|
||||||
description: Unauthorized - authentication required.
|
description: Unauthorized - authentication required.
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -1327,28 +1330,7 @@ def list_users() -> Response:
|
||||||
|
|
||||||
email_filter = request.args.get("email")
|
email_filter = request.args.get("email")
|
||||||
|
|
||||||
# Query users
|
# Validate pagination parameters if provided
|
||||||
if email_filter:
|
|
||||||
# Validate email format if provided (allow + in local part)
|
|
||||||
email_match: Optional[Match[str]] = re.match(
|
|
||||||
r"^[\w\._\+-]+@([\w_-]+\.)+[\w-]{2,}$", email_filter
|
|
||||||
)
|
|
||||||
if not email_match:
|
|
||||||
return get_json_result(
|
|
||||||
data=False,
|
|
||||||
message=f"Invalid email address: {email_filter}!",
|
|
||||||
code=RetCode.OPERATING_ERROR,
|
|
||||||
)
|
|
||||||
users_query = UserService.query(email=email_filter)
|
|
||||||
users_list: List[User] = list(users_query)
|
|
||||||
else:
|
|
||||||
users_list: List[User] = UserService.get_all_users()
|
|
||||||
|
|
||||||
# Convert users to dictionaries
|
|
||||||
users_data: List[Dict[str, Any]] = [
|
|
||||||
user.to_dict() for user in users_list
|
|
||||||
]
|
|
||||||
|
|
||||||
if page is not None and page_size is not None:
|
if page is not None and page_size is not None:
|
||||||
if page < 1:
|
if page < 1:
|
||||||
return get_json_result(
|
return get_json_result(
|
||||||
|
|
@ -1363,12 +1345,37 @@ def list_users() -> Response:
|
||||||
code=RetCode.ARGUMENT_ERROR,
|
code=RetCode.ARGUMENT_ERROR,
|
||||||
)
|
)
|
||||||
|
|
||||||
start_idx: int = (page - 1) * page_size
|
# Build query
|
||||||
end_idx: int = start_idx + page_size
|
if email_filter:
|
||||||
users_data = users_data[start_idx:end_idx]
|
# Validate email format if provided (allow + in local part)
|
||||||
|
email_match: Optional[Match[str]] = re.match(
|
||||||
|
r"^[\w\._\+-]+@([\w_-]+\.)+[\w-]{2,}$", email_filter
|
||||||
|
)
|
||||||
|
if not email_match:
|
||||||
|
return get_json_result(
|
||||||
|
data=False,
|
||||||
|
message=f"Invalid email address: {email_filter}!",
|
||||||
|
code=RetCode.OPERATING_ERROR,
|
||||||
|
)
|
||||||
|
users_query = UserService.model.select().where(UserService.model.email == email_filter)
|
||||||
|
else:
|
||||||
|
users_query = UserService.model.select()
|
||||||
|
|
||||||
|
# Get total count before pagination
|
||||||
|
total: int = users_query.count()
|
||||||
|
|
||||||
|
# Apply pagination at database level
|
||||||
|
if page is not None and page_size is not None:
|
||||||
|
users_query = users_query.paginate(page, page_size)
|
||||||
|
|
||||||
|
# Convert to list and dictionaries
|
||||||
|
users_list: List[User] = list(users_query)
|
||||||
|
users_data: List[Dict[str, Any]] = [
|
||||||
|
user.to_dict() for user in users_list
|
||||||
|
]
|
||||||
|
|
||||||
return get_json_result(
|
return get_json_result(
|
||||||
data=users_data,
|
data={"users": users_data, "total": total},
|
||||||
message=f"Retrieved {len(users_data)} user(s) successfully!",
|
message=f"Retrieved {len(users_data)} user(s) successfully!",
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,13 @@ class TestUserList:
|
||||||
|
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth)
|
list_res: dict[str, Any] = list_users(web_api_auth)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
assert len(list_res["data"]) >= 1
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
|
assert isinstance(list_res["data"]["users"], list)
|
||||||
|
assert len(list_res["data"]["users"]) >= 1
|
||||||
# Verify the created user is in the list
|
# Verify the created user is in the list
|
||||||
user_emails: list[str] = [u["email"] for u in list_res["data"]]
|
user_emails: list[str] = [u["email"] for u in list_res["data"]["users"]]
|
||||||
assert unique_email in user_emails
|
assert unique_email in user_emails
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
|
|
@ -135,10 +138,13 @@ class TestUserList:
|
||||||
|
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth)
|
list_res: dict[str, Any] = list_users(web_api_auth)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
assert len(list_res["data"]) >= len(created_emails)
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
|
assert isinstance(list_res["data"]["users"], list)
|
||||||
|
assert len(list_res["data"]["users"]) >= len(created_emails)
|
||||||
# Verify all created users are in the list
|
# Verify all created users are in the list
|
||||||
user_emails: list[str] = [u["email"] for u in list_res["data"]]
|
user_emails: list[str] = [u["email"] for u in list_res["data"]["users"]]
|
||||||
for email in created_emails:
|
for email in created_emails:
|
||||||
assert email in user_emails
|
assert email in user_emails
|
||||||
|
|
||||||
|
|
@ -166,10 +172,13 @@ class TestUserList:
|
||||||
params: dict[str, str] = {"email": unique_email}
|
params: dict[str, str] = {"email": unique_email}
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
assert len(list_res["data"]) >= 1
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
|
assert isinstance(list_res["data"]["users"], list)
|
||||||
|
assert len(list_res["data"]["users"]) >= 1
|
||||||
# Verify all returned users have the filtered email
|
# Verify all returned users have the filtered email
|
||||||
for user in list_res["data"]:
|
for user in list_res["data"]["users"]:
|
||||||
assert user["email"] == unique_email
|
assert user["email"] == unique_email
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
|
|
@ -191,8 +200,12 @@ class TestUserList:
|
||||||
params: dict[str, str] = {"email": nonexistent_email}
|
params: dict[str, str] = {"email": nonexistent_email}
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
assert len(list_res["data"]) == 0
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
|
assert isinstance(list_res["data"]["users"], list)
|
||||||
|
assert len(list_res["data"]["users"]) == 0
|
||||||
|
assert list_res["data"]["total"] == 0
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
@ -240,9 +253,12 @@ class TestUserList:
|
||||||
|
|
||||||
if expected_valid:
|
if expected_valid:
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
|
assert isinstance(list_res["data"]["users"], list)
|
||||||
# Verify pagination limits
|
# Verify pagination limits
|
||||||
assert len(list_res["data"]) <= page_size
|
assert len(list_res["data"]["users"]) <= page_size
|
||||||
else:
|
else:
|
||||||
assert list_res["code"] != 0
|
assert list_res["code"] != 0
|
||||||
assert "must be greater than 0" in list_res["message"]
|
assert "must be greater than 0" in list_res["message"]
|
||||||
|
|
@ -276,19 +292,21 @@ class TestUserList:
|
||||||
|
|
||||||
# Get total count of all users to calculate pagination boundaries
|
# Get total count of all users to calculate pagination boundaries
|
||||||
list_res_all: dict[str, Any] = list_users(web_api_auth)
|
list_res_all: dict[str, Any] = list_users(web_api_auth)
|
||||||
total_users: int = len(list_res_all["data"])
|
total_users: int = list_res_all["data"]["total"]
|
||||||
|
|
||||||
# Test first page
|
# Test first page
|
||||||
params: dict[str, int] = {"page": 1, "page_size": 2}
|
params: dict[str, int] = {"page": 1, "page_size": 2}
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert len(list_res["data"]) == 2
|
assert len(list_res["data"]["users"]) == 2
|
||||||
|
assert list_res["data"]["total"] == total_users
|
||||||
|
|
||||||
# Test that pagination returns consistent page sizes
|
# Test that pagination returns consistent page sizes
|
||||||
params = {"page": 2, "page_size": 2}
|
params = {"page": 2, "page_size": 2}
|
||||||
list_res = list_users(web_api_auth, params=params)
|
list_res = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert len(list_res["data"]) == 2
|
assert len(list_res["data"]["users"]) == 2
|
||||||
|
assert list_res["data"]["total"] == total_users
|
||||||
|
|
||||||
# Test last page (might have fewer items)
|
# Test last page (might have fewer items)
|
||||||
# Calculate expected last page: ceil(total_users / page_size)
|
# Calculate expected last page: ceil(total_users / page_size)
|
||||||
|
|
@ -298,14 +316,16 @@ class TestUserList:
|
||||||
params = {"page": last_page, "page_size": page_size}
|
params = {"page": last_page, "page_size": page_size}
|
||||||
list_res = list_users(web_api_auth, params=params)
|
list_res = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert len(list_res["data"]) <= page_size
|
assert len(list_res["data"]["users"]) <= page_size
|
||||||
|
assert list_res["data"]["total"] == total_users
|
||||||
|
|
||||||
# Test page beyond available data
|
# Test page beyond available data
|
||||||
# Use a page number that's definitely beyond available data
|
# Use a page number that's definitely beyond available data
|
||||||
params = {"page": total_users + 10, "page_size": 2}
|
params = {"page": total_users + 10, "page_size": 2}
|
||||||
list_res = list_users(web_api_auth, params=params)
|
list_res = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert len(list_res["data"]) == 0
|
assert len(list_res["data"]["users"]) == 0
|
||||||
|
assert list_res["data"]["total"] == total_users
|
||||||
|
|
||||||
@pytest.mark.p1
|
@pytest.mark.p1
|
||||||
def test_list_users_response_structure(
|
def test_list_users_response_structure(
|
||||||
|
|
@ -315,10 +335,14 @@ class TestUserList:
|
||||||
res: dict[str, Any] = list_users(web_api_auth)
|
res: dict[str, Any] = list_users(web_api_auth)
|
||||||
assert res["code"] == 0
|
assert res["code"] == 0
|
||||||
assert "data" in res
|
assert "data" in res
|
||||||
assert isinstance(res["data"], list)
|
assert isinstance(res["data"], dict)
|
||||||
|
assert "users" in res["data"]
|
||||||
|
assert "total" in res["data"]
|
||||||
|
assert isinstance(res["data"]["users"], list)
|
||||||
|
assert isinstance(res["data"]["total"], int)
|
||||||
|
|
||||||
if len(res["data"]) > 0:
|
if len(res["data"]["users"]) > 0:
|
||||||
user: dict[str, Any] = res["data"][0]
|
user: dict[str, Any] = res["data"]["users"][0]
|
||||||
# Verify user structure
|
# Verify user structure
|
||||||
assert "id" in user
|
assert "id" in user
|
||||||
assert "email" in user
|
assert "email" in user
|
||||||
|
|
@ -372,9 +396,12 @@ class TestUserList:
|
||||||
}
|
}
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
list_res: dict[str, Any] = list_users(web_api_auth, params=params)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
# Should return at least the created user
|
# Should return at least the created user
|
||||||
assert len(list_res["data"]) >= 1
|
assert len(list_res["data"]["users"]) >= 1
|
||||||
|
assert list_res["data"]["total"] >= 1
|
||||||
|
|
||||||
@pytest.mark.p2
|
@pytest.mark.p2
|
||||||
def test_list_users_performance_with_many_users(
|
def test_list_users_performance_with_many_users(
|
||||||
|
|
@ -405,7 +432,10 @@ class TestUserList:
|
||||||
# List all users
|
# List all users
|
||||||
list_res: dict[str, Any] = list_users(web_api_auth)
|
list_res: dict[str, Any] = list_users(web_api_auth)
|
||||||
assert list_res["code"] == 0, list_res
|
assert list_res["code"] == 0, list_res
|
||||||
assert isinstance(list_res["data"], list)
|
assert isinstance(list_res["data"], dict)
|
||||||
|
assert "users" in list_res["data"]
|
||||||
|
assert "total" in list_res["data"]
|
||||||
# Should return at least the created users
|
# Should return at least the created users
|
||||||
assert len(list_res["data"]) >= created_count
|
assert len(list_res["data"]["users"]) >= created_count
|
||||||
|
assert list_res["data"]["total"] >= created_count
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue