mirror of https://github.com/zulip/zulip.git
types: Fix declared type of custom profile field values.
None of the existing custom profile field types have the value as an integer like declared in many places - nor is it a string like currently decalred in types.py. The correct type is Union[str, List[int]]. Rather than tracking this in so many places throughout the codebase, we add a new ProfileDataElementValue type and insert it where appropriate.
This commit is contained in:
parent
91ea21a3fc
commit
cacff28578
|
@ -169,7 +169,7 @@ from zerver.lib.topic import (
|
|||
update_messages_for_topic_edit,
|
||||
)
|
||||
from zerver.lib.topic_mutes import add_topic_mute, get_topic_mutes, remove_topic_mute
|
||||
from zerver.lib.types import ProfileFieldData
|
||||
from zerver.lib.types import ProfileDataElementValue, ProfileFieldData
|
||||
from zerver.lib.upload import (
|
||||
claim_attachment,
|
||||
delete_avatar_image,
|
||||
|
@ -7738,7 +7738,7 @@ def notify_user_update_custom_profile_data(
|
|||
|
||||
def do_update_user_custom_profile_data_if_changed(
|
||||
user_profile: UserProfile,
|
||||
data: List[Dict[str, Union[int, str, List[int]]]],
|
||||
data: List[Dict[str, Union[int, ProfileDataElementValue]]],
|
||||
) -> None:
|
||||
with transaction.atomic():
|
||||
for custom_profile_field in data:
|
||||
|
|
|
@ -14,6 +14,9 @@ ExtendedValidator = Callable[[str, str, object], str]
|
|||
RealmUserValidator = Callable[[int, object, bool], List[int]]
|
||||
|
||||
|
||||
ProfileDataElementValue = Union[str, List[int]]
|
||||
|
||||
|
||||
class ProfileDataElementBase(TypedDict):
|
||||
id: int
|
||||
name: str
|
||||
|
@ -24,13 +27,13 @@ class ProfileDataElementBase(TypedDict):
|
|||
|
||||
|
||||
class ProfileDataElement(ProfileDataElementBase):
|
||||
value: str
|
||||
value: ProfileDataElementValue
|
||||
rendered_value: Optional[str]
|
||||
|
||||
|
||||
ProfileData = List[ProfileDataElement]
|
||||
|
||||
FieldElement = Tuple[int, Promise, Validator[Union[int, str, List[int]]], Callable[[Any], Any], str]
|
||||
FieldElement = Tuple[int, Promise, Validator[ProfileDataElementValue], Callable[[Any], Any], str]
|
||||
ExtendedFieldElement = Tuple[int, Promise, ExtendedValidator, Callable[[Any], Any], str]
|
||||
UserFieldElement = Tuple[int, Promise, RealmUserValidator, Callable[[Any], Any], str]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import re
|
||||
import unicodedata
|
||||
from collections import defaultdict
|
||||
from typing import Any, Dict, List, Optional, Sequence, Union
|
||||
from typing import Any, Dict, List, Optional, Sequence, Union, cast
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -20,6 +20,7 @@ from zerver.lib.cache import (
|
|||
)
|
||||
from zerver.lib.exceptions import JsonableError, OrganizationAdministratorRequired
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
from zerver.lib.types import ProfileDataElementValue
|
||||
from zerver.models import (
|
||||
CustomProfileField,
|
||||
CustomProfileFieldValue,
|
||||
|
@ -310,8 +311,8 @@ def get_all_api_keys(user_profile: UserProfile) -> List[str]:
|
|||
|
||||
|
||||
def validate_user_custom_profile_field(
|
||||
realm_id: int, field: CustomProfileField, value: Union[int, str, List[int]]
|
||||
) -> Union[int, str, List[int]]:
|
||||
realm_id: int, field: CustomProfileField, value: ProfileDataElementValue
|
||||
) -> ProfileDataElementValue:
|
||||
validators = CustomProfileField.FIELD_VALIDATORS
|
||||
field_type = field.field_type
|
||||
var_name = f"{field.name}"
|
||||
|
@ -332,7 +333,7 @@ def validate_user_custom_profile_field(
|
|||
|
||||
|
||||
def validate_user_custom_profile_data(
|
||||
realm_id: int, profile_data: List[Dict[str, Union[int, str, List[int]]]]
|
||||
realm_id: int, profile_data: List[Dict[str, Union[int, ProfileDataElementValue]]]
|
||||
) -> None:
|
||||
# This function validate all custom field values according to their field type.
|
||||
for item in profile_data:
|
||||
|
@ -343,7 +344,9 @@ def validate_user_custom_profile_data(
|
|||
raise JsonableError(_("Field id {id} not found.").format(id=field_id))
|
||||
|
||||
try:
|
||||
validate_user_custom_profile_field(realm_id, field, item["value"])
|
||||
validate_user_custom_profile_field(
|
||||
realm_id, field, cast(ProfileDataElementValue, item["value"])
|
||||
)
|
||||
except ValidationError as error:
|
||||
raise JsonableError(error.message)
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ from zerver.lib.types import (
|
|||
LinkifierDict,
|
||||
ProfileData,
|
||||
ProfileDataElementBase,
|
||||
ProfileDataElementValue,
|
||||
RealmUserValidator,
|
||||
UserFieldElement,
|
||||
Validator,
|
||||
|
@ -3904,7 +3905,7 @@ class CustomProfileField(models.Model):
|
|||
|
||||
ALL_FIELD_TYPES = [*FIELD_TYPE_DATA, *SELECT_FIELD_TYPE_DATA, *USER_FIELD_TYPE_DATA]
|
||||
|
||||
FIELD_VALIDATORS: Dict[int, Validator[Union[int, str, List[int]]]] = {
|
||||
FIELD_VALIDATORS: Dict[int, Validator[ProfileDataElementValue]] = {
|
||||
item[0]: item[2] for item in FIELD_TYPE_DATA
|
||||
}
|
||||
FIELD_CONVERTERS: Dict[int, Callable[[Any], Any]] = {
|
||||
|
|
|
@ -566,7 +566,7 @@ class UpdateCustomProfileFieldTest(CustomProfileFieldTestCase):
|
|||
self.assert_error_update_invalid_value(
|
||||
field_name, "1909-3-5", f"{field_name} is not a date"
|
||||
)
|
||||
self.assert_error_update_invalid_value(field_name, 123, f"{field_name} is not a string")
|
||||
self.assert_error_update_invalid_value(field_name, [123], f"{field_name} is not a string")
|
||||
|
||||
def test_update_invalid_url(self) -> None:
|
||||
field_name = "Favorite website"
|
||||
|
|
|
@ -20,7 +20,7 @@ from zerver.lib.exceptions import JsonableError
|
|||
from zerver.lib.external_accounts import validate_external_account_field_data
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.types import ProfileFieldData
|
||||
from zerver.lib.types import ProfileDataElementValue, ProfileFieldData
|
||||
from zerver.lib.users import validate_user_custom_profile_data
|
||||
from zerver.lib.validator import (
|
||||
check_capped_string,
|
||||
|
@ -196,12 +196,12 @@ def remove_user_custom_profile_data(
|
|||
def update_user_custom_profile_data(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
data: List[Dict[str, Union[int, str, List[int]]]] = REQ(
|
||||
data: List[Dict[str, Union[int, ProfileDataElementValue]]] = REQ(
|
||||
json_validator=check_list(
|
||||
check_dict_only(
|
||||
[
|
||||
("id", check_int),
|
||||
("value", check_union([check_int, check_string, check_list(check_int)])),
|
||||
("value", check_union([check_string, check_list(check_int)])),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
|
|
@ -38,7 +38,7 @@ from zerver.lib.integrations import EMBEDDED_BOTS
|
|||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.streams import access_stream_by_id, access_stream_by_name, subscribed_to_stream
|
||||
from zerver.lib.types import Validator
|
||||
from zerver.lib.types import ProfileDataElementValue, Validator
|
||||
from zerver.lib.upload import upload_avatar_image
|
||||
from zerver.lib.url_encoding import add_query_arg_to_redirect_url
|
||||
from zerver.lib.users import (
|
||||
|
@ -141,14 +141,16 @@ def reactivate_user_backend(
|
|||
return json_success()
|
||||
|
||||
|
||||
check_profile_data: Validator[List[Dict[str, Optional[Union[int, str, List[int]]]]]] = check_list(
|
||||
check_profile_data: Validator[
|
||||
List[Dict[str, Optional[Union[int, ProfileDataElementValue]]]]
|
||||
] = check_list(
|
||||
check_dict_only(
|
||||
[
|
||||
("id", check_int),
|
||||
(
|
||||
"value",
|
||||
check_none_or(
|
||||
check_union([check_int, check_string, check_list(check_int)]),
|
||||
check_union([check_string, check_list(check_int)]),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
@ -168,7 +170,7 @@ def update_user_backend(
|
|||
UserProfile.ROLE_TYPES,
|
||||
),
|
||||
),
|
||||
profile_data: Optional[List[Dict[str, Optional[Union[int, str, List[int]]]]]] = REQ(
|
||||
profile_data: Optional[List[Dict[str, Optional[Union[int, ProfileDataElementValue]]]]] = REQ(
|
||||
default=None,
|
||||
json_validator=check_profile_data,
|
||||
),
|
||||
|
|
|
@ -73,6 +73,7 @@ from zerver.lib.rate_limiter import RateLimitedObject
|
|||
from zerver.lib.redis_utils import get_dict_from_redis, get_redis_client, put_dict_in_redis
|
||||
from zerver.lib.request import RequestNotes
|
||||
from zerver.lib.subdomains import get_subdomain
|
||||
from zerver.lib.types import ProfileDataElementValue
|
||||
from zerver.lib.url_encoding import add_query_to_redirect_url
|
||||
from zerver.lib.users import check_full_name, validate_user_custom_profile_field
|
||||
from zerver.models import (
|
||||
|
@ -1343,7 +1344,7 @@ def sync_user_profile_custom_fields(
|
|||
var_name = "_".join(data["name"].lower().split(" "))
|
||||
existing_values[var_name] = data["value"]
|
||||
|
||||
profile_data: List[Dict[str, Union[int, str, List[int]]]] = []
|
||||
profile_data: List[Dict[str, Union[int, ProfileDataElementValue]]] = []
|
||||
for var_name, value in custom_field_name_to_value.items():
|
||||
try:
|
||||
field = fields_by_var_name[var_name]
|
||||
|
|
Loading…
Reference in New Issue