Skip to content

Commit 2e8e989

Browse files
committed
fix: Make the dashboard functions strongly typed
1 parent f8b0248 commit 2e8e989

15 files changed

+220
-178
lines changed

supertokens_python/recipe/dashboard/api/userdetails/user_delete.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from ...interfaces import APIInterface, APIOptions, APIResponse, UserDeleteAPIResponse
1+
from ...interfaces import APIInterface, APIOptions, UserDeleteAPIResponse
22
from supertokens_python.exceptions import raise_bad_input_exception
33
from supertokens_python import Supertokens
44

55

66
async def handle_user_delete(
77
_api_interface: APIInterface, api_options: APIOptions
8-
) -> APIResponse:
8+
) -> UserDeleteAPIResponse:
99
user_id = api_options.request.get_query_param("userId")
1010

1111
if user_id is None:

supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_get.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
FeatureNotEnabledError,
99
)
1010

11-
from supertokens_python.types import APIResponse
11+
from typing import Union
1212

1313

1414
async def handle_user_email_verify_get(
1515
_api_interface: APIInterface, api_options: APIOptions
16-
) -> APIResponse:
16+
) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
1717
req = api_options.request
1818
user_id = req.get_query_param("userId")
1919

supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_put.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414
from ...interfaces import (
1515
APIInterface,
1616
APIOptions,
17-
APIResponse,
1817
UserEmailVerifyPutAPIResponse,
1918
)
2019

2120

2221
async def handle_user_email_verify_put(
2322
_api_interface: APIInterface, api_options: APIOptions
24-
) -> APIResponse:
23+
) -> UserEmailVerifyPutAPIResponse:
2524
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
2625
user_id = request_body.get("userId")
2726
verified = request_body.get("verified")

supertokens_python/recipe/dashboard/api/userdetails/user_email_verify_token_post.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict
1+
from typing import Any, Dict, Union
22

33
from supertokens_python.exceptions import raise_bad_input_exception
44
from supertokens_python.recipe.emailverification.asyncio import (
@@ -13,34 +13,26 @@
1313
EmailVerificationRecipe,
1414
GetEmailForUserIdOkResult,
1515
)
16-
from supertokens_python.recipe.emailverification.utils import get_email_verify_link
17-
from supertokens_python.types import APIResponse
1816
from supertokens_python.recipe.emailverification.types import (
1917
VerificationEmailTemplateVars,
2018
VerificationEmailTemplateVarsUser,
2119
)
20+
from supertokens_python.recipe.emailverification.utils import get_email_verify_link
2221

23-
from ...interfaces import APIInterface, APIOptions, APIResponse
24-
25-
26-
class UserEmailVerifyTokenPostAPIOkResponse(APIResponse):
27-
# TODO: Move to interfaces.py
28-
status: str = "OK"
29-
30-
def to_json(self) -> Dict[str, Any]:
31-
return {"status": self.status}
32-
33-
34-
class UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse(APIResponse):
35-
status: str = "EMAIL_ALREADY_VERIFIED_ERROR"
36-
37-
def to_json(self) -> Dict[str, Any]:
38-
return {"status": self.status}
22+
from ...interfaces import (
23+
APIInterface,
24+
APIOptions,
25+
UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
26+
UserEmailVerifyTokenPostAPIOkResponse,
27+
)
3928

4029

4130
async def handle_email_verify_token_post(
4231
_api_interface: APIInterface, api_options: APIOptions
43-
) -> APIResponse:
32+
) -> Union[
33+
UserEmailVerifyTokenPostAPIOkResponse,
34+
UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
35+
]:
4436
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
4537
user_id = request_body.get("userId")
4638

supertokens_python/recipe/dashboard/api/userdetails/user_get.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from supertokens_python.recipe.usermetadata import UserMetadataRecipe
44
from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
5-
from supertokens_python.types import APIResponse
65

76
from supertokens_python.recipe.dashboard.utils import get_user_for_recipe_id
87

