11from typing import List
22
33from descope .auth import Auth
4+ from descope .common import DeliveryMethod
45from descope .exceptions import ERROR_TYPE_INVALID_ARGUMENT , AuthException
56from descope .management .common import (
67 AssociatedTenant ,
@@ -48,7 +49,62 @@ def create(
4849 response = self ._auth .do_post (
4950 MgmtV1 .user_create_path ,
5051 User ._compose_create_body (
51- login_id , email , phone , display_name , role_names , user_tenants , False
52+ login_id ,
53+ email ,
54+ phone ,
55+ display_name ,
56+ role_names ,
57+ user_tenants ,
58+ False ,
59+ False ,
60+ ),
61+ pswd = self ._auth .management_key ,
62+ )
63+ return response .json ()
64+
65+ def create_test_user (
66+ self ,
67+ login_id : str ,
68+ email : str = None ,
69+ phone : str = None ,
70+ display_name : str = None ,
71+ role_names : List [str ] = [],
72+ user_tenants : List [AssociatedTenant ] = [],
73+ ) -> dict :
74+ """
75+ Create a new test user.
76+ The login_id is required and will determine what the user will use to sign in.
77+ Make sure the login id is unique for test. All other fields are optional.
78+
79+ Args:
80+ login_id (str): user login ID.
81+ email (str): Optional user email address.
82+ phone (str): Optional user phone number.
83+ display_name (str): Optional user display name.
84+ role_names (List[str]): An optional list of the user's roles without tenant association. These roles are
85+ mutually exclusive with the `user_tenant` roles.
86+ user_tenants (List[AssociatedTenant]): An optional list of the user's tenants, and optionally, their roles per tenant. These roles are
87+ mutually exclusive with the general `role_names`.
88+
89+ Return value (dict):
90+ Return dict in the format
91+ {"user": {}}
92+ Containing the created test user information.
93+
94+ Raise:
95+ AuthException: raised if update operation fails
96+ """
97+ response = self ._auth .do_post (
98+ MgmtV1 .user_create_path ,
99+ User ._compose_create_body (
100+ login_id ,
101+ email ,
102+ phone ,
103+ display_name ,
104+ role_names ,
105+ user_tenants ,
106+ False ,
107+ True ,
52108 ),
53109 pswd = self ._auth .management_key ,
54110 )
@@ -77,7 +133,14 @@ def invite(
77133 response = self ._auth .do_post (
78134 MgmtV1 .user_create_path ,
79135 User ._compose_create_body (
80- login_id , email , phone , display_name , role_names , user_tenants , True
136+ login_id ,
137+ email ,
138+ phone ,
139+ display_name ,
140+ role_names ,
141+ user_tenants ,
142+ True ,
143+ False ,
81144 ),
82145 pswd = self ._auth .management_key ,
83146 )
@@ -112,7 +175,7 @@ def update(
112175 self ._auth .do_post (
113176 MgmtV1 .user_update_path ,
114177 User ._compose_update_body (
115- login_id , email , phone , display_name , role_names , user_tenants
178+ login_id , email , phone , display_name , role_names , user_tenants , False
116179 ),
117180 pswd = self ._auth .management_key ,
118181 )
@@ -136,6 +199,21 @@ def delete(
136199 pswd = self ._auth .management_key ,
137200 )
138201
202+ def delete_all_test_users (
203+ self ,
204+ ):
205+ """
206+ Delete all test users in the project. IMPORTANT: This action is irreversible. Use carefully.
207+
208+ Raise:
209+ AuthException: raised if creation operation fails
210+ """
211+ self ._auth .do_post (
212+ MgmtV1 .user_delete_all_test_users_path ,
213+ {},
214+ pswd = self ._auth .management_key ,
215+ )
216+
139217 def load (
140218 self ,
141219 login_id : str ,
@@ -193,6 +271,8 @@ def search_all(
193271 role_names : List [str ] = [],
194272 limit : int = 0 ,
195273 page : int = 0 ,
274+ test_users_only : bool = False ,
275+ with_test_user : bool = False ,
196276 ) -> dict :
197277 """
198278 Search all users.
@@ -202,6 +282,8 @@ def search_all(
202282 role_names (List[str]): Optional list of role names to filter by
203283 limit (int): Optional limit of the number of users returned. Leave empty for default.
204284 page (int): Optional pagination control. Pages start at 0 and must be non-negative.
285+ test_users_only (bool): Optional filter only test users.
286+ with_test_user (bool): Optional include test users in search.
205287
206288 Return value (dict):
207289 Return dict in the format
@@ -228,6 +310,8 @@ def search_all(
228310 "roleNames" : role_names ,
229311 "limit" : limit ,
230312 "page" : page ,
313+ "testUsersOnly" : test_users_only ,
314+ "withTestUser" : with_test_user ,
231315 },
232316 pswd = self ._auth .management_key ,
233317 )
@@ -536,6 +620,98 @@ def remove_tenant_roles(
536620 )
537621 return response .json ()
538622
623+ def generate_otp_for_test_user (
624+ self ,
625+ method : DeliveryMethod ,
626+ login_id : str ,
627+ ) -> dict :
628+ """
629+ Generate OTP for the given login ID of a test user.
630+ This is useful when running tests and don't want to use 3rd party messaging services.
631+
632+ Args:
633+ method (DeliveryMethod): The method to use for "delivering" the OTP verification code to the user, for example
634+ EMAIL, SMS, or WHATSAPP
635+ login_id (str): The login ID of the test user being validated.
636+
637+ Return value (dict):
638+ Return dict in the format
639+ {"code": "", "loginId": ""}
640+ Containing the code for the login (exactly as it sent via Email or SMS).
641+
642+ Raise:
643+ AuthException: raised if the operation fails
644+ """
645+ response = self ._auth .do_post (
646+ MgmtV1 .user_generate_otp_for_test_path ,
647+ {"loginId" : login_id , "deliveryMethod" : Auth .get_method_string (method )},
648+ pswd = self ._auth .management_key ,
649+ )
650+ return response .json ()
651+
652+ def generate_magic_link_for_test_user (
653+ self ,
654+ method : DeliveryMethod ,
655+ login_id : str ,
656+ uri : str ,
657+ ) -> dict :
658+ """
659+ Generate Magic Link for the given login ID of a test user.
660+ This is useful when running tests and don't want to use 3rd party messaging services.
661+
662+ Args:
663+ method (DeliveryMethod): The method to use for "delivering" the verification magic link to the user, for example
664+ EMAIL, SMS, or WHATSAPP
665+ login_id (str): The login ID of the test user being validated.
666+ uri (str): Optional redirect uri which will be used instead of any global configuration.
667+
668+ Return value (dict):
669+ Return dict in the format
670+ {"link": "", "loginId": ""}
671+ Containing the magic link for the login (exactly as it sent via Email or SMS).
672+
673+ Raise:
674+ AuthException: raised if the operation fails
675+ """
676+ response = self ._auth .do_post (
677+ MgmtV1 .user_generate_magic_link_for_test_path ,
678+ {
679+ "loginId" : login_id ,
680+ "deliveryMethod" : Auth .get_method_string (method ),
681+ "URI" : uri ,
682+ },
683+ pswd = self ._auth .management_key ,
684+ )
685+ return response .json ()
686+
687+ def generate_enchanted_link_for_test_user (
688+ self ,
689+ login_id : str ,
690+ uri : str ,
691+ ) -> dict :
692+ """
693+ Generate Enchanted Link for the given login ID of a test user.
694+ This is useful when running tests and don't want to use 3rd party messaging services.
695+
696+ Args:
697+ login_id (str): The login ID of the test user being validated.
698+ uri (str): Optional redirect uri which will be used instead of any global configuration.
699+
700+ Return value (dict):
701+ Return dict in the format
702+ {"link": "", "loginId": "", "pendingRef": ""}
703+ Containing the enchanted link for the login (exactly as it sent via Email or SMS) and pendingRef.
704+
705+ Raise:
706+ AuthException: raised if the operation fails
707+ """
708+ response = self ._auth .do_post (
709+ MgmtV1 .user_generate_enchanted_link_for_test_path ,
710+ {"loginId" : login_id , "URI" : uri },
711+ pswd = self ._auth .management_key ,
712+ )
713+ return response .json ()
714+
539715 @staticmethod
540716 def _compose_create_body (
541717 login_id : str ,
@@ -545,9 +721,10 @@ def _compose_create_body(
545721 role_names : List [str ],
546722 user_tenants : List [AssociatedTenant ],
547723 invite : bool ,
724+ test : bool ,
548725 ) -> dict :
549726 body = User ._compose_update_body (
550- login_id , email , phone , display_name , role_names , user_tenants
727+ login_id , email , phone , display_name , role_names , user_tenants , test
551728 )
552729 body ["invite" ] = invite
553730 return body
@@ -560,6 +737,7 @@ def _compose_update_body(
560737 display_name : str ,
561738 role_names : List [str ],
562739 user_tenants : List [AssociatedTenant ],
740+ test : bool ,
563741 ) -> dict :
564742 return {
565743 "loginId" : login_id ,
@@ -568,4 +746,5 @@ def _compose_update_body(
568746 "displayName" : display_name ,
569747 "roleNames" : role_names ,
570748 "userTenants" : associated_tenants_to_dict (user_tenants ),
749+ "test" : test ,
571750 }
0 commit comments