Skip to content

Commit 32256ce

Browse files
authored
Add test user apis (#454)
* Add test user apis * fix test
1 parent 423494d commit 32256ce

File tree

4 files changed

+264
-2
lines changed

4 files changed

+264
-2
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,13 @@ descope_client.mgmt.user.create_test_user(
13931393
],
13941394
)
13951395

1396+
# Search all test users, optionally according to tenant and/or role filter
1397+
# results can be paginated using the limit and page parameters
1398+
users_resp = descope_client.mgmt.user.search_all_test_users()
1399+
users = users_resp["users"]
1400+
for user in users:
1401+
# Do something
1402+
13961403
# Now test user got created, and this user will be available until you delete it,
13971404
# you can use any management operation for test user CRUD.
13981405
# You can also delete all test users.

descope/management/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class MgmtV1:
2121

2222
# user
2323
user_create_path = "/v1/mgmt/user/create"
24+
test_user_create_path = "/v1/mgmt/user/create/test"
2425
user_create_batch_path = "/v1/mgmt/user/create/batch"
2526
user_update_path = "/v1/mgmt/user/update"
2627
user_patch_path = "/v1/mgmt/user/patch"
@@ -29,6 +30,7 @@ class MgmtV1:
2930
user_delete_all_test_users_path = "/v1/mgmt/user/test/delete/all"
3031
user_load_path = "/v1/mgmt/user"
3132
users_search_path = "/v2/mgmt/user/search"
33+
test_users_search_path = "/v2/mgmt/user/search/test"
3234
user_get_provider_token = "/v1/mgmt/user/provider/token"
3335
user_update_status_path = "/v1/mgmt/user/update/status"
3436
user_update_login_id_path = "/v1/mgmt/user/update/loginid"

