Skip to content

Commit 3d2a94a

Browse files
committed
refactor: Expand UserWithMetadata definition to work with both user list and fetch APIs
1 parent 3405e48 commit 3d2a94a

File tree

4 files changed

+120
-38
lines changed

4 files changed

+120
-38
lines changed

supertokens_python/recipe/dashboard/api/users_get.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020

2121
from ...usermetadata import UserMetadataRecipe
2222
from ...usermetadata.asyncio import get_user_metadata
23-
from ..interfaces import DashboardUsersGetResponse, UserWithMetadata
23+
from ..interfaces import DashboardUsersGetResponse
24+
from ..utils import UserWithMetadata
2425

2526
if TYPE_CHECKING:
2627
from supertokens_python.recipe.dashboard.interfaces import (
@@ -66,7 +67,7 @@ async def handle_users_get_api(
6667
)
6768

6869
users_with_metadata: List[UserWithMetadata] = [
69-
UserWithMetadata(user) for user in users_response.users
70+
UserWithMetadata().from_user(user) for user in users_response.users
7071
]
7172
metadata_fetch_awaitables: List[Awaitable[Any]] = []
7273

supertokens_python/recipe/dashboard/interfaces.py

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
from abc import ABC, abstractmethod
1717
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
1818

19+
from supertokens_python.types import User
20+
1921
from ...supertokens import AppInfo
20-
from ...types import APIResponse, User
21-
from .utils import DashboardConfig, GetUserForRecipeUser
22+
from ...types import APIResponse
23+
from .utils import DashboardConfig, UserWithMetadata
2224

2325
if TYPE_CHECKING:
2426
from supertokens_python.framework import BaseRequest, BaseResponse
@@ -69,28 +71,6 @@ def __init__(self):
6971
] = None
7072

7173

72-
class UserWithMetadata:
73-
def __init__(
74-
self,
75-
user: User,
76-
first_name: Optional[str] = None,
77-
last_name: Optional[str] = None,
78-
):
79-
self.user = user
80-
self.first_name = first_name
81-
self.last_name = last_name
82-
83-
def to_json(self) -> Dict[str, Any]:
84-
res = self.user.to_json()
85-
res["user"].update(
86-
{
87-
"firstName": self.first_name,
88-
"lastName": self.last_name,
89-
}
90-
)
91-
return res
92-
93-
9474
class DashboardUsersGetResponse(APIResponse):
9575
status: str = "OK"
9676

@@ -123,7 +103,7 @@ def to_json(self) -> Dict[str, Any]:
123103
class UserGetAPIOkResponse(APIResponse):
124104
status: str = "OK"
125105

126-
def __init__(self, recipe_id: str, user: GetUserForRecipeUser):
106+
def __init__(self, recipe_id: str, user: UserWithMetadata):
127107
self.recipe_id = recipe_id
128108
self.user = user
129109

supertokens_python/recipe/dashboard/utils.py

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from supertokens_python.recipe.thirdpartypasswordless.asyncio import (
3434
get_user_by_id as tppless_get_user_by_id,
3535
)
36+
from supertokens_python.types import User
3637
from supertokens_python.utils import Awaitable
3738

3839
from ...normalised_url_path import NormalisedURLPath
@@ -53,8 +54,84 @@
5354
if TYPE_CHECKING:
5455
from .interfaces import APIInterface, RecipeInterface
5556