@@ -13,11 +12,12 @@
1312
UserGetAPIOkResponse,
1413
)
1514
from ...utils import is_valid_recipe_id
15+
from typing import Union
1616

1717

1818
async def handle_user_get(
1919
_api_interface: APIInterface, api_options: APIOptions
20-
) -> APIResponse:
20+
) -> Union[UserGetAPINoUserFoundError, UserGetAPIOkResponse]:
2121
user_id = api_options.request.get_query_param("userId")
2222
recipe_id = api_options.request.get_query_param("recipeId")
2323

@@ -30,34 +30,29 @@ async def handle_user_get(
3030
if not is_valid_recipe_id(recipe_id):
3131
raise_bad_input_exception("Invalid recipe id")
3232

33-
user = (await get_user_for_recipe_id(user_id, recipe_id)).get("user")
34-
35-
if user is None:
33+
user_response = await get_user_for_recipe_id(user_id, recipe_id)
34+
if user_response is None:
3635
return UserGetAPINoUserFoundError()
3736

37+
user = user_response.user
38+
3839
# FIXME: Shouldn't be required, no?
3940
recipe_id_: str = recipe_id # type: ignore
4041
user_id_: str = user_id # type: ignore
4142

4243
try:
4344
UserMetadataRecipe.get_instance()
4445
except Exception:
45-
user = {
46-
**user,
47-
"firstName": "FEATURE_NOT_ENABLED",
48-
"lastName": "FEATURE_NOT_ENABLED",
49-
}
46+
user.first_name = "FEATURE_NOT_ENABLED"
47+
user.last_name = "FEATURE_NOT_ENABLED"
5048

5149
return UserGetAPIOkResponse(recipe_id_, user)
5250

5351
user_metadata = await get_user_metadata(user_id_)
5452
first_name = user_metadata.metadata.get("first_name", "")
5553
last_name = user_metadata.metadata.get("last_name", "")
5654

57-
user = {
58-
**user,
59-
"firstName": first_name,
60-
"lastName": last_name,
61-
}
55+
user.first_name = first_name
56+
user.last_name = last_name
6257

6358
return UserGetAPIOkResponse(recipe_id_, user)

supertokens_python/recipe/dashboard/api/userdetails/user_metadata_get.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
FeatureNotEnabledError,
55
UserMetadataGetAPIOkResponse,
66
)
7-
from supertokens_python.types import APIResponse
87
from supertokens_python.exceptions import raise_bad_input_exception
98
from supertokens_python.recipe.usermetadata import UserMetadataRecipe
109
from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
10+
from typing import Union
1111

1212

1313
async def handle_metadata_get(
1414
_api_interface: APIInterface, api_options: APIOptions
15-
) -> APIResponse:
15+
) -> Union[UserMetadataGetAPIOkResponse, FeatureNotEnabledError]:
1616
user_id = api_options.request.get_query_param("userId")
1717

1818
if user_id is None:

supertokens_python/recipe/dashboard/api/userdetails/user_metadata_put.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,13 @@
77
clear_user_metadata,
88
update_user_metadata,
99
)
10-
from supertokens_python.types import APIResponse
1110

12-
from ...interfaces import APIInterface, APIOptions, APIResponse
13-
14-
15-
class UserMetadataPutAPIResponse(APIResponse):
16-
status: str = "OK"
17-
18-
def to_json(self) -> Dict[str, Any]:
19-
return {"status": self.status}
11+
from ...interfaces import APIInterface, APIOptions, UserMetadataPutAPIResponse
2012

2113

2214
async def handle_metadata_put(
2315
_api_interface: APIInterface, api_options: APIOptions
24-
) -> APIResponse:
16+
) -> UserMetadataPutAPIResponse:
2517
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
2618
user_id = request_body.get("userId")
2719
data = request_body.get("data")
@@ -39,16 +31,15 @@ async def handle_metadata_put(
3931
"Required parameter 'data' is missing or has an invalid type"
4032
)
4133

