From d35914c9689cdea5f96276bc5a8008190bcc766c Mon Sep 17 00:00:00 2001 From: Deepjyoti Barman Date: Tue, 24 Sep 2024 09:59:57 +0530 Subject: [PATCH] Add support for any type in value field instead of only string --- CHANGELOG.md | 5 +++ .../recipe/emailpassword/api/utils.py | 4 +- .../recipe/emailpassword/types.py | 7 ++-- .../recipe/emailpassword/utils.py | 19 +++++---- tests/emailpassword/test_signin.py | 42 +++++++++++++++++++ 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b3c6d9a8..b4a7610f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.24.3] - 2024-09-24 + +- Adds support for form field related improvements by making fields accept any type of values +- Adds support for optional fields to properly optional + ## [0.24.2] - 2024-09-03 - Makes optional input form fields truly optional instead of just being able to accept `""`. diff --git a/supertokens_python/recipe/emailpassword/api/utils.py b/supertokens_python/recipe/emailpassword/api/utils.py index 989b017a9..536fec7d4 100644 --- a/supertokens_python/recipe/emailpassword/api/utils.py +++ b/supertokens_python/recipe/emailpassword/api/utils.py @@ -41,7 +41,9 @@ async def validate_form_or_throw_error( input_field: Union[None, FormField] = find_first_occurrence_in_list( lambda x: x.id == field.id, inputs ) - is_invalid_value = input_field is None or input_field.value == "" + is_invalid_value = input_field is None or ( + isinstance(input_field.value, str) and input_field.value == "" + ) if not field.optional and is_invalid_value: validation_errors.append(ErrorFormField(field.id, "Field is not optional")) continue diff --git a/supertokens_python/recipe/emailpassword/types.py b/supertokens_python/recipe/emailpassword/types.py index 41752c2cd..ef3d4b6f9 100644 --- a/supertokens_python/recipe/emailpassword/types.py +++ b/supertokens_python/recipe/emailpassword/types.py @@ -12,7 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. from __future__ import annotations -from typing import Awaitable, Callable, List, TypeVar, Union + +from typing import Any, Awaitable, Callable, List, TypeVar, Union from supertokens_python.ingredients.emaildelivery import EmailDeliveryIngredient from supertokens_python.ingredients.emaildelivery.types import ( @@ -53,9 +54,9 @@ def __init__(self, id: str, error: str): # pylint: disable=redefined-builtin class FormField: - def __init__(self, id: str, value: str): # pylint: disable=redefined-builtin + def __init__(self, id: str, value: Any): # pylint: disable=redefined-builtin self.id: str = id - self.value: str = value + self.value: Any = value class InputFormField: diff --git a/supertokens_python/recipe/emailpassword/utils.py b/supertokens_python/recipe/emailpassword/utils.py index 390fc49f4..8d349c1d1 100644 --- a/supertokens_python/recipe/emailpassword/utils.py +++ b/supertokens_python/recipe/emailpassword/utils.py @@ -14,9 +14,9 @@ from __future__ import annotations from re import fullmatch -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union, Dict -from supertokens_python.framework import BaseRequest +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union +from supertokens_python.framework import BaseRequest from supertokens_python.ingredients.emaildelivery.types import ( EmailDeliveryConfig, EmailDeliveryConfigWithService, @@ -26,17 +26,14 @@ ) from .interfaces import APIInterface, RecipeInterface -from .types import InputFormField, NormalisedFormField, EmailTemplateVars +from .types import EmailTemplateVars, InputFormField, NormalisedFormField if TYPE_CHECKING: from supertokens_python.supertokens import AppInfo from supertokens_python.utils import get_filtered_list -from .constants import ( - FORM_FIELD_EMAIL_ID, - FORM_FIELD_PASSWORD_ID, -) +from .constants import FORM_FIELD_EMAIL_ID, FORM_FIELD_PASSWORD_ID async def default_validator(_: str, __: str) -> Union[str, None]: @@ -261,10 +258,14 @@ def validate_and_normalise_user_input( email_delivery: Union[EmailDeliveryConfig[EmailTemplateVars], None] = None, ) -> EmailPasswordConfig: - if sign_up_feature is not None and not isinstance(sign_up_feature, InputSignUpFeature): # type: ignore + # type: ignore + if sign_up_feature is not None and not isinstance( + sign_up_feature, InputSignUpFeature + ): raise ValueError("sign_up_feature must be of type InputSignUpFeature or None") - if override is not None and not isinstance(override, InputOverrideConfig): # type: ignore + # type: ignore + if override is not None and not isinstance(override, InputOverrideConfig): raise ValueError("override must be of type InputOverrideConfig or None") if override is None: diff --git a/tests/emailpassword/test_signin.py b/tests/emailpassword/test_signin.py index c27174dd2..e2e5b3575 100644 --- a/tests/emailpassword/test_signin.py +++ b/tests/emailpassword/test_signin.py @@ -719,6 +719,48 @@ async def test_optional_custom_field_without_input(driver_config_client: TestCli assert dict_response["status"] == "OK" +@mark.asyncio +async def test_non_optional_custom_field_with_boolean_value( + driver_config_client: TestClient, +): + init( + supertokens_config=SupertokensConfig("http://localhost:3567"), + app_info=InputAppInfo( + app_name="SuperTokens Demo", + api_domain="http://api.supertokens.io", + website_domain="http://supertokens.io", + api_base_path="/auth", + ), + framework="fastapi", + recipe_list=[ + emailpassword.init( + sign_up_feature=emailpassword.InputSignUpFeature( + form_fields=[ + emailpassword.InputFormField("autoVerify", optional=False) + ] + ) + ), + session.init(get_token_transfer_method=lambda _, __, ___: "cookie"), + ], + ) + start_st() + + response_1 = driver_config_client.post( + url="/auth/signup", + headers={"Content-Type": "application/json"}, + json={ + "formFields": [ + {"id": "email", "value": "random@gmail.com"}, + {"id": "password", "value": "validpassword123"}, + {"id": "autoVerify", "value": False}, + ] + }, + ) + assert response_1.status_code == 200 + dict_response = json.loads(response_1.text) + assert dict_response["status"] == "OK" + + @mark.asyncio async def test_too_many_fields(driver_config_client: TestClient): init(