56-
from supertokens_python.recipe.dashboard.interfaces import UserWithMetadata
57-
from supertokens_python.types import User
57+
58+
class UserWithMetadata:
59+
user_id: str
60+
time_joined: int
61+
recipe_id: Optional[str] = None
62+
email: Optional[str] = None
63+
phone_number: Optional[str] = None
64+
tp_info: Optional[Dict[str, Any]] = None
65+
first_name: Optional[str] = None
66+
last_name: Optional[str] = None
67+
68+
def from_user(
69+
self,
70+
user: User,
71+
first_name: Optional[str] = None,
72+
last_name: Optional[str] = None,
73+
):
74+
self.first_name = first_name
75+
self.last_name = last_name
76+
77+
self.user_id = user.user_id
78+
self.recipe_id = user.recipe_id
79+
self.time_joined = user.time_joined
80+
self.email = user.email
81+
self.phone_number = user.phone_number
82+
self.tp_info = (
83+
None if user.third_party_info is None else user.third_party_info.__dict__
84+
)
85+
86+
return self
87+
88+
def from_dict(
89+
self,
90+
user_dict: Dict[str, Any],
91+
first_name: Optional[str] = None,
92+
last_name: Optional[str] = None,
93+
):
94+
self.first_name = first_name
95+
self.last_name = last_name
96+
97+
self.user_id = user_dict["user_id"]
98+
self.recipe_id = user_dict.get("recipe_id")
99+
self.time_joined = user_dict["time_joined"]
100+
self.email = user_dict.get("email")
101+
self.phone_number = user_dict.get("phone_number")
102+
self.tp_info = (
103+
None
104+
if user_dict.get("third_party_info") is None
105+
else user_dict["third_party_info"].__dict__
106+
)
107+
108+
return self
109+
110+
def to_json(self) -> Dict[str, Any]:
111+
user_json = {
112+
"id": self.user_id,
113+
"timeJoined": self.time_joined,
114+
}
115+
if self.tp_info is not None:
116+
user_json["thirdParty"] = {
117+
"id": self.tp_info["id"],
118+
"userId": self.tp_info["user_id"],
119+
}
120+
if self.phone_number is not None:
121+
user_json["phoneNumber"] = self.phone_number
122+
if self.email is not None:
123+
user_json["email"] = self.email
124+
if self.first_name is not None:
125+
user_json["firstName"] = self.first_name
126+
if self.last_name is not None:
127+
user_json["lastName"] = self.last_name
128+
129+
if self.recipe_id is not None:
130+
return {
131+
"recipeId": self.recipe_id,
132+
"user": user_json,
133+
}
134+
return user_json
58135

59136

60137
class InputOverrideConfig:
@@ -161,35 +238,60 @@ def __init__(self, user: UserWithMetadata, recipe: str):
161238
self.recipe = recipe
162239

163240

241+
if TYPE_CHECKING:
242+
from supertokens_python.recipe.emailpassword.types import User as EmailPasswordUser
243+
from supertokens_python.recipe.passwordless.types import User as PasswordlessUser
244+
from supertokens_python.recipe.thirdparty.types import User as ThirdPartyUser
245+
from supertokens_python.recipe.thirdpartyemailpassword.types import (
246+
User as ThirdPartyEmailPasswordUser,
247+
)
248+
from supertokens_python.recipe.thirdpartypasswordless.types import (
249+
User as ThirdPartyPasswordlessUser,
250+
)
251+
252+
GetUserResult = Union[
253+
EmailPasswordUser,
254+
ThirdPartyUser,
255+
PasswordlessUser,
256+
None,
257+
ThirdPartyEmailPasswordUser,
258+
ThirdPartyPasswordlessUser,
259+
]
260+
261+
164262
async def get_user_for_recipe_id(
165263
user_id: str, recipe_id: str
166264
) -> Optional[GetUserForRecipeIdResult]:
167265
user: Optional[UserWithMetadata] = None
168266
recipe: Optional[str] = None
169267

170268
async def update_user_dict(
171-
get_user_func1: Callable[[str], Awaitable[Optional[User]]],
172-
get_user_func2: Callable[[str], Awaitable[Optional[User]]],
269+
get_user_func1: Callable[[str], Awaitable[GetUserResult]],
270+
get_user_func2: Callable[[str], Awaitable[GetUserResult]],
173271
recipe1: str,
174272
recipe2: str,
175273
):
176274
nonlocal user, user_id, recipe
177275

178276
try:
179-
matching_user = await get_user_func1(user_id) # type: ignore
277+
recipe_user = await get_user_func1(user_id) # type: ignore
180278

181-
if matching_user is not None:
182-
user = UserWithMetadata(matching_user, first_name="", last_name="")
279+
if recipe_user is not None:
280+
user = UserWithMetadata().from_dict(
281+
recipe_user.__dict__, first_name="", last_name=""
282+
)
183283
recipe = recipe1
184284
except Exception:
185285
pass
186286

187287
if user is None:
188288
try:
189-
matching_user = await get_user_func2(user_id)
289+
recipe_user = await get_user_func2(user_id)
190290

191-
if matching_user is not None:
192-
user = UserWithMetadata(matching_user, first_name="", last_name="")
291+
if recipe_user is not None:
292+
user = UserWithMetadata().from_dict(
293+
recipe_user.__dict__, first_name="", last_name=""
294+
)
193295
recipe = recipe2
194296
except Exception:
195297
pass

supertokens_python/types.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ def __init__(
4141
self.phone_number = phone_number
4242

4343
def to_json(self) -> Dict[str, Any]:
44-
4544
return {
4645
"recipeId": self.recipe_id,
4746
"user": {

0 commit comments

Comments
 (0)