34+
parsed_data: Dict[str, Any] = {}
4235
try:
4336
parsed_data = json.loads(data)
44-
45-
if not isinstance(parsed_data, dict) or parsed_data is None:
37+
if not isinstance(parsed_data, dict): # type: ignore
4638
raise Exception()
4739

4840
except Exception:
4941
raise_bad_input_exception("'data' must be a valid JSON body")
5042

51-
#
5243
# This API is meant to set the user metadata of a user. We delete the existing data
5344
# before updating it because we want to make sure that shallow merging does not result
5445
# in the data being incorrect
@@ -59,10 +50,7 @@ async def handle_metadata_put(
5950
#
6051
# Removing first ensures that the final data is exactly what the user wanted it to be
6152

62-
# FIXME: This Shouldn't be required
63-
parsed_data_: Dict[str, Any] = parsed_data # type: ignore
64-
6553
await clear_user_metadata(user_id)
66-
await update_user_metadata(user_id, parsed_data_)
54+
await update_user_metadata(user_id, parsed_data)
6755

6856
return UserMetadataPutAPIResponse()

supertokens_python/recipe/dashboard/api/userdetails/user_password_put.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, List, Union
1+
from typing import Any, Callable, Dict, List, Union
22

33
from supertokens_python.exceptions import raise_bad_input_exception
44
from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
@@ -10,8 +10,10 @@
1010
)
1111
from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_PASSWORD_ID
1212
from supertokens_python.recipe.emailpassword.interfaces import (
13+
CreateResetPasswordOkResult,
1314
CreateResetPasswordWrongUserIdError,
1415
ResetPasswordUsingTokenInvalidTokenError,
16+
ResetPasswordUsingTokenOkResult,
1517
)
1618
from supertokens_python.recipe.emailpassword.types import NormalisedFormField
1719
from supertokens_python.recipe.thirdpartyemailpassword import (
@@ -23,7 +25,7 @@
2325
from supertokens_python.recipe.thirdpartyemailpassword.asyncio import (
2426
reset_password_using_token as tpep_reset_password_using_token,
2527
)
26-
from supertokens_python.types import APIResponse
28+
from supertokens_python.utils import Awaitable
2729
from typing_extensions import Literal
2830

2931
from ...interfaces import (
@@ -36,7 +38,7 @@
3638

3739
async def handle_user_password_put(
3840
_api_interface: APIInterface, api_options: APIOptions
39-
) -> APIResponse:
41+
) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
4042
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
4143
user_id = request_body.get("userId")
4244
new_password = request_body.get("newPassword")
@@ -69,9 +71,24 @@ async def handle_user_password_put(
6971

7072
async def reset_password(
7173
recipe: Any, # FIXME
72-
create_reset_password_token: Any, # FIXME
73-
reset_password_using_token: Any, # FIXME
74-
) -> APIResponse:
74+
create_reset_password_token: Callable[
75+
[str],
76+
Awaitable[
77+
Union[CreateResetPasswordOkResult, CreateResetPasswordWrongUserIdError]
78+
],
79+
],
80+
reset_password_using_token: Callable[
81+
[str, str],
82+
Awaitable[
83+
Union[
84+
ResetPasswordUsingTokenOkResult,
85+
ResetPasswordUsingTokenInvalidTokenError,
86+
]
87+
],
88+
],
89+
) -> Union[
90+
UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
91+
]:
7592
form_fields: List[NormalisedFormField] = recipe.get_instance().config.sign_up_feature.form_fields # type: ignore # FIXME?
7693
password_form_field = [
7794
field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
@@ -91,10 +108,8 @@ async def reset_password(
91108
# UNKNOWN_USER_ID_ERROR FIXME
92109
raise Exception("Should never come here")
93110

94-
password_reset_response = (
95-
await reset_password_using_token( # type; ignore # FIXME
96-
password_reset_token.token, new_password
97-
)
111+
password_reset_response = await reset_password_using_token(
112+
password_reset_token.token, new_password
98113
)
99114

100115
if isinstance(

0 commit comments

Comments
 (0)