mirror of https://github.com/zulip/zulip.git
message_edit: Migrate message_edit to use @typed_endpoint.
This demonstrates how an alias is created and its suitable use case, the use of PathOnly, NonNegativeInt, and Literal.
This commit is contained in:
parent
9c53995830
commit
6201914fd3
|
@ -103,6 +103,11 @@ WebhookPayload: TypeAlias = Annotated[Json[T], ApiParamConfig(argument_type_is_b
|
|||
# A shorthand to declare path only variables that should not be parsed from the
|
||||
# request by the @typed_endpoint decorator.
|
||||
PathOnly: TypeAlias = Annotated[T, ApiParamConfig(path_only=True)]
|
||||
OptionalTopic: TypeAlias = Annotated[
|
||||
Optional[str],
|
||||
StringConstraints(strip_whitespace=True),
|
||||
ApiParamConfig(whence="topic", aliases=("subject")),
|
||||
]
|
||||
|
||||
# Reusable annotation metadata for Annotated types
|
||||
|
||||
|
|
|
@ -63,11 +63,9 @@ def schema_type(schema: Dict[str, Any]) -> Union[type, Tuple[type, object]]:
|
|||
# Ideally, we'd turn this into a Union type.
|
||||
return schema_type(schema["oneOf"][0])
|
||||
elif "anyOf" in schema:
|
||||
return schema_type(schema["anyOf"][0]) # nocoverage
|
||||
return schema_type(schema["anyOf"][0])
|
||||
elif schema.get("contentMediaType") == "application/json":
|
||||
return schema_type(
|
||||
schema["contentSchema"]
|
||||
) # nocoverage # Will be covered as more endpoints are migrated
|
||||
return schema_type(schema["contentSchema"])
|
||||
elif schema["type"] == "array":
|
||||
return (list, schema_type(schema["items"]))
|
||||
else:
|
||||
|
@ -436,9 +434,7 @@ do not match the types declared in the implementation of {function.__name__}.\n"
|
|||
]
|
||||
json_request_var_names.add(expected_request_var_name)
|
||||
else:
|
||||
expected_param_schema = expected_param_schema[
|
||||
"schema"
|
||||
] # nocoverage # Will be covered as more endpoints are migrated
|
||||
expected_param_schema = expected_param_schema["schema"]
|
||||
|
||||
openapi_params.add((expected_request_var_name, schema_type(expected_param_schema)))
|
||||
|
||||
|
@ -460,9 +456,7 @@ do not match the types declared in the implementation of {function.__name__}.\n"
|
|||
# actual_param_schema is a json_schema. Reference:
|
||||
# https://docs.pydantic.dev/latest/api/json_schema/#pydantic.json_schema.GenerateJsonSchema.json_schema
|
||||
actual_param_schema = actual_param_schema["contentSchema"]
|
||||
elif (
|
||||
"contentMediaType" in actual_param_schema
|
||||
): # nocoverage # Will be covered as more endpoints are migrated
|
||||
elif "contentMediaType" in actual_param_schema:
|
||||
function_schema_type = schema_type(actual_param_schema)
|
||||
# We do not specify that the content type of int or bool
|
||||
# parameters should be JSON encoded, while our code does expect
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import datetime
|
||||
from typing import List, Optional, Union
|
||||
from typing import List, Literal, Optional, Union
|
||||
|
||||
import orjson
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
@ -7,6 +7,7 @@ from django.db import IntegrityError, transaction
|
|||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext as _
|
||||
from pydantic import Json, NonNegativeInt
|
||||
|
||||
from zerver.actions.message_delete import do_delete_messages
|
||||
from zerver.actions.message_edit import check_update_message
|
||||
|
@ -14,12 +15,11 @@ from zerver.context_processors import get_valid_realm_from_request
|
|||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.html_diff import highlight_html_differences
|
||||
from zerver.lib.message import access_message, access_web_public_message, messages_for_ids
|
||||
from zerver.lib.request import REQ, RequestNotes, has_request_variables
|
||||
from zerver.lib.request import RequestNotes
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.topic import REQ_topic
|
||||
from zerver.lib.typed_endpoint import OptionalTopic, PathOnly, typed_endpoint
|
||||
from zerver.lib.types import EditHistoryEvent, FormattedEditHistoryEvent
|
||||
from zerver.lib.validator import check_bool, check_string_in, to_non_negative_int
|
||||
from zerver.models import Message, UserProfile
|
||||
|
||||
|
||||
|
@ -88,11 +88,12 @@ def fill_edit_history_entries(
|
|||
return formatted_edit_history
|
||||
|
||||
|
||||
@has_request_variables
|
||||
@typed_endpoint
|
||||
def get_message_edit_history(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
||||
*,
|
||||
message_id: PathOnly[NonNegativeInt],
|
||||
) -> HttpResponse:
|
||||
if not user_profile.realm.allow_edit_history:
|
||||
raise JsonableError(_("Message edit history is disabled in this organization"))
|
||||
|
@ -109,22 +110,18 @@ def get_message_edit_history(
|
|||
return json_success(request, data={"message_history": list(reversed(message_edit_history))})
|
||||
|
||||
|
||||
PROPAGATE_MODE_VALUES = ["change_later", "change_one", "change_all"]
|
||||
|
||||
|
||||
@has_request_variables
|
||||
@typed_endpoint
|
||||
def update_message_backend(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
||||
stream_id: Optional[int] = REQ(converter=to_non_negative_int, default=None),
|
||||
topic_name: Optional[str] = REQ_topic(),
|
||||
propagate_mode: str = REQ(
|
||||
default="change_one", str_validator=check_string_in(PROPAGATE_MODE_VALUES)
|
||||
),
|
||||
send_notification_to_old_thread: bool = REQ(default=False, json_validator=check_bool),
|
||||
send_notification_to_new_thread: bool = REQ(default=True, json_validator=check_bool),
|
||||
content: Optional[str] = REQ(default=None),
|
||||
*,
|
||||
message_id: PathOnly[NonNegativeInt],
|
||||
stream_id: Optional[Json[NonNegativeInt]] = None,
|
||||
topic_name: OptionalTopic = None,
|
||||
propagate_mode: Literal["change_later", "change_one", "change_all"] = "change_one",
|
||||
send_notification_to_old_thread: Json[bool] = False,
|
||||
send_notification_to_new_thread: Json[bool] = True,
|
||||
content: Optional[str] = None,
|
||||
) -> HttpResponse:
|
||||
number_changed = check_update_message(
|
||||
user_profile,
|
||||
|
@ -167,11 +164,12 @@ def validate_can_delete_message(user_profile: UserProfile, message: Message) ->
|
|||
|
||||
|
||||
@transaction.atomic
|
||||
@has_request_variables
|
||||
@typed_endpoint
|
||||
def delete_message_backend(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
||||
*,
|
||||
message_id: PathOnly[NonNegativeInt],
|
||||
) -> HttpResponse:
|
||||
# We lock the `Message` object to ensure that any transactions modifying the `Message` object
|
||||
# concurrently are serialized properly with deleting the message; this prevents a deadlock
|
||||
|
@ -186,12 +184,13 @@ def delete_message_backend(
|
|||
return json_success(request)
|
||||
|
||||
|
||||
@has_request_variables
|
||||
@typed_endpoint
|
||||
def json_fetch_raw_message(
|
||||
request: HttpRequest,
|
||||
maybe_user_profile: Union[UserProfile, AnonymousUser],
|
||||
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
||||
apply_markdown: bool = REQ(json_validator=check_bool, default=True),
|
||||
*,
|
||||
message_id: PathOnly[NonNegativeInt],
|
||||
apply_markdown: Json[bool] = True,
|
||||
) -> HttpResponse:
|
||||
if not maybe_user_profile.is_authenticated:
|
||||
realm = get_valid_realm_from_request(request)
|
||||
|
|
Loading…
Reference in New Issue