Skip to content

Commit

Permalink
more apis
Browse files Browse the repository at this point in the history
  • Loading branch information
rishabhpoddar committed Sep 17, 2024
1 parent 9a4fa38 commit 06e5d86
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ async def handle_user_email_verify_get(
user_context: Dict[str, Any],
) -> Union[UserEmailVerifyGetAPIResponse, FeatureNotEnabledError]:
req = api_options.request
user_id = req.get_query_param("userId")
recipe_user_id = req.get_query_param("recipeUserId")

if user_id is None:
raise_bad_input_exception("Missing required parameter 'userId'")
if recipe_user_id is None:
raise_bad_input_exception("Missing required parameter 'recipeUserId'")

try:
EmailVerificationRecipe.get_instance_or_throw()
except Exception:
return FeatureNotEnabledError()

is_verified = await is_email_verified(
RecipeUserId(user_id), user_context=user_context
RecipeUserId(recipe_user_id), user_context=user_context
)
return UserEmailVerifyGetAPIResponse(is_verified)
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ async def handle_user_email_verify_put(
user_context: Dict[str, Any],
) -> UserEmailVerifyPutAPIResponse:
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
user_id = request_body.get("userId")
recipe_user_id = request_body.get("recipeUserId")
verified = request_body.get("verified")

if user_id is None or not isinstance(user_id, str):
if recipe_user_id is None or not isinstance(recipe_user_id, str):
raise_bad_input_exception(
"Required parameter 'userId' is missing or has an invalid type"
"Required parameter 'recipeUserId' is missing or has an invalid type"
)

