webhooks: Migrate transifex to use endpoint to use @typed_endpoint.

Transifex has parameters that need to be parsed from JSON and converted
to int. Note that we use Optional[Json[int]] instead of
Json[Optional[int]] to replicate the behavior of json_validator. This
caveat is explained in a new test called test_json_optional.
This commit is contained in:
Zixuan James Li 2023-08-14 17:26:28 -04:00 committed by Tim Abbott
parent 1329284848
commit 9fef12950a
2 changed files with 26 additions and 8 deletions

View File

@ -546,6 +546,23 @@ class TestEndpoint(ZulipTestCase):
call_endpoint(test_view, HostRequestMock({"foo": ""})) call_endpoint(test_view, HostRequestMock({"foo": ""}))
def test_json_optional(self) -> None:
# Optional[Json[int]] does not allow client provided optional value at
# all. The only possible way for val to be None is through the default
# value (if it has one).
@typed_endpoint
def foo(request: HttpRequest, *, val: Optional[Json[int]]) -> None:
...
# Json[Optional[int]] however, allows client specified None value.
@typed_endpoint
def bar(request: HttpRequest, *, val: Json[Optional[int]]) -> None:
...
with self.assertRaisesMessage(ApiParamValidationError, "val is not an integer"):
call_endpoint(foo, HostRequestMock({"val": orjson.dumps(None).decode()}))
call_endpoint(bar, HostRequestMock({"val": orjson.dumps(None).decode()}))
class ValidationErrorHandlingTest(ZulipTestCase): class ValidationErrorHandlingTest(ZulipTestCase):
def test_special_handling_errors(self) -> None: def test_special_handling_errors(self) -> None:

View File

@ -2,12 +2,12 @@
from typing import Optional from typing import Optional
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from pydantic import Json
from zerver.decorator import webhook_view from zerver.decorator import webhook_view
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.validator import check_int from zerver.lib.typed_endpoint import typed_endpoint
from zerver.lib.webhooks.common import check_send_webhook_message from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile from zerver.models import UserProfile
@ -15,15 +15,16 @@ All_EVENT_TYPES = ["translated", "review"]
@webhook_view("Transifex", notify_bot_owner_on_invalid_json=False, all_event_types=All_EVENT_TYPES) @webhook_view("Transifex", notify_bot_owner_on_invalid_json=False, all_event_types=All_EVENT_TYPES)
@has_request_variables @typed_endpoint
def api_transifex_webhook( def api_transifex_webhook(
request: HttpRequest, request: HttpRequest,
user_profile: UserProfile, user_profile: UserProfile,
project: str = REQ(), *,
resource: str = REQ(), project: str,
language: str = REQ(), resource: str,
translated: Optional[int] = REQ(json_validator=check_int, default=None), language: str,
reviewed: Optional[int] = REQ(json_validator=check_int, default=None), translated: Optional[Json[int]] = None,
reviewed: Optional[Json[int]] = None,
) -> HttpResponse: ) -> HttpResponse:
topic = f"{project} in {language}" topic = f"{project} in {language}"
if translated: if translated: