mirror of https://github.com/zulip/zulip.git
user_settings: Migrate to typed_endpoint.
Migrate `user_settings.py` to `typed_endpoint`. Fix the error messages for the tests. Migrate required validators.
This commit is contained in:
parent
61ba381c30
commit
0f692436ca
|
@ -69,6 +69,18 @@ def timezone_or_empty_validator() -> AfterValidator:
|
|||
return AfterValidator(lambda s: to_timezone_or_empty(s))
|
||||
|
||||
|
||||
def check_timezone(s: str) -> str:
|
||||
try:
|
||||
zoneinfo.ZoneInfo(canonicalize_timezone(s))
|
||||
except (ValueError, zoneinfo.ZoneInfoNotFoundError):
|
||||
raise ValueError(_("Not a recognized time zone"))
|
||||
return s
|
||||
|
||||
|
||||
def timezone_validator() -> AfterValidator:
|
||||
return AfterValidator(lambda s: check_timezone(s))
|
||||
|
||||
|
||||
def to_non_negative_int_or_none(s: str) -> NonNegativeInt | None:
|
||||
try:
|
||||
i = int(s)
|
||||
|
|
|
@ -29,7 +29,6 @@ for any particular type of object.
|
|||
"""
|
||||
|
||||
import re
|
||||
import zoneinfo
|
||||
from collections.abc import Collection, Container, Iterator
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
|
@ -44,7 +43,6 @@ from pydantic.functional_validators import ModelWrapValidatorHandler
|
|||
from typing_extensions import override
|
||||
|
||||
from zerver.lib.exceptions import InvalidJSONError, JsonableError
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
from zerver.lib.types import ProfileFieldData, Validator
|
||||
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
@ -116,17 +114,6 @@ def check_long_string(var_name: str, val: object) -> str:
|
|||
return check_capped_string(500)(var_name, val)
|
||||
|
||||
|
||||
def check_timezone(var_name: str, val: object) -> str:
|
||||
s = check_string(var_name, val)
|
||||
try:
|
||||
zoneinfo.ZoneInfo(canonicalize_timezone(s))
|
||||
except (ValueError, zoneinfo.ZoneInfoNotFoundError):
|
||||
raise ValidationError(
|
||||
_("{var_name} is not a recognized time zone").format(var_name=var_name)
|
||||
)
|
||||
return s
|
||||
|
||||
|
||||
def check_date(var_name: str, val: object) -> str:
|
||||
if not isinstance(val, str):
|
||||
raise ValidationError(_("{var_name} is not a string").format(var_name=var_name))
|
||||
|
|
|
@ -407,39 +407,86 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||
self.do_test_change_user_setting("timezone")
|
||||
|
||||
def test_invalid_setting_value(self) -> None:
|
||||
invalid_values_dict = dict(
|
||||
default_language="invalid_de",
|
||||
web_home_view="invalid_view",
|
||||
emojiset="apple",
|
||||
timezone="invalid_US/Mountain",
|
||||
demote_inactive_streams=10,
|
||||
web_mark_read_on_scroll_policy=10,
|
||||
web_channel_default_view=10,
|
||||
user_list_style=10,
|
||||
web_animate_image_previews="invalid_value",
|
||||
web_stream_unreads_count_display_policy=10,
|
||||
color_scheme=10,
|
||||
notification_sound="invalid_sound",
|
||||
desktop_icon_count_display=10,
|
||||
)
|
||||
|
||||
invalid_values: list[dict[str, Any]] = [
|
||||
{
|
||||
"setting_name": "default_language",
|
||||
"value": "invalid_de",
|
||||
"error_msg": "Invalid default_language",
|
||||
},
|
||||
{
|
||||
"setting_name": "web_home_view",
|
||||
"value": "invalid_view",
|
||||
"error_msg": "Invalid web_home_view: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "emojiset",
|
||||
"value": "apple",
|
||||
"error_msg": "Invalid emojiset: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "timezone",
|
||||
"value": "invalid_US/Mountain",
|
||||
"error_msg": "Invalid timezone: Value error, Not a recognized time zone",
|
||||
},
|
||||
{
|
||||
"setting_name": "demote_inactive_streams",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid demote_inactive_streams: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "web_mark_read_on_scroll_policy",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid web_mark_read_on_scroll_policy: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "web_channel_default_view",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid web_channel_default_view: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "user_list_style",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid user_list_style: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "web_animate_image_previews",
|
||||
"value": "invalid_value",
|
||||
"error_msg": "Invalid web_animate_image_previews: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "web_stream_unreads_count_display_policy",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid web_stream_unreads_count_display_policy: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "color_scheme",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid color_scheme: Value error, Not in the list of possible values",
|
||||
},
|
||||
{
|
||||
"setting_name": "notification_sound",
|
||||
"value": "invalid_sound",
|
||||
"error_msg": "Invalid notification sound '\"invalid_sound\"'",
|
||||
},
|
||||
{
|
||||
"setting_name": "desktop_icon_count_display",
|
||||
"value": 10,
|
||||
"error_msg": "Invalid desktop_icon_count_display: Value error, Not in the list of possible values",
|
||||
},
|
||||
]
|
||||
self.login("hamlet")
|
||||
for setting_name in invalid_values_dict:
|
||||
invalid_value = invalid_values_dict.get(setting_name)
|
||||
if isinstance(invalid_value, str):
|
||||
invalid_value = orjson.dumps(invalid_value).decode()
|
||||
for invalid_value in invalid_values:
|
||||
if isinstance(invalid_value["value"], str):
|
||||
invalid_value["value"] = orjson.dumps(invalid_value["value"]).decode()
|
||||
|
||||
req = {setting_name: invalid_value}
|
||||
req = {invalid_value["setting_name"]: invalid_value["value"]}
|
||||
result = self.client_patch("/json/settings", req)
|
||||
|
||||
expected_error_msg = f"Invalid {setting_name}"
|
||||
if setting_name == "notification_sound":
|
||||
expected_error_msg = f"Invalid notification sound '{invalid_value}'"
|
||||
elif setting_name == "timezone":
|
||||
expected_error_msg = "timezone is not a recognized time zone"
|
||||
self.assert_json_error(result, expected_error_msg)
|
||||
self.assert_json_error(result, invalid_value["error_msg"])
|
||||
hamlet = self.example_user("hamlet")
|
||||
self.assertNotEqual(getattr(hamlet, setting_name), invalid_value)
|
||||
self.assertNotEqual(
|
||||
getattr(hamlet, invalid_value["setting_name"]), invalid_value["value"]
|
||||
)
|
||||
|
||||
def do_change_emojiset(self, emojiset: str) -> "TestHttpResponse":
|
||||
self.login("hamlet")
|
||||
|
@ -454,7 +501,9 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||
|
||||
for emojiset in banned_emojisets:
|
||||
result = self.do_change_emojiset(emojiset)
|
||||
self.assert_json_error(result, "Invalid emojiset")
|
||||
self.assert_json_error(
|
||||
result, "Invalid emojiset: Value error, Not in the list of possible values"
|
||||
)
|
||||
|
||||
for emojiset in valid_emojisets:
|
||||
result = self.do_change_emojiset(emojiset)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from email.headerregistry import Address
|
||||
from typing import Any
|
||||
from typing import Annotated, Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, update_session_auth_hash
|
||||
|
@ -13,6 +13,7 @@ from django.utils.html import escape
|
|||
from django.utils.safestring import SafeString
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy
|
||||
from pydantic import Json
|
||||
|
||||
from confirmation.models import (
|
||||
Confirmation,
|
||||
|
@ -45,18 +46,16 @@ from zerver.lib.exceptions import (
|
|||
)
|
||||
from zerver.lib.i18n import get_available_language_codes
|
||||
from zerver.lib.rate_limiter import RateLimitedUser
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.send_email import FromAddress, send_email
|
||||
from zerver.lib.sounds import get_available_notification_sounds
|
||||
from zerver.lib.upload import upload_avatar_image
|
||||
from zerver.lib.validator import (
|
||||
check_bool,
|
||||
check_int,
|
||||
check_int_in,
|
||||
check_string_in,
|
||||
check_timezone,
|
||||
from zerver.lib.typed_endpoint import typed_endpoint, typed_endpoint_without_parameters
|
||||
from zerver.lib.typed_endpoint_validators import (
|
||||
check_int_in_validator,
|
||||
check_string_in_validator,
|
||||
timezone_validator,
|
||||
)
|
||||
from zerver.lib.upload import upload_avatar_image
|
||||
from zerver.models import EmailChangeStatus, RealmUserDefault, UserBaseSettings, UserProfile
|
||||
from zerver.models.realms import avatar_changes_disabled, name_changes_disabled
|
||||
from zerver.views.auth import redirect_to_deactivation_notice
|
||||
|
@ -223,120 +222,111 @@ def check_information_density_setting_values(
|
|||
|
||||
|
||||
@human_users_only
|
||||
@has_request_variables
|
||||
@typed_endpoint
|
||||
def json_change_settings(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
full_name: str | None = REQ(default=None),
|
||||
email: str | None = REQ(default=None),
|
||||
old_password: str | None = REQ(default=None),
|
||||
new_password: str | None = REQ(default=None),
|
||||
twenty_four_hour_time: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
dense_mode: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
web_mark_read_on_scroll_policy: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
web_channel_default_view: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
starred_message_counts: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
receives_typing_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
fluid_layout_width: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
high_contrast_mode: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
color_scheme: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None
|
||||
),
|
||||
web_font_size_px: int | None = REQ(json_validator=check_int, default=None),
|
||||
web_line_height_percent: int | None = REQ(json_validator=check_int, default=None),
|
||||
translate_emoticons: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
display_emoji_reaction_users: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
default_language: str | None = REQ(default=None),
|
||||
web_home_view: str | None = REQ(
|
||||
str_validator=check_string_in(web_home_view_options), default=None
|
||||
),
|
||||
web_escape_navigates_to_home_view: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
left_side_userlist: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
emojiset: str | None = REQ(str_validator=check_string_in(emojiset_choices), default=None),
|
||||
demote_inactive_streams: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.DEMOTE_STREAMS_CHOICES), default=None
|
||||
),
|
||||
web_stream_unreads_count_display_policy: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
timezone: str | None = REQ(str_validator=check_timezone, default=None),
|
||||
email_notifications_batching_period_seconds: int | None = REQ(
|
||||
json_validator=check_int, default=None
|
||||
),
|
||||
enable_drafts_synchronization: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_stream_desktop_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_stream_email_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_stream_push_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_stream_audible_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
wildcard_mentions_notify: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_followed_topic_desktop_notifications: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
enable_followed_topic_email_notifications: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
enable_followed_topic_push_notifications: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
enable_followed_topic_audible_notifications: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
enable_followed_topic_wildcard_mentions_notify: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
notification_sound: str | None = REQ(default=None),
|
||||
enable_desktop_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_sounds: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_offline_email_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_offline_push_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_online_push_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_digest_emails: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_login_emails: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enable_marketing_emails: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
message_content_in_email_notifications: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
pm_content_in_desktop_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
desktop_icon_count_display: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES), default=None
|
||||
),
|
||||
realm_name_in_email_notifications_policy: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_follow_topics_policy: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_unmute_topics_in_muted_streams_policy: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_follow_topics_where_mentioned: bool | None = REQ(
|
||||
json_validator=check_bool, default=None
|
||||
),
|
||||
presence_enabled: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
enter_sends: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
send_private_typing_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
send_stream_typing_notifications: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
send_read_receipts: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
user_list_style: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.USER_LIST_STYLE_CHOICES), default=None
|
||||
),
|
||||
web_animate_image_previews: str | None = REQ(
|
||||
str_validator=check_string_in(web_animate_image_previews_options), default=None
|
||||
),
|
||||
email_address_visibility: int | None = REQ(
|
||||
json_validator=check_int_in(UserProfile.EMAIL_ADDRESS_VISIBILITY_TYPES), default=None
|
||||
),
|
||||
web_navigate_to_sent_message: bool | None = REQ(json_validator=check_bool, default=None),
|
||||
*,
|
||||
full_name: str | None = None,
|
||||
email: str | None = None,
|
||||
old_password: str | None = None,
|
||||
new_password: str | None = None,
|
||||
twenty_four_hour_time: Json[bool] | None = None,
|
||||
dense_mode: Json[bool] | None = None,
|
||||
web_mark_read_on_scroll_policy: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES)
|
||||
]
|
||||
| None = None,
|
||||
web_channel_default_view: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES)
|
||||
]
|
||||
| None = None,
|
||||
starred_message_counts: Json[bool] | None = None,
|
||||
receives_typing_notifications: Json[bool] | None = None,
|
||||
fluid_layout_width: Json[bool] | None = None,
|
||||
high_contrast_mode: Json[bool] | None = None,
|
||||
color_scheme: Annotated[Json[int], check_int_in_validator(UserProfile.COLOR_SCHEME_CHOICES)]
|
||||
| None = None,
|
||||
web_font_size_px: Json[int] | None = None,
|
||||
web_line_height_percent: Json[int] | None = None,
|
||||
translate_emoticons: Json[bool] | None = None,
|
||||
display_emoji_reaction_users: Json[bool] | None = None,
|
||||
default_language: str | None = None,
|
||||
web_home_view: Annotated[str, check_string_in_validator(web_home_view_options)] | None = None,
|
||||
web_escape_navigates_to_home_view: Json[bool] | None = None,
|
||||
left_side_userlist: Json[bool] | None = None,
|
||||
emojiset: Annotated[str, check_string_in_validator(emojiset_choices)] | None = None,
|
||||
demote_inactive_streams: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.DEMOTE_STREAMS_CHOICES)
|
||||
]
|
||||
| None = None,
|
||||
web_stream_unreads_count_display_policy: Annotated[
|
||||
Json[int],
|
||||
check_int_in_validator(UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES),
|
||||
]
|
||||
| None = None,
|
||||
timezone: Annotated[str, timezone_validator()] | None = None,
|
||||
email_notifications_batching_period_seconds: Json[int] | None = None,
|
||||
enable_drafts_synchronization: Json[bool] | None = None,
|
||||
enable_stream_desktop_notifications: Json[bool] | None = None,
|
||||
enable_stream_email_notifications: Json[bool] | None = None,
|
||||
enable_stream_push_notifications: Json[bool] | None = None,
|
||||
enable_stream_audible_notifications: Json[bool] | None = None,
|
||||
wildcard_mentions_notify: Json[bool] | None = None,
|
||||
enable_followed_topic_desktop_notifications: Json[bool] | None = None,
|
||||
enable_followed_topic_email_notifications: Json[bool] | None = None,
|
||||
enable_followed_topic_push_notifications: Json[bool] | None = None,
|
||||
enable_followed_topic_audible_notifications: Json[bool] | None = None,
|
||||
enable_followed_topic_wildcard_mentions_notify: Json[bool] | None = None,
|
||||
notification_sound: str | None = None,
|
||||
enable_desktop_notifications: Json[bool] | None = None,
|
||||
enable_sounds: Json[bool] | None = None,
|
||||
enable_offline_email_notifications: Json[bool] | None = None,
|
||||
enable_offline_push_notifications: Json[bool] | None = None,
|
||||
enable_online_push_notifications: Json[bool] | None = None,
|
||||
enable_digest_emails: Json[bool] | None = None,
|
||||
enable_login_emails: Json[bool] | None = None,
|
||||
enable_marketing_emails: Json[bool] | None = None,
|
||||
message_content_in_email_notifications: Json[bool] | None = None,
|
||||
pm_content_in_desktop_notifications: Json[bool] | None = None,
|
||||
desktop_icon_count_display: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES)
|
||||
]
|
||||
| None = None,
|
||||
realm_name_in_email_notifications_policy: Annotated[
|
||||
Json[int],
|
||||
check_int_in_validator(UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES),
|
||||
]
|
||||
| None = None,
|
||||
automatically_follow_topics_policy: Annotated[
|
||||
Json[int],
|
||||
check_int_in_validator(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
]
|
||||
| None = None,
|
||||
automatically_unmute_topics_in_muted_streams_policy: Annotated[
|
||||
Json[int],
|
||||
check_int_in_validator(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
]
|
||||
| None = None,
|
||||
automatically_follow_topics_where_mentioned: Json[bool] | None = None,
|
||||
presence_enabled: Json[bool] | None = None,
|
||||
enter_sends: Json[bool] | None = None,
|
||||
send_private_typing_notifications: Json[bool] | None = None,
|
||||
send_stream_typing_notifications: Json[bool] | None = None,
|
||||
send_read_receipts: Json[bool] | None = None,
|
||||
user_list_style: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.USER_LIST_STYLE_CHOICES)
|
||||
]
|
||||
| None = None,
|
||||
web_animate_image_previews: Annotated[
|
||||
str, check_string_in_validator(web_animate_image_previews_options)
|
||||
]
|
||||
| None = None,
|
||||
email_address_visibility: Annotated[
|
||||
Json[int], check_int_in_validator(UserProfile.EMAIL_ADDRESS_VISIBILITY_TYPES)
|
||||
]
|
||||
| None = None,
|
||||
web_navigate_to_sent_message: Json[bool] | None = None,
|
||||
) -> HttpResponse:
|
||||
# UserProfile object is being refetched here to make sure that we
|
||||
# do not use stale object from cache which can happen when a
|
||||
|
@ -485,7 +475,7 @@ def delete_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> Ht
|
|||
|
||||
# We don't use @human_users_only here, because there are use cases for
|
||||
# a bot regenerating its own API key.
|
||||
@has_request_variables
|
||||
@typed_endpoint_without_parameters
|
||||
def regenerate_api_key(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
||||
new_api_key = do_regenerate_api_key(user_profile, user_profile)
|
||||
json_result = dict(
|
||||
|
|
Loading…
Reference in New Issue