if verified is None or not isinstance(verified, bool):
Expand All @@ -43,7 +43,7 @@ async def handle_user_email_verify_put(
if verified:
token_response = await create_email_verification_token(
tenant_id=tenant_id,
recipe_user_id=RecipeUserId(user_id),
recipe_user_id=RecipeUserId(recipe_user_id),
email=None,
user_context=user_context,
)
Expand All @@ -62,6 +62,6 @@ async def handle_user_email_verify_put(
raise Exception("Should not come here")

else:
await unverify_email(RecipeUserId(user_id), user_context=user_context)
await unverify_email(RecipeUserId(recipe_user_id), user_context=user_context)

return UserEmailVerifyPutAPIResponse()
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Any, Dict, Union
from supertokens_python.asyncio import get_user

from supertokens_python.exceptions import raise_bad_input_exception
from supertokens_python.recipe.emailverification.asyncio import (
Expand Down Expand Up @@ -26,17 +27,22 @@ async def handle_email_verify_token_post(
UserEmailVerifyTokenPostAPIEmailAlreadyVerifiedErrorResponse,
]:
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
user_id = request_body.get("userId")
recipe_user_id = request_body.get("recipeUserId")

if user_id is None or not isinstance(user_id, str):
if recipe_user_id is None or not isinstance(recipe_user_id, str):
raise_bad_input_exception(
"Required parameter 'userId' is missing or has an invalid type"
"Required parameter 'recipeUserId' is missing or has an invalid type"
)

user = await get_user(recipe_user_id, user_context)

if user is None:
raise_bad_input_exception("User not found")

res = await send_email_verification_email(
tenant_id=tenant_id,
user_id=user_id,
recipe_user_id=RecipeUserId(user_id),
user_id=user.id,
recipe_user_id=RecipeUserId(recipe_user_id),
email=None,
user_context=user_context,
)
Expand Down
44 changes: 14 additions & 30 deletions supertokens_python/recipe/dashboard/api/userdetails/user_get.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,51 @@
from typing import Union, Dict, Any
from supertokens_python.asyncio import get_user

from supertokens_python.exceptions import raise_bad_input_exception
from supertokens_python.recipe.dashboard.utils import get_user_for_recipe_id
from supertokens_python.recipe.dashboard.utils import (
UserWithMetadata,
)
from supertokens_python.recipe.usermetadata import UserMetadataRecipe
from supertokens_python.recipe.usermetadata.asyncio import get_user_metadata
from supertokens_python.types import RecipeUserId

from ...interfaces import (
APIInterface,
APIOptions,
UserGetAPINoUserFoundError,
UserGetAPIOkResponse,
UserGetAPIRecipeNotInitialisedError,
)
from ...utils import is_recipe_initialised, is_valid_recipe_id


async def handle_user_get(
_api_interface: APIInterface,
_tenant_id: str,
api_options: APIOptions,
_user_context: Dict[str, Any],
) -> Union[
UserGetAPINoUserFoundError,
UserGetAPIOkResponse,
UserGetAPIRecipeNotInitialisedError,
]:
) -> Union[UserGetAPINoUserFoundError, UserGetAPIOkResponse,]:
user_id = api_options.request.get_query_param("userId")
recipe_id = api_options.request.get_query_param("recipeId")

if user_id is None:
raise_bad_input_exception("Missing required parameter 'userId'")

if recipe_id is None:
raise_bad_input_exception("Missing required parameter 'recipeId'")

if not is_valid_recipe_id(recipe_id):
raise_bad_input_exception("Invalid recipe id")

if not is_recipe_initialised(recipe_id):
return UserGetAPIRecipeNotInitialisedError()

user_response = await get_user_for_recipe_id(
RecipeUserId(user_id), recipe_id, _user_context
)
if user_response.user is None:
user_response = await get_user(user_id, _user_context)
if user_response is None:
return UserGetAPINoUserFoundError()

user = user_response.user
user_with_metadata: UserWithMetadata = UserWithMetadata().from_user(user_response)

try:
UserMetadataRecipe.get_instance()
except Exception:
user.first_name = "FEATURE_NOT_ENABLED"
user.last_name = "FEATURE_NOT_ENABLED"
user_with_metadata.first_name = "FEATURE_NOT_ENABLED"
user_with_metadata.last_name = "FEATURE_NOT_ENABLED"

return UserGetAPIOkResponse(recipe_id, user)
return UserGetAPIOkResponse(user_with_metadata)

user_metadata = await get_user_metadata(user_id, user_context=_user_context)
first_name = user_metadata.metadata.get("first_name", "")
last_name = user_metadata.metadata.get("last_name", "")

user.first_name = first_name
user.last_name = last_name
user_with_metadata.first_name = first_name
user_with_metadata.last_name = last_name

return UserGetAPIOkResponse(recipe_id, user)
return UserGetAPIOkResponse(user_with_metadata)
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
from typing import Any, Dict, List, Union
from typing import Any, Dict, Union

from supertokens_python.exceptions import raise_bad_input_exception
from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
from supertokens_python.recipe.emailpassword.constants import FORM_FIELD_PASSWORD_ID
from supertokens_python.recipe.emailpassword.interfaces import (
PasswordPolicyViolationError,
UnknownUserIdError,
PasswordResetTokenInvalidError,
)
from supertokens_python.recipe.emailpassword.asyncio import (
create_reset_password_token,
reset_password_using_token,
)
from supertokens_python.recipe.emailpassword.types import NormalisedFormField
from supertokens_python.types import RecipeUserId

from ...interfaces import (
APIInterface,
Expand All @@ -28,52 +23,33 @@ async def handle_user_password_put(
user_context: Dict[str, Any],
) -> Union[UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse]:
request_body: Dict[str, Any] = await api_options.request.json() # type: ignore
user_id = request_body.get("userId")
recipe_user_id = request_body.get("recipeUserId")
new_password = request_body.get("newPassword")

if user_id is None or not isinstance(user_id, str):
raise_bad_input_exception("Missing required parameter 'userId'")
if recipe_user_id is None or not isinstance(recipe_user_id, str):
raise_bad_input_exception("Missing required parameter 'recipeUserId'")

if new_password is None or not isinstance(new_password, str):
raise_bad_input_exception("Missing required parameter 'newPassword'")

async def reset_password(
form_fields: List[NormalisedFormField],
) -> Union[
UserPasswordPutAPIResponse, UserPasswordPutAPIInvalidPasswordErrorResponse
]:
password_form_field = [
field for field in form_fields if field.id == FORM_FIELD_PASSWORD_ID
][0]

password_validation_error = await password_form_field.validate(
new_password, tenant_id
)

if password_validation_error is not None:
return UserPasswordPutAPIInvalidPasswordErrorResponse(
password_validation_error
)

password_reset_token = await create_reset_password_token(
tenant_id, user_id, "", user_context
email_password_recipe = EmailPasswordRecipe.get_instance()
update_response = (
await email_password_recipe.recipe_implementation.update_email_or_password(
recipe_user_id=RecipeUserId(recipe_user_id),
email=None,
password=new_password,
apply_password_policy=True,
tenant_id_for_password_policy=tenant_id,
user_context=user_context,
)
)

if isinstance(password_reset_token, UnknownUserIdError):
# Techincally it can but its an edge case so we assume that it wont
# UNKNOWN_USER_ID_ERROR
raise Exception("Should never come here")

password_reset_response = await reset_password_using_token(
tenant_id, password_reset_token.token, new_password, user_context
if isinstance(update_response, PasswordPolicyViolationError):
return UserPasswordPutAPIInvalidPasswordErrorResponse(
error=update_response.failure_reason
)

if isinstance(password_reset_response, PasswordResetTokenInvalidError):
# RESET_PASSWORD_INVALID_TOKEN_ERROR
raise Exception("Should not come here")
if isinstance(update_response, UnknownUserIdError):
raise Exception("Should never come here")

return UserPasswordPutAPIResponse()

return await reset_password(
EmailPasswordRecipe.get_instance().config.sign_up_feature.form_fields,
)
return UserPasswordPutAPIResponse()
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from supertokens_python.exceptions import raise_bad_input_exception
from supertokens_python.recipe.dashboard.utils import (
get_user_for_recipe_id,
is_valid_recipe_id,
)
from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
from supertokens_python.recipe.emailpassword.asyncio import (
Expand Down Expand Up @@ -195,9 +194,6 @@ async def handle_user_put(
"Required parameter 'recipeId' is missing or has an invalid type"
)

if not is_valid_recipe_id(recipe_id):
raise_bad_input_exception("Invalid recipe id")

if first_name is None and not isinstance(first_name, str):
raise_bad_input_exception(
"Required parameter 'firstName' is missing or has an invalid type"
Expand Down
11 changes: 1 addition & 10 deletions supertokens_python/recipe/dashboard/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,12 @@ def to_json(self) -> Dict[str, Any]:
class UserGetAPIOkResponse(APIResponse):
status: str = "OK"

def __init__(self, recipe_id: str, user: UserWithMetadata):
self.recipe_id = recipe_id
def __init__(self, user: UserWithMetadata):
self.user = user

def to_json(self) -> Dict[str, Any]:
return {
"status": self.status,
"recipeId": self.recipe_id,
"user": self.user.to_json(),
}

Expand All @@ -159,13 +157,6 @@ def to_json(self) -> Dict[str, Any]:
return {"status": self.status}


class UserGetAPIRecipeNotInitialisedError(APIResponse):
status: str = "RECIPE_NOT_INITIALISED"

def to_json(self) -> Dict[str, Any]:
return {"status": self.status}


class FeatureNotEnabledError(APIResponse):
status: str = "FEATURE_NOT_ENABLED_ERROR"

Expand Down
31 changes: 0 additions & 31 deletions supertokens_python/recipe/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,6 @@ def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
return None


def is_valid_recipe_id(recipe_id: str) -> bool:
return recipe_id in ("emailpassword", "thirdparty", "passwordless")


class GetUserForRecipeIdHelperResult:
def __init__(
self, user: Optional[AccountLinkingUser] = None, recipe: Optional[str] = None
Expand Down Expand Up @@ -265,33 +261,6 @@ async def _get_user_for_recipe_id(
return GetUserForRecipeIdHelperResult(user=user, recipe=recipe)


def is_recipe_initialised(recipeId: str) -> bool:
isRecipeInitialised: bool = False

if recipeId == EmailPasswordRecipe.recipe_id:
try:
EmailPasswordRecipe.get_instance()
isRecipeInitialised = True
except Exception:
pass

elif recipeId == PasswordlessRecipe.recipe_id:
try:
PasswordlessRecipe.get_instance()
isRecipeInitialised = True
except Exception:
pass

elif recipeId == ThirdPartyRecipe.recipe_id:
try:
ThirdPartyRecipe.get_instance()
isRecipeInitialised = True
except Exception:
pass

return isRecipeInitialised


async def validate_api_key(
req: BaseRequest, config: DashboardConfig, _user_context: Dict[str, Any]
) -> bool:
Expand Down

0 comments on commit 06e5d86

Please sign in to comment.