Skip to content

Commit 4cc357c

Browse files
EnovotnyEric Novotny
andauthored
fix users endpoint (#278)
* fix users endpoint * fix office filter to removes other office roles * bump version 1.05 * mypy fix --------- Co-authored-by: Eric Novotny <nov00002@umn.edu>
1 parent c3c4f44 commit 4cc357c

4 files changed

Lines changed: 105 additions & 6 deletions

File tree

cwms/catalog/catalog.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ def get_locations_catalog(
6767
"location-kind-like": location_kind_like,
6868
}
6969

70-
response = api.get(endpoint=endpoint, params=params, api_version=2)
70+
response = api.get_with_paging(
71+
endpoint=endpoint, selector="entries", params=params, api_version=2
72+
)
7173
return Data(response, selector="entries")
7274

7375

@@ -131,7 +133,9 @@ def get_timeseries_catalog(
131133
"include-extents": include_extents,
132134
}
133135

134-
response = api.get(endpoint=endpoint, params=params, api_version=2)
136+
response = api.get_with_paging(
137+
endpoint=endpoint, selector="entries", params=params, api_version=2
138+
)
135139
return Data(response, selector="entries")
136140

137141

cwms/users/users.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,66 @@ def get_user_profile() -> dict[str, Any]:
3535
return dict(response)
3636

3737

38+
def filter_users_by_office(data: dict[str, Any], office: str) -> dict[str, Any]:
39+
"""
40+
Filter users JSON to only include users that have roles for the specified office.
41+
Each user's roles dict will only contain the entry for that office.
42+
43+
Args:
44+
data: The full users JSON as a Python dict.
45+
office: The office key to filter by (e.g., 'MVP', 'LRL').
46+
47+
Returns:
48+
A new dict with the same structure, filtered to the specified office.
49+
"""
50+
filtered_users = []
51+
52+
for user in data.get("users", []):
53+
roles = user.get("roles", {})
54+
55+
if office in roles:
56+
# Build a copy of the user with only the target office's roles
57+
filtered_user = {k: v for k, v in user.items() if k != "roles"}
58+
filtered_user["roles"] = {office: roles[office]}
59+
filtered_users.append(filtered_user)
60+
61+
return {
62+
"page": data.get("page"),
63+
"page-size": data.get("page-size"),
64+
"total": len(filtered_users),
65+
"users": filtered_users,
66+
}
67+
68+
3869
def get_users(
3970
office_id: Optional[str] = None,
71+
username_like: Optional[str] = None,
72+
include_roles: Optional[bool] = None,
4073
page: Optional[str] = None,
41-
page_size: Optional[int] = None,
74+
page_size: Optional[int] = 5000,
4275
) -> Data:
4376
"""Retrieve users with optional office and paging filters."""
4477

45-
params = {"office": office_id, "page": page, "page-size": page_size}
78+
endpoint = "users"
79+
params = {
80+
"office": office_id,
81+
"username-like": username_like,
82+
"include-roles": include_roles,
83+
"page": page,
84+
"page-size": page_size,
85+
}
4686
try:
47-
response = api.get("users", params=params, api_version=1)
87+
response = api.get_with_paging(
88+
endpoint=endpoint, selector="users", params=params, api_version=1
89+
)
4890
except api.ApiError as error:
4991
_raise_user_management_error(error, "User list lookup")
92+
93+
# filter by office if office_id is provided since the API does not
94+
# currently support filtering by office on the backend. This is a
95+
# temporary workaround until the API supports office filtering.
96+
if office_id:
97+
response = filter_users_by_office(response, office_id)
5098
return Data(response, selector="users")
5199

52100

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "cwms-python"
33
repository = "https://github.com/HydrologicEngineeringCenter/cwms-python"
44

5-
version = "1.0.4"
5+
version = "1.0.5"
66

77

88
packages = [

tests/cda/users/users_CDA_test.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,53 @@ def test_get_users():
107107
)
108108

109109

110+
def test_get_users_by_office():
111+
# /users?office={office_id}
112+
# Covers filtering the user list by office and verifies the known test account appears in the
113+
# returned page when filtering by the test user's office.
114+
user_name = str(TEST_USER_NAME)
115+
office_id = TEST_OFFICE_ID
116+
users = cwms.get_users(office_id=office_id, page_size=200).json
117+
users_list = users.get("users", [])
118+
assert isinstance(users_list, list)
119+
assert any(
120+
str(u.get("user-name", "")).lower() == user_name.lower() for u in users_list
121+
)
122+
for user in users["users"]:
123+
roles = user["roles"]
124+
assert set(roles.keys()) == {
125+
office_id
126+
}, f"{user['user-name']} has unexpected office keys: {set(roles.keys())}"
127+
128+
129+
def test_get_users_by_office_not_present_in_other_office():
130+
# /users?office={office_id}
131+
# Covers filtering the user list by office and verifies the known test account does not appear in the
132+
# returned page when filtering by a different office.
133+
user_name = str(TEST_USER_NAME)
134+
office_id = "MVP" if TEST_OFFICE_ID != "MVP" else "SPK"
135+
users = cwms.get_users(office_id=office_id, page_size=200)
136+
users_list = users.json.get("users", [])
137+
assert isinstance(users_list, list)
138+
assert not any(
139+
str(u.get("user-name", "")).lower() == user_name.lower() for u in users_list
140+
)
141+
142+
143+
def test_get_users_with_paging():
144+
# /users with paging parameters
145+
# Covers requesting a specific page and page size and verifies the response contains the expected pagination metadata.
146+
page_size = 2
147+
users_with_page = cwms.get_users(page_size=page_size)
148+
users_list = users_with_page.json.get("users", [])
149+
users_without_page = cwms.get_users()
150+
all_users_list = users_without_page.json.get("users", [])
151+
assert isinstance(users_list, list)
152+
assert isinstance(all_users_list, list)
153+
assert len(users_list) >= page_size
154+
assert len(all_users_list) == len(users_list)
155+
156+
110157
def test_store_update_delete_user_roles_roundtrip():
111158
# Round Trip: Covers the role-management lifecycle for an existing user in one office:
112159
# 1. add a role the user does not currently have

0 commit comments

Comments
 (0)