descope/management/user.py

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def create_test_user(
178178
user_tenants = [] if user_tenants is None else user_tenants
179179

180180
response = self._auth.do_post(
181-
MgmtV1.user_create_path,
181+
MgmtV1.test_user_create_path,
182182
User._compose_create_body(
183183
login_id,
184184
email,
@@ -199,6 +199,7 @@ def create_test_user(
199199
None,
200200
None,
201201
additional_login_ids,
202+
sso_app_ids,
202203
),
203204
pswd=self._auth.management_key,
204205
)
@@ -671,6 +672,97 @@ def search_all(
671672
)
672673
return response.json()
673674

675+
def search_all_test_users(
676+
self,
677+
tenant_ids: Optional[List[str]] = None,
678+
role_names: Optional[List[str]] = None,
679+
limit: int = 0,
680+
page: int = 0,
681+
custom_attributes: Optional[dict] = None,
682+
statuses: Optional[List[str]] = None,
683+
emails: Optional[List[str]] = None,
684+
phones: Optional[List[str]] = None,
685+
sso_app_ids: Optional[List[str]] = None,
686+
sort: Optional[List[Sort]] = None,
687+
text: Optional[str] = None,
688+
login_ids: Optional[List[str]] = None,
689+
) -> dict:
690+
"""
691+
Search all test users.
692+
693+
Args:
694+
tenant_ids (List[str]): Optional list of tenant IDs to filter by
695+
role_names (List[str]): Optional list of role names to filter by
696+
limit (int): Optional limit of the number of users returned. Leave empty for default.
697+
page (int): Optional pagination control. Pages start at 0 and must be non-negative.
698+
custom_attributes (dict): Optional search for a attribute with a given value
699+
statuses (List[str]): Optional list of statuses to search for ("enabled", "disabled", "invited")
700+
emails (List[str]): Optional list of emails to search for
701+
phones (List[str]): Optional list of phones to search for
702+
sso_app_ids (List[str]): Optional list of SSO application IDs to filter by
703+
text (str): Optional string, allows free text search among all user's attributes.
704+
login_ids (List[str]): Optional list of login ids
705+
sort (List[Sort]): Optional List[dict], allows to sort by fields.
706+
707+
Return value (dict):
708+
Return dict in the format
709+
{"users": []}
710+
"users" contains a list of all of the found users and their information
711+
712+
Raise:
713+
AuthException: raised if search operation fails
714+
"""
715+
tenant_ids = [] if tenant_ids is None else tenant_ids
716+
role_names = [] if role_names is None else role_names
717+
718+
if limit < 0:
719+
raise AuthException(
720+
400, ERROR_TYPE_INVALID_ARGUMENT, "limit must be non-negative"
721+
)
722+
723+
if page < 0:
724+
raise AuthException(
725+
400, ERROR_TYPE_INVALID_ARGUMENT, "page must be non-negative"
726+
)
727+
body = {
728+
"tenantIds": tenant_ids,
729+
"roleNames": role_names,
730+
"limit": limit,
731+
"page": page,
732+
"testUsersOnly": True,
733+
"withTestUser": True,
734+
}
735+
if statuses is not None:
736+
body["statuses"] = statuses
737+
738+
if emails is not None:
739+
body["emails"] = emails
740+
741+
if phones is not None:
742+
body["phones"] = phones
743+
744+
if custom_attributes is not None:
745+
body["customAttributes"] = custom_attributes
746+
747+
if sso_app_ids is not None:
748+
body["ssoAppIds"] = sso_app_ids
749+
750+
if login_ids is not None:
751+
body["loginIds"] = login_ids
752+
753+
if text is not None:
754+
body["text"] = text
755+
756+
if sort is not None:
757+
body["sort"] = sort_to_dict(sort)
758+
759+
response = self._auth.do_post(
760+
MgmtV1.test_users_search_path,
761+
body=body,
762+
pswd=self._auth.management_key,
763+
)
764+
return response.json()
765+
674766
def get_provider_token(
675767
self,
676768
login_id: str,

tests/management/test_user.py

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def test_create_test_user(self):
180180
user = resp["user"]
181181
self.assertEqual(user["id"], "u1")
182182
mock_post.assert_called_with(
183-
f"{common.DEFAULT_BASE_URL}{MgmtV1.user_create_path}",
183+
f"{common.DEFAULT_BASE_URL}{MgmtV1.test_user_create_path}",
184184
headers={
185185
**common.default_headers,
186186
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
@@ -964,6 +964,167 @@ def test_search_all(self):
964964
timeout=DEFAULT_TIMEOUT_SECONDS,
965965
)
966966

967+
def test_search_all_test_users(self):
968+
# Test failed flows
969+
with patch("requests.post") as mock_post:
970+
mock_post.return_value.ok = False
971+
self.assertRaises(
972+
AuthException,
973+
self.client.mgmt.user.search_all_test_users,
974+
["t1, t2"],
975+
["r1", "r2"],
976+
)
977+
978+
with patch("requests.post") as mock_post:
979+
mock_post.return_value.ok = True
980+
self.assertRaises(
981+
AuthException,
982+
self.client.mgmt.user.search_all_test_users,
983+
[],
984+
[],
985+
-1,
986+
0,
987+
)
988+
989+
self.assertRaises(
990+
AuthException,
991+
self.client.mgmt.user.search_all_test_users,
992+
[],
993+
[],
994+
0,
995+
-1,
996+
)
997+
998+
# Test success flow
999+
with patch("requests.post") as mock_post:
1000+
network_resp = mock.Mock()
1001+
network_resp.ok = True
1002+
network_resp.json.return_value = json.loads(
1003+
"""{"users": [{"id": "u1"}, {"id": "u2"}]}"""
1004+
)
1005+
mock_post.return_value = network_resp
1006+
resp = self.client.mgmt.user.search_all_test_users(
1007+
["t1, t2"],
1008+
["r1", "r2"],
1009+
sso_app_ids=["app1"],
1010+
login_ids=["l1"],
1011+
)
1012+
users = resp["users"]
1013+
self.assertEqual(len(users), 2)
1014+
self.assertEqual(users[0]["id"], "u1")
1015+
self.assertEqual(users[1]["id"], "u2")
1016+
mock_post.assert_called_with(
1017+
f"{common.DEFAULT_BASE_URL}{MgmtV1.test_users_search_path}",
1018+
headers={
1019+
**common.default_headers,
1020+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
1021+
},
1022+
params=None,
1023+
json={
1024+
"tenantIds": ["t1, t2"],
1025+
"roleNames": ["r1", "r2"],
1026+
"limit": 0,
1027+
"page": 0,
1028+
"testUsersOnly": True,
1029+
"withTestUser": True,
1030+
"ssoAppIds": ["app1"],
1031+
"loginIds": ["l1"],
1032+
},
1033+
allow_redirects=False,
1034+
verify=True,
1035+
timeout=DEFAULT_TIMEOUT_SECONDS,
1036+
)
1037+
1038+
# Test success flow with text and sort
1039+
with patch("requests.post") as mock_post:
1040+
network_resp = mock.Mock()
1041+
network_resp.ok = True
1042+
network_resp.json.return_value = json.loads(
1043+
"""{"users": [{"id": "u1"}, {"id": "u2"}]}"""
1044+
)
1045+
mock_post.return_value = network_resp
1046+
sort = [Sort(field="kuku", desc=True), Sort(field="bubu")]
1047+
resp = self.client.mgmt.user.search_all_test_users(
1048+
["t1, t2"],
1049+
["r1", "r2"],
1050+
sso_app_ids=["app1"],
1051+
text="blue",
1052+
sort=sort,
1053+
)
1054+
users = resp["users"]
1055+
self.assertEqual(len(users), 2)
1056+
self.assertEqual(users[0]["id"], "u1")
1057+
self.assertEqual(users[1]["id"], "u2")
1058+
mock_post.assert_called_with(
1059+
f"{common.DEFAULT_BASE_URL}{MgmtV1.test_users_search_path}",
1060+
headers={
1061+
**common.default_headers,
1062+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
1063+
},
1064+
params=None,
1065+
json={
1066+
"tenantIds": ["t1, t2"],
1067+
"roleNames": ["r1", "r2"],
1068+
"limit": 0,
1069+
"page": 0,
1070+
"testUsersOnly": True,
1071+
"withTestUser": True,
1072+
"ssoAppIds": ["app1"],
1073+
"text": "blue",
1074+
"sort": [
1075+
{"desc": True, "field": "kuku"},
1076+
{"desc": False, "field": "bubu"},
1077+
],
1078+
},
1079+
allow_redirects=False,
1080+
verify=True,
1081+
timeout=DEFAULT_TIMEOUT_SECONDS,
1082+
)
1083+
1084+
# Test success flow with custom attributes
1085+
with patch("requests.post") as mock_post:
1086+
network_resp = mock.Mock()
1087+
network_resp.ok = True
1088+
network_resp.json.return_value = json.loads(
1089+
"""{"users": [{"id": "u1"}, {"id": "u2"}]}"""
1090+
)
1091+
mock_post.return_value = network_resp
1092+
resp = self.client.mgmt.user.search_all_test_users(
1093+
["t1, t2"],
1094+
["r1", "r2"],
1095+
custom_attributes={"ak": "av"},
1096+
statuses=["invited"],
1097+
phones=["+111111"],
1098+
emails=["[email protected]"],
1099+
)
1100+
users = resp["users"]
1101+
self.assertEqual(len(users), 2)
1102+
self.assertEqual(users[0]["id"], "u1")
1103+
self.assertEqual(users[1]["id"], "u2")
1104+
mock_post.assert_called_with(
1105+
f"{common.DEFAULT_BASE_URL}{MgmtV1.test_users_search_path}",
1106+
headers={
1107+
**common.default_headers,
1108+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
1109+
},
1110+
params=None,
1111+
json={
1112+
"tenantIds": ["t1, t2"],
1113+
"roleNames": ["r1", "r2"],
1114+
"limit": 0,
1115+
"page": 0,
1116+
"testUsersOnly": True,
1117+
"withTestUser": True,
1118+
"customAttributes": {"ak": "av"},
1119+
"statuses": ["invited"],
1120+
"emails": ["[email protected]"],
1121+
"phones": ["+111111"],
1122+
},
1123+
allow_redirects=False,
1124+
verify=True,
1125+
timeout=DEFAULT_TIMEOUT_SECONDS,
1126+
)
1127+
9671128
def test_get_provider_token(self):
9681129
# Test failed flows
9691130
with patch("requests.get") as mock_post:

0 commit comments

Comments
 (0)