diff --git a/analytics/views/support.py b/analytics/views/support.py index 4cd07031ec..d7760a3883 100644 --- a/analytics/views/support.py +++ b/analytics/views/support.py @@ -25,8 +25,8 @@ from zerver.lib.actions import ( do_scrub_realm, do_send_realm_reactivation_email, ) +from zerver.lib.exceptions import JsonableError from zerver.lib.realm_icon import realm_icon_url -from zerver.lib.response import json_error from zerver.lib.subdomains import get_subdomain_from_hostname from zerver.models import MultiuseInvite, PreregistrationUser, Realm, UserProfile, get_realm from zerver.views.invite import get_invitee_emails_set @@ -110,7 +110,7 @@ def support(request: HttpRequest) -> HttpResponse: if "csrfmiddlewaretoken" in keys: keys.remove("csrfmiddlewaretoken") if len(keys) != 2: - return json_error(_("Invalid parameters")) + raise JsonableError(_("Invalid parameters")) realm_id = request.POST.get("realm_id") realm = Realm.objects.get(id=realm_id) diff --git a/corporate/views.py b/corporate/views.py index 4e9e864db3..7f5754702a 100644 --- a/corporate/views.py +++ b/corporate/views.py @@ -47,6 +47,7 @@ from zerver.decorator import ( zulip_login_required, ) from zerver.lib.actions import do_make_user_billing_admin +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_error, json_success from zerver.lib.send_email import FromAddress, send_email @@ -372,12 +373,12 @@ def update_plan( new_plan, last_ledger_entry = make_end_of_cycle_updates_if_needed(plan, timezone_now()) if new_plan is not None: - return json_error( + raise JsonableError( _("Unable to update the plan. The plan has been expired and replaced with a new plan.") ) if last_ledger_entry is None: - return json_error(_("Unable to update the plan. The plan has ended.")) + raise JsonableError(_("Unable to update the plan. The plan has ended.")) if status is not None: if status == CustomerPlan.ACTIVE: @@ -398,19 +399,19 @@ def update_plan( if licenses is not None: if plan.automanage_licenses: - return json_error( + raise JsonableError( _( "Unable to update licenses manually. Your plan is on automatic license management." ) ) if last_ledger_entry.licenses == licenses: - return json_error( + raise JsonableError( _( "Your plan is already on {licenses} licenses in the current billing period." ).format(licenses=licenses) ) if last_ledger_entry.licenses > licenses: - return json_error( + raise JsonableError( _("You cannot decrease the licenses in the current billing period.").format( licenses=licenses ) @@ -426,13 +427,13 @@ def update_plan( if licenses_at_next_renewal is not None: if plan.automanage_licenses: - return json_error( + raise JsonableError( _( "Unable to update licenses manually. Your plan is on automatic license management." ) ) if last_ledger_entry.licenses_at_next_renewal == licenses_at_next_renewal: - return json_error( + raise JsonableError( _( "Your plan is already scheduled to renew with {licenses_at_next_renewal} licenses." ).format(licenses_at_next_renewal=licenses_at_next_renewal) @@ -450,7 +451,7 @@ def update_plan( ) return json_success() - return json_error(_("Nothing to change.")) + raise JsonableError(_("Nothing to change.")) @require_billing_access diff --git a/zerver/decorator.py b/zerver/decorator.py index 0ebc5a40af..94b33f635c 100644 --- a/zerver/decorator.py +++ b/zerver/decorator.py @@ -452,7 +452,7 @@ def human_users_only(view_func: ViewFuncT) -> ViewFuncT: @wraps(view_func) def _wrapped_view_func(request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: if request.user.is_bot: - return json_error(_("This endpoint does not accept bot requests.")) + raise JsonableError(_("This endpoint does not accept bot requests.")) return view_func(request, *args, **kwargs) return cast(ViewFuncT, _wrapped_view_func) # https://github.com/python/mypy/issues/1927 @@ -549,7 +549,7 @@ def require_member_or_admin(view_func: ViewFuncT) -> ViewFuncT: if user_profile.is_guest: raise JsonableError(_("Not allowed for guest users")) if user_profile.is_bot: - return json_error(_("This endpoint does not accept bot requests.")) + raise JsonableError(_("This endpoint does not accept bot requests.")) return view_func(request, user_profile, *args, **kwargs) return cast(ViewFuncT, _wrapped_view_func) # https://github.com/python/mypy/issues/1927 @@ -624,7 +624,7 @@ def authenticated_rest_api_view( auth_type, credentials = request.META["HTTP_AUTHORIZATION"].split() # case insensitive per RFC 1945 if auth_type.lower() != "basic": - return json_error(_("This endpoint requires HTTP basic authentication.")) + raise JsonableError(_("This endpoint requires HTTP basic authentication.")) role, api_key = base64.b64decode(credentials).decode("utf-8").split(":") except ValueError: return json_unauthorized(_("Invalid authorization header for basic auth")) diff --git a/zerver/lib/error_notify.py b/zerver/lib/error_notify.py index 8e3854f5a3..1af7a2f81e 100644 --- a/zerver/lib/error_notify.py +++ b/zerver/lib/error_notify.py @@ -9,7 +9,8 @@ from django.utils.translation import gettext as _ from zerver.filters import clean_data_from_query_parameters from zerver.lib.actions import internal_send_stream_message -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.models import get_stream, get_system_bot @@ -199,5 +200,5 @@ def do_report_error(type: str, report: Dict[str, Any]) -> HttpResponse: elif type == "server": notify_server_error(report) else: - return json_error(_("Invalid type parameter")) + raise JsonableError(_("Invalid type parameter")) return json_success() diff --git a/zerver/tests/test_integrations_dev_panel.py b/zerver/tests/test_integrations_dev_panel.py index b3d53c219d..31920b6ce2 100644 --- a/zerver/tests/test_integrations_dev_panel.py +++ b/zerver/tests/test_integrations_dev_panel.py @@ -220,6 +220,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "user_register.txt", "status_code": 400, @@ -228,6 +229,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "publish_post_no_data_provided.txt", "status_code": 400, @@ -236,6 +238,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "unknown_action_no_data.txt", "status_code": 400, @@ -244,6 +247,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "publish_page.txt", "status_code": 400, @@ -252,6 +256,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "unknown_action_no_hook_provided.txt", "status_code": 400, @@ -260,6 +265,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "publish_post_type_not_provided.txt", "status_code": 400, @@ -268,6 +274,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "wp_login.txt", "status_code": 400, @@ -276,6 +283,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): "message": { "msg": "Unknown WordPress webhook action: WordPress action", "result": "error", + "code": "BAD_REQUEST", }, "fixture_name": "publish_post.txt", "status_code": 400, diff --git a/zerver/tests/test_realm_export.py b/zerver/tests/test_realm_export.py index 06cca60b89..221ea949bb 100644 --- a/zerver/tests/test_realm_export.py +++ b/zerver/tests/test_realm_export.py @@ -184,8 +184,9 @@ class RealmExportTest(ZulipTestCase): ) RealmAuditLog.objects.bulk_create(exports) - result = export_realm(self.client_post, admin) - self.assert_json_error(result, "Exceeded rate limit.") + with self.assertRaises(JsonableError) as error: + export_realm(self.client_post, admin) + self.assertEqual(str(error.exception), "Exceeded rate limit.") def test_upload_and_message_limit(self) -> None: admin = self.example_user("iago") diff --git a/zerver/tornado/views.py b/zerver/tornado/views.py index 71c5386c49..ab8ca9430f 100644 --- a/zerver/tornado/views.py +++ b/zerver/tornado/views.py @@ -6,7 +6,8 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import REQ, has_request_variables, internal_notify_view, process_client -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.validator import ( check_bool, check_int, @@ -34,7 +35,7 @@ def cleanup_event_queue( if client is None: raise BadEventQueueIdError(queue_id) if user_profile.id != client.user_profile_id: - return json_error(_("You are not authorized to access this queue")) + raise JsonableError(_("You are not authorized to access this queue")) request._log_data["extra"] = f"[{queue_id}]" client.cleanup() return json_success() @@ -102,7 +103,7 @@ def get_events_backend( ), ) -> HttpResponse: if all_public_streams and not user_profile.can_access_public_streams(): - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) # Extract the Tornado handler from the request handler: AsyncDjangoHandler = request._tornado_handler diff --git a/zerver/views/auth.py b/zerver/views/auth.py index 58e7a38d7c..950774b4cb 100644 --- a/zerver/views/auth.py +++ b/zerver/views/auth.py @@ -822,7 +822,7 @@ def api_fetch_api_key( realm = get_realm_from_request(request) if realm is None: - return json_error(_("Invalid subdomain")) + raise JsonableError(_("Invalid subdomain")) if not ldap_auth_enabled(realm=realm): # In case we don't authenticate against LDAP, check for a valid @@ -945,12 +945,12 @@ def json_fetch_api_key( ) -> HttpResponse: realm = get_realm_from_request(request) if realm is None: - return json_error(_("Invalid subdomain")) + raise JsonableError(_("Invalid subdomain")) if password_auth_enabled(user_profile.realm): if not authenticate( request=request, username=user_profile.delivery_email, password=password, realm=realm ): - return json_error(_("Your username or password is incorrect.")) + raise JsonableError(_("Your username or password is incorrect.")) api_key = get_api_key(user_profile) return json_success({"api_key": api_key, "email": user_profile.delivery_email}) diff --git a/zerver/views/compatibility.py b/zerver/views/compatibility.py index 2e94e27a01..7ee1fa866f 100644 --- a/zerver/views/compatibility.py +++ b/zerver/views/compatibility.py @@ -2,7 +2,8 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.lib.compatibility import find_mobile_os, version_lt -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.user_agent import parse_user_agent # Zulip Mobile release 16.2.96 was made 2018-08-22. It fixed a @@ -14,16 +15,16 @@ android_min_app_version = "16.2.96" def check_global_compatibility(request: HttpRequest) -> HttpResponse: if request.META.get("HTTP_USER_AGENT") is None: - return json_error(_("User-Agent header missing from request")) + raise JsonableError(_("User-Agent header missing from request")) # This string should not be tagged for translation, since old # clients are checking for an extra string. legacy_compatibility_error_message = "Client is too old" user_agent = parse_user_agent(request.META["HTTP_USER_AGENT"]) if user_agent["name"] == "ZulipInvalid": - return json_error(legacy_compatibility_error_message) + raise JsonableError(legacy_compatibility_error_message) if user_agent["name"] == "ZulipMobile": user_os = find_mobile_os(request.META["HTTP_USER_AGENT"]) if user_os == "android" and version_lt(user_agent["version"], android_min_app_version): - return json_error(legacy_compatibility_error_message) + raise JsonableError(legacy_compatibility_error_message) return json_success() diff --git a/zerver/views/custom_profile_fields.py b/zerver/views/custom_profile_fields.py index 2905164563..8dcf005a90 100644 --- a/zerver/views/custom_profile_fields.py +++ b/zerver/views/custom_profile_fields.py @@ -19,7 +19,7 @@ from zerver.lib.actions import ( 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_error, json_success +from zerver.lib.response import json_success from zerver.lib.types import ProfileFieldData from zerver.lib.users import validate_user_custom_profile_data from zerver.lib.validator import ( @@ -124,7 +124,7 @@ def create_realm_custom_profile_field( ) return json_success({"id": field.id}) except IntegrityError: - return json_error(_("A field with that label already exists.")) + raise JsonableError(_("A field with that label already exists.")) @require_realm_admin @@ -134,7 +134,7 @@ def delete_realm_custom_profile_field( try: field = CustomProfileField.objects.get(id=field_id) except CustomProfileField.DoesNotExist: - return json_error(_("Field id {id} not found.").format(id=field_id)) + raise JsonableError(_("Field id {id} not found.").format(id=field_id)) do_remove_realm_custom_profile_field(realm=user_profile.realm, field=field) return json_success() @@ -154,17 +154,17 @@ def update_realm_custom_profile_field( try: field = CustomProfileField.objects.get(realm=realm, id=field_id) except CustomProfileField.DoesNotExist: - return json_error(_("Field id {id} not found.").format(id=field_id)) + raise JsonableError(_("Field id {id} not found.").format(id=field_id)) if field.field_type == CustomProfileField.EXTERNAL_ACCOUNT: if is_default_external_field(field.field_type, orjson.loads(field.field_data)): - return json_error(_("Default custom field cannot be updated.")) + raise JsonableError(_("Default custom field cannot be updated.")) validate_custom_profile_field(name, hint, field.field_type, field_data) try: try_update_realm_custom_profile_field(realm, field, name, hint=hint, field_data=field_data) except IntegrityError: - return json_error(_("A field with that label already exists.")) + raise JsonableError(_("A field with that label already exists.")) return json_success() diff --git a/zerver/views/development/dev_login.py b/zerver/views/development/dev_login.py index ee60c69cdb..0cec6a1517 100644 --- a/zerver/views/development/dev_login.py +++ b/zerver/views/development/dev_login.py @@ -103,7 +103,7 @@ def api_dev_fetch_api_key(request: HttpRequest, username: str = REQ()) -> HttpRe validate_login_email(username) realm = get_realm_from_request(request) if realm is None: - return json_error(_("Invalid subdomain")) + raise JsonableError(_("Invalid subdomain")) return_data: Dict[str, bool] = {} user_profile = authenticate(dev_auth_username=username, realm=realm, return_data=return_data) if return_data.get("inactive_realm"): diff --git a/zerver/views/development/integrations.py b/zerver/views/development/integrations.py index 5d5270c858..063c8b8cc0 100644 --- a/zerver/views/development/integrations.py +++ b/zerver/views/development/integrations.py @@ -6,6 +6,7 @@ from django.http import HttpRequest, HttpResponse from django.shortcuts import render from django.test import Client +from zerver.lib.exceptions import JsonableError from zerver.lib.integrations import WEBHOOK_INTEGRATIONS from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_error, json_success @@ -104,7 +105,7 @@ def check_send_webhook_fixture_message( try: custom_headers_dict = orjson.loads(custom_headers) except orjson.JSONDecodeError as ve: - return json_error(f"Custom HTTP headers are not in a valid JSON format. {ve}") # nolint + raise JsonableError(f"Custom HTTP headers are not in a valid JSON format. {ve}") # nolint response = send_webhook_fixture_message(url, body, is_json, custom_headers_dict) if response.status_code == 200: diff --git a/zerver/views/email_mirror.py b/zerver/views/email_mirror.py index f15f1234ce..be46882666 100644 --- a/zerver/views/email_mirror.py +++ b/zerver/views/email_mirror.py @@ -2,8 +2,9 @@ from django.http import HttpRequest, HttpResponse from zerver.decorator import internal_notify_view from zerver.lib.email_mirror import mirror_email_message +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success @internal_notify_view(False) @@ -15,5 +16,5 @@ def email_mirror_message( ) -> HttpResponse: result = mirror_email_message(rcpt_to, msg_base64) if result["status"] == "error": - return json_error(result["msg"]) + raise JsonableError(result["msg"]) return json_success() diff --git a/zerver/views/events_register.py b/zerver/views/events_register.py index d6225c5884..e08aeaed5d 100644 --- a/zerver/views/events_register.py +++ b/zerver/views/events_register.py @@ -4,8 +4,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.lib.events import do_events_register +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.validator import check_bool, check_dict, check_list, check_string from zerver.models import Stream, UserProfile @@ -70,7 +71,7 @@ def events_register_backend( queue_lifespan_secs: int = REQ(converter=int, default=0, documentation_pending=True), ) -> HttpResponse: if all_public_streams and not user_profile.can_access_public_streams(): - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) all_public_streams = _default_all_public_streams(user_profile, all_public_streams) narrow = _default_narrow(user_profile, narrow) diff --git a/zerver/views/hotspots.py b/zerver/views/hotspots.py index fdab56f417..234f0d2571 100644 --- a/zerver/views/hotspots.py +++ b/zerver/views/hotspots.py @@ -3,9 +3,10 @@ from django.utils.translation import gettext as _ from zerver.decorator import human_users_only from zerver.lib.actions import do_mark_hotspot_as_read +from zerver.lib.exceptions import JsonableError from zerver.lib.hotspots import ALL_HOTSPOTS from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.models import UserProfile @@ -15,6 +16,6 @@ def mark_hotspot_as_read( request: HttpRequest, user: UserProfile, hotspot: str = REQ() ) -> HttpResponse: if hotspot not in ALL_HOTSPOTS: - return json_error(_("Unknown hotspot: {}").format(hotspot)) + raise JsonableError(_("Unknown hotspot: {}").format(hotspot)) do_mark_hotspot_as_read(user, hotspot) return json_success() diff --git a/zerver/views/invite.py b/zerver/views/invite.py index e70a737e7c..4e73439935 100644 --- a/zerver/views/invite.py +++ b/zerver/views/invite.py @@ -15,7 +15,7 @@ from zerver.lib.actions import ( ) from zerver.lib.exceptions import OrganizationOwnerRequired from zerver.lib.request import REQ, JsonableError, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.streams import access_stream_by_id from zerver.lib.validator import check_int, check_list from zerver.models import MultiuseInvite, PreregistrationUser, Stream, UserProfile @@ -44,7 +44,7 @@ def invite_users_backend( # be handled by the decorator above. raise JsonableError(_("Insufficient permission")) if invite_as not in PreregistrationUser.INVITE_AS.values(): - return json_error(_("Must be invited as an valid type of user")) + raise JsonableError(_("Must be invited as an valid type of user")) check_if_owner_required(invite_as, user_profile) if ( invite_as @@ -54,11 +54,11 @@ def invite_users_backend( ] and not user_profile.is_realm_admin ): - return json_error(_("Must be an organization administrator")) + raise JsonableError(_("Must be an organization administrator")) if not invitee_emails_raw: - return json_error(_("You must specify at least one email address.")) + raise JsonableError(_("You must specify at least one email address.")) if not stream_ids: - return json_error(_("You must specify at least one stream for invitees to join.")) + raise JsonableError(_("You must specify at least one stream for invitees to join.")) invitee_emails = get_invitee_emails_set(invitee_emails_raw) @@ -67,7 +67,7 @@ def invite_users_backend( try: (stream, sub) = access_stream_by_id(user_profile, stream_id) except JsonableError: - return json_error( + raise JsonableError( _("Stream does not exist with id: {}. No invites were sent.").format(stream_id) ) streams.append(stream) @@ -174,7 +174,7 @@ def generate_multiuse_invite_backend( try: (stream, sub) = access_stream_by_id(user_profile, stream_id) except JsonableError: - return json_error(_("Invalid stream id {}. No invites were sent.").format(stream_id)) + raise JsonableError(_("Invalid stream id {}. No invites were sent.").format(stream_id)) streams.append(stream) invite_link = do_create_multiuse_invite_link(user_profile, invite_as, streams) diff --git a/zerver/views/message_edit.py b/zerver/views/message_edit.py index 3d8c6b51eb..2ae4587376 100644 --- a/zerver/views/message_edit.py +++ b/zerver/views/message_edit.py @@ -12,7 +12,7 @@ from zerver.lib.actions import check_update_message, do_delete_messages from zerver.lib.exceptions import JsonableError from zerver.lib.html_diff import highlight_html_differences from zerver.lib.message import access_message -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.topic import LEGACY_PREV_TOPIC, REQ_topic from zerver.lib.validator import check_bool, check_string_in, to_non_negative_int @@ -74,7 +74,7 @@ def get_message_edit_history( message_id: int = REQ(converter=to_non_negative_int, path_only=True), ) -> HttpResponse: if not user_profile.realm.allow_edit_history: - return json_error(_("Message edit history is disabled in this organization")) + raise JsonableError(_("Message edit history is disabled in this organization")) message, ignored_user_message = access_message(user_profile, message_id) # Extract the message edit history from the message diff --git a/zerver/views/message_fetch.py b/zerver/views/message_fetch.py index 0a72fbd29c..8eed1b433b 100644 --- a/zerver/views/message_fetch.py +++ b/zerver/views/message_fetch.py @@ -38,7 +38,7 @@ from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids from zerver.lib.exceptions import ErrorCode, JsonableError, MissingAuthenticationError from zerver.lib.message import get_first_visible_message_id, messages_for_ids from zerver.lib.narrow import is_web_public_compatible, is_web_public_narrow -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection from zerver.lib.streams import ( can_access_stream_history_by_id, @@ -946,7 +946,7 @@ def get_messages_backend( ) -> HttpResponse: anchor = parse_anchor_value(anchor_val, use_first_unread_anchor_val) if num_before + num_after > MAX_MESSAGES_PER_FETCH: - return json_error( + raise JsonableError( _("Too many messages requested (maximum {}).").format( MAX_MESSAGES_PER_FETCH, ) diff --git a/zerver/views/message_send.py b/zerver/views/message_send.py index 73e7ceb36e..41a2036e0d 100644 --- a/zerver/views/message_send.py +++ b/zerver/views/message_send.py @@ -18,8 +18,9 @@ from zerver.lib.actions import ( extract_private_recipients, extract_stream_indicator, ) +from zerver.lib.exceptions import JsonableError from zerver.lib.message import render_markdown -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.timestamp import convert_to_UTC from zerver.lib.topic import REQ_topic from zerver.lib.zcommand import process_zcommands @@ -151,7 +152,7 @@ def handle_deferred_message( try: deliver_at = dateparser(defer_until) except ValueError: - return json_error(_("Invalid time format")) + raise JsonableError(_("Invalid time format")) deliver_at_usertz = deliver_at if deliver_at_usertz.tzinfo is None: @@ -160,7 +161,7 @@ def handle_deferred_message( deliver_at = convert_to_UTC(deliver_at_usertz) if deliver_at <= timezone_now(): - return json_error(_("Time must be in the future.")) + raise JsonableError(_("Time must be in the future.")) check_schedule_message( sender, @@ -223,13 +224,13 @@ def send_message_backend( client = request.client can_forge_sender = request.user.can_forge_sender if forged and not can_forge_sender: - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) realm = None if realm_str and realm_str != user_profile.realm.string_id: # The realm_str parameter does nothing, because it has to match # the user's realm - but we keep it around for backward compatibility. - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) if client.name in ["zephyr_mirror", "irc_mirror", "jabber_mirror", "JabberMirror"]: # Here's how security works for mirroring: @@ -246,14 +247,14 @@ def send_message_backend( # `create_mirrored_message_users` below, which checks the # same-realm constraint. if "sender" not in request.POST: - return json_error(_("Missing sender")) + raise JsonableError(_("Missing sender")) if message_type_name != "private" and not can_forge_sender: - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) # For now, mirroring only works with recipient emails, not for # recipient user IDs. if not all(isinstance(to_item, str) for to_item in message_to): - return json_error(_("Mirroring not allowed with recipient user IDs")) + raise JsonableError(_("Mirroring not allowed with recipient user IDs")) # We need this manual cast so that mypy doesn't complain about # create_mirrored_message_users not being able to accept a Sequence[int] @@ -263,18 +264,18 @@ def send_message_backend( try: mirror_sender = create_mirrored_message_users(request, user_profile, message_to) except InvalidMirrorInput: - return json_error(_("Invalid mirrored message")) + raise JsonableError(_("Invalid mirrored message")) if client.name == "zephyr_mirror" and not user_profile.realm.is_zephyr_mirror_realm: - return json_error(_("Zephyr mirroring is not allowed in this organization")) + raise JsonableError(_("Zephyr mirroring is not allowed in this organization")) sender = mirror_sender else: if "sender" in request.POST: - return json_error(_("Invalid mirrored message")) + raise JsonableError(_("Invalid mirrored message")) sender = user_profile if (delivery_type == "send_later" or delivery_type == "remind") and defer_until is None: - return json_error(_("Missing deliver_at in a request for delayed message delivery")) + raise JsonableError(_("Missing deliver_at in a request for delayed message delivery")) if (delivery_type == "send_later" or delivery_type == "remind") and defer_until is not None: return handle_deferred_message( diff --git a/zerver/views/muting.py b/zerver/views/muting.py index df7cd8b4c1..7e1ff72beb 100644 --- a/zerver/views/muting.py +++ b/zerver/views/muting.py @@ -6,8 +6,9 @@ from django.utils.timezone import now as timezone_now from django.utils.translation import gettext as _ from zerver.lib.actions import do_mute_topic, do_mute_user, do_unmute_topic, do_unmute_user +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.streams import ( access_stream_by_id, access_stream_by_name, @@ -36,7 +37,7 @@ def mute_topic( (stream, sub) = access_stream_by_id(user_profile, stream_id) if topic_is_muted(user_profile, stream.id, topic_name): - return json_error(_("Topic already muted")) + raise JsonableError(_("Topic already muted")) do_mute_topic(user_profile, stream, topic_name, date_muted) return json_success() @@ -54,7 +55,7 @@ def unmute_topic( stream = access_stream_for_unmute_topic_by_id(user_profile, stream_id, error) if not topic_is_muted(user_profile, stream.id, topic_name): - return json_error(error) + raise JsonableError(error) do_unmute_topic(user_profile, stream, topic_name) return json_success() @@ -91,13 +92,13 @@ def update_muted_topic( def mute_user(request: HttpRequest, user_profile: UserProfile, muted_user_id: int) -> HttpResponse: if user_profile.id == muted_user_id: - return json_error(_("Cannot mute self")) + raise JsonableError(_("Cannot mute self")) muted_user = access_user_by_id(user_profile, muted_user_id, allow_bots=False, for_admin=False) date_muted = timezone_now() if get_mute_object(user_profile, muted_user) is not None: - return json_error(_("User already muted")) + raise JsonableError(_("User already muted")) do_mute_user(user_profile, muted_user, date_muted) return json_success() @@ -110,7 +111,7 @@ def unmute_user( mute_object = get_mute_object(user_profile, muted_user) if mute_object is None: - return json_error(_("User is not muted")) + raise JsonableError(_("User is not muted")) do_unmute_user(mute_object) return json_success() diff --git a/zerver/views/presence.py b/zerver/views/presence.py index 79becf5d08..6fdc2fe02c 100644 --- a/zerver/views/presence.py +++ b/zerver/views/presence.py @@ -10,7 +10,7 @@ from zerver.decorator import human_users_only from zerver.lib.actions import do_update_user_status, update_user_presence from zerver.lib.presence import get_presence_for_user, get_presence_response from zerver.lib.request import REQ, JsonableError, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.validator import check_bool, check_capped_string from zerver.models import ( @@ -37,14 +37,14 @@ def get_presence_backend( email = user_id_or_email target = get_active_user(email, user_profile.realm) except UserProfile.DoesNotExist: - return json_error(_("No such user")) + raise JsonableError(_("No such user")) if target.is_bot: - return json_error(_("Presence is not supported for bot users.")) + raise JsonableError(_("Presence is not supported for bot users.")) presence_dict = get_presence_for_user(target.id) if len(presence_dict) == 0: - return json_error( + raise JsonableError( _("No presence data for {user_id_or_email}").format(user_id_or_email=user_id_or_email) ) @@ -73,7 +73,7 @@ def update_user_status_backend( status_text = status_text.strip() if (away is None) and (status_text is None): - return json_error(_("Client did not pass any new values.")) + raise JsonableError(_("Client did not pass any new values.")) do_update_user_status( user_profile=user_profile, diff --git a/zerver/views/realm.py b/zerver/views/realm.py index db4a922f52..7fe549e925 100644 --- a/zerver/views/realm.py +++ b/zerver/views/realm.py @@ -21,7 +21,7 @@ from zerver.lib.actions import ( from zerver.lib.exceptions import OrganizationOwnerRequired from zerver.lib.i18n import get_available_language_codes from zerver.lib.request import REQ, JsonableError, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.retention import parse_message_retention_days from zerver.lib.streams import access_stream_by_id from zerver.lib.validator import ( @@ -131,15 +131,15 @@ def update_realm( if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() if True not in list(authentication_methods.values()): - return json_error(_("At least one authentication method must be enabled.")) + raise JsonableError(_("At least one authentication method must be enabled.")) if video_chat_provider is not None and video_chat_provider not in { p["id"] for p in Realm.VIDEO_CHAT_PROVIDERS.values() }: - return json_error(_("Invalid video_chat_provider {}").format(video_chat_provider)) + raise JsonableError(_("Invalid video_chat_provider {}").format(video_chat_provider)) if giphy_rating is not None and giphy_rating not in { p["id"] for p in Realm.GIPHY_RATING_OPTIONS.values() }: - return json_error(_("Invalid giphy_rating {}").format(giphy_rating)) + raise JsonableError(_("Invalid giphy_rating {}").format(giphy_rating)) message_retention_days: Optional[int] = None if message_retention_days_raw is not None: diff --git a/zerver/views/realm_domains.py b/zerver/views/realm_domains.py index 1bc52c441d..5525a8f23d 100644 --- a/zerver/views/realm_domains.py +++ b/zerver/views/realm_domains.py @@ -5,8 +5,9 @@ from django.utils.translation import gettext as _ from zerver.decorator import require_realm_admin from zerver.lib.actions import do_add_realm_domain, do_change_realm_domain, do_remove_realm_domain from zerver.lib.domains import validate_domain +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.validator import check_bool from zerver.models import RealmDomain, UserProfile, get_realm_domains @@ -28,9 +29,9 @@ def create_realm_domain( try: validate_domain(domain) except ValidationError as e: - return json_error(_("Invalid domain: {}").format(e.messages[0])) + raise JsonableError(_("Invalid domain: {}").format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): - return json_error( + raise JsonableError( _("The domain {domain} is already a part of your organization.").format(domain=domain) ) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains) @@ -49,7 +50,7 @@ def patch_realm_domain( realm_domain = RealmDomain.objects.get(realm=user_profile.realm, domain=domain) do_change_realm_domain(realm_domain, allow_subdomains) except RealmDomain.DoesNotExist: - return json_error(_("No entry found for domain {domain}.").format(domain=domain)) + raise JsonableError(_("No entry found for domain {domain}.").format(domain=domain)) return json_success() @@ -62,5 +63,5 @@ def delete_realm_domain( realm_domain = RealmDomain.objects.get(realm=user_profile.realm, domain=domain) do_remove_realm_domain(realm_domain, acting_user=user_profile) except RealmDomain.DoesNotExist: - return json_error(_("No entry found for domain {domain}.").format(domain=domain)) + raise JsonableError(_("No entry found for domain {domain}.").format(domain=domain)) return json_success() diff --git a/zerver/views/realm_emoji.py b/zerver/views/realm_emoji.py index 71dbfb60e5..f5f93bc50b 100644 --- a/zerver/views/realm_emoji.py +++ b/zerver/views/realm_emoji.py @@ -6,7 +6,7 @@ from zerver.decorator import require_member_or_admin from zerver.lib.actions import check_add_realm_emoji, do_remove_realm_emoji from zerver.lib.emoji import check_emoji_admin, check_valid_emoji_name from zerver.lib.request import REQ, JsonableError, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.models import RealmEmoji, UserProfile @@ -28,12 +28,12 @@ def upload_emoji( if RealmEmoji.objects.filter( realm=user_profile.realm, name=emoji_name, deactivated=False ).exists(): - return json_error(_("A custom emoji with this name already exists.")) + raise JsonableError(_("A custom emoji with this name already exists.")) if len(request.FILES) != 1: - return json_error(_("You must upload exactly one file.")) + raise JsonableError(_("You must upload exactly one file.")) emoji_file = list(request.FILES.values())[0] if (settings.MAX_EMOJI_FILE_SIZE_MIB * 1024 * 1024) < emoji_file.size: - return json_error( + raise JsonableError( _("Uploaded file is larger than the allowed limit of {} MiB").format( settings.MAX_EMOJI_FILE_SIZE_MIB, ) @@ -41,7 +41,7 @@ def upload_emoji( realm_emoji = check_add_realm_emoji(user_profile.realm, emoji_name, user_profile, emoji_file) if realm_emoji is None: - return json_error(_("Image file upload failed.")) + raise JsonableError(_("Image file upload failed.")) return json_success() diff --git a/zerver/views/realm_export.py b/zerver/views/realm_export.py index ddadb741de..2f95dba0c6 100644 --- a/zerver/views/realm_export.py +++ b/zerver/views/realm_export.py @@ -9,9 +9,10 @@ from django.utils.translation import gettext as _ from analytics.models import RealmCount from zerver.decorator import require_realm_admin from zerver.lib.actions import do_delete_realm_export, notify_realm_export +from zerver.lib.exceptions import JsonableError from zerver.lib.export import get_realm_exports_serialized from zerver.lib.queue import queue_json_publish -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.models import RealmAuditLog, UserProfile @@ -36,7 +37,7 @@ def export_realm(request: HttpRequest, user: UserProfile) -> HttpResponse: realm=realm, event_type=event_type, event_time__gte=event_time_delta ) if len(limit_check) >= EXPORT_LIMIT: - return json_error(_("Exceeded rate limit.")) + raise JsonableError(_("Exceeded rate limit.")) total_messages = sum( realm_count.value @@ -48,7 +49,7 @@ def export_realm(request: HttpRequest, user: UserProfile) -> HttpResponse: total_messages > MAX_MESSAGE_HISTORY or user.realm.currently_used_upload_space_bytes() > MAX_UPLOAD_QUOTA ): - return json_error( + raise JsonableError( _("Please request a manual export from {email}.").format( email=settings.ZULIP_ADMINISTRATOR, ) @@ -87,10 +88,10 @@ def delete_realm_export(request: HttpRequest, user: UserProfile, export_id: int) id=export_id, realm=user.realm, event_type=RealmAuditLog.REALM_EXPORTED ) except RealmAuditLog.DoesNotExist: - return json_error(_("Invalid data export ID")) + raise JsonableError(_("Invalid data export ID")) export_data = orjson.loads(audit_log_entry.extra_data) if "deleted_timestamp" in export_data: - return json_error(_("Export already deleted")) + raise JsonableError(_("Export already deleted")) do_delete_realm_export(user, audit_log_entry) return json_success() diff --git a/zerver/views/realm_icon.py b/zerver/views/realm_icon.py index 55e889e0d1..82064f3fef 100644 --- a/zerver/views/realm_icon.py +++ b/zerver/views/realm_icon.py @@ -5,8 +5,9 @@ from django.utils.translation import gettext as _ from zerver.decorator import require_realm_admin from zerver.lib.actions import do_change_icon_source +from zerver.lib.exceptions import JsonableError from zerver.lib.realm_icon import realm_icon_url -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.upload import upload_icon_image from zerver.lib.url_encoding import add_query_arg_to_redirect_url from zerver.models import UserProfile @@ -16,11 +17,11 @@ from zerver.models import UserProfile def upload_icon(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: if len(request.FILES) != 1: - return json_error(_("You must upload exactly one icon.")) + raise JsonableError(_("You must upload exactly one icon.")) icon_file = list(request.FILES.values())[0] if (settings.MAX_ICON_FILE_SIZE_MIB * 1024 * 1024) < icon_file.size: - return json_error( + raise JsonableError( _("Uploaded file is larger than the allowed limit of {} MiB").format( settings.MAX_ICON_FILE_SIZE_MIB, ) diff --git a/zerver/views/realm_linkifiers.py b/zerver/views/realm_linkifiers.py index b45c5768c6..6a91bc3540 100644 --- a/zerver/views/realm_linkifiers.py +++ b/zerver/views/realm_linkifiers.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext as _ from zerver.decorator import require_realm_admin from zerver.lib.actions import do_add_linkifier, do_remove_linkifier, do_update_linkifier +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_error, json_success from zerver.models import RealmFilter, UserProfile, linkifiers_for_realm @@ -41,7 +42,7 @@ def delete_linkifier( try: do_remove_linkifier(realm=user_profile.realm, id=filter_id) except RealmFilter.DoesNotExist: - return json_error(_("Linkifier not found.")) + raise JsonableError(_("Linkifier not found.")) return json_success() @@ -63,6 +64,6 @@ def update_linkifier( ) return json_success() except RealmFilter.DoesNotExist: - return json_error(_("Linkifier not found.")) + raise JsonableError(_("Linkifier not found.")) except ValidationError as e: return json_error(e.messages[0], data={"errors": dict(e)}) diff --git a/zerver/views/realm_logo.py b/zerver/views/realm_logo.py index 77b13b94c1..1f3980b861 100644 --- a/zerver/views/realm_logo.py +++ b/zerver/views/realm_logo.py @@ -5,9 +5,10 @@ from django.utils.translation import gettext as _ from zerver.decorator import require_realm_admin from zerver.lib.actions import do_change_logo_source +from zerver.lib.exceptions import JsonableError from zerver.lib.realm_logo import get_realm_logo_url from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.upload import upload_logo_image from zerver.lib.url_encoding import add_query_arg_to_redirect_url from zerver.lib.validator import check_bool @@ -22,10 +23,10 @@ def upload_logo( user_profile.realm.ensure_not_on_limited_plan() if len(request.FILES) != 1: - return json_error(_("You must upload exactly one logo.")) + raise JsonableError(_("You must upload exactly one logo.")) logo_file = list(request.FILES.values())[0] if (settings.MAX_LOGO_FILE_SIZE_MIB * 1024 * 1024) < logo_file.size: - return json_error( + raise JsonableError( _("Uploaded file is larger than the allowed limit of {} MiB").format( settings.MAX_LOGO_FILE_SIZE_MIB, ) diff --git a/zerver/views/storage.py b/zerver/views/storage.py index 18c9dc72a2..57c49a09ec 100644 --- a/zerver/views/storage.py +++ b/zerver/views/storage.py @@ -10,7 +10,8 @@ from zerver.lib.bot_storage import ( remove_bot_storage, set_bot_storage, ) -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.validator import check_dict, check_list, check_string from zerver.models import UserProfile @@ -24,7 +25,7 @@ def update_storage( try: set_bot_storage(user_profile, list(storage.items())) except StateError as e: # nocoverage - return json_error(str(e)) + raise JsonableError(str(e)) return json_success() @@ -39,7 +40,7 @@ def get_storage( try: storage = {key: get_bot_storage(user_profile, key) for key in keys} except StateError as e: - return json_error(str(e)) + raise JsonableError(str(e)) return json_success({"storage": storage}) @@ -54,5 +55,5 @@ def remove_storage( try: remove_bot_storage(user_profile, keys) except StateError as e: - return json_error(str(e)) + raise JsonableError(str(e)) return json_success() diff --git a/zerver/views/streams.py b/zerver/views/streams.py index b4e4e847da..1e757fb330 100644 --- a/zerver/views/streams.py +++ b/zerver/views/streams.py @@ -148,7 +148,7 @@ def add_default_stream( ) -> HttpResponse: (stream, sub) = access_stream_by_id(user_profile, stream_id) if stream.invite_only: - return json_error(_("Private streams cannot be made default.")) + raise JsonableError(_("Private streams cannot be made default.")) do_add_default_stream(stream) return json_success() @@ -180,7 +180,7 @@ def update_default_stream_group_info( new_description: Optional[str] = REQ(default=None), ) -> None: if not new_group_name and not new_description: - return json_error(_('You must pass "new_description" or "new_group_name".')) + raise JsonableError(_('You must pass "new_description" or "new_group_name".')) group = access_default_stream_group_by_id(user_profile.realm, group_id) if new_group_name is not None: @@ -210,7 +210,7 @@ def update_default_stream_group_streams( elif op == "remove": do_remove_streams_from_default_stream_group(user_profile.realm, group, streams) else: - return json_error(_('Invalid value for "op". Specify one of "add" or "remove".')) + raise JsonableError(_('Invalid value for "op". Specify one of "add" or "remove".')) return json_success() @@ -278,7 +278,7 @@ def update_stream_backend( if new_name is not None: new_name = new_name.strip() if stream.name == new_name: - return json_error(_("Stream already has that name!")) + raise JsonableError(_("Stream already has that name!")) if stream.name.lower() != new_name.lower(): # Check that the stream name is available (unless we are # are only changing the casing of the stream name). @@ -301,7 +301,7 @@ def update_stream_backend( default_stream_ids = {s.id for s in get_default_streams_for_realm(stream.realm_id)} (stream, sub) = access_stream_by_id(user_profile, stream_id) if is_private and stream.id in default_stream_ids: - return json_error(_("Default streams cannot be made private.")) + raise JsonableError(_("Default streams cannot be made private.")) do_change_stream_invite_only(stream, is_private, history_public_to_subscribers) return json_success() @@ -340,7 +340,7 @@ def update_subscriptions_backend( add: Sequence[Mapping[str, str]] = REQ(json_validator=add_subscriptions_schema, default=[]), ) -> HttpResponse: if not add and not delete: - return json_error(_('Nothing to do. Specify at least one of "add" or "delete".')) + raise JsonableError(_('Nothing to do. Specify at least one of "add" or "delete".')) thunks = [ lambda: add_subscriptions_backend(request, user_profile, streams_raw=add), @@ -498,7 +498,7 @@ def add_subscriptions_backend( user_profile, existing_streams ) if len(unauthorized_streams) > 0 and authorization_errors_fatal: - return json_error( + raise JsonableError( _("Unable to access stream ({stream_name}).").format( stream_name=unauthorized_streams[0].name, ) @@ -508,7 +508,7 @@ def add_subscriptions_backend( if len(principals) > 0: if realm.is_zephyr_mirror_realm and not all(stream.invite_only for stream in streams): - return json_error( + raise JsonableError( _("You can only invite other Zephyr mirroring users to private streams.") ) if not user_profile.can_subscribe_other_users(): @@ -865,16 +865,16 @@ def update_subscription_properties_backend( value = change["value"] if property not in property_converters: - return json_error(_("Unknown subscription property: {}").format(property)) + raise JsonableError(_("Unknown subscription property: {}").format(property)) (stream, sub) = access_stream_by_id(user_profile, stream_id) if sub is None: - return json_error(_("Not subscribed to stream id {}").format(stream_id)) + raise JsonableError(_("Not subscribed to stream id {}").format(stream_id)) try: value = property_converters[property](property, value) except ValidationError as error: - return json_error(error.message) + raise JsonableError(error.message) do_change_subscription_property( user_profile, sub, stream, property, value, acting_user=user_profile diff --git a/zerver/views/submessage.py b/zerver/views/submessage.py index d9a2df9446..60f138dade 100644 --- a/zerver/views/submessage.py +++ b/zerver/views/submessage.py @@ -8,7 +8,7 @@ from zerver.decorator import REQ, has_request_variables from zerver.lib.actions import do_add_submessage, verify_submessage_sender from zerver.lib.exceptions import JsonableError from zerver.lib.message import access_message -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.validator import check_int, validate_poll_data, validate_todo_data from zerver.lib.widget import get_widget_type from zerver.models import UserProfile @@ -35,7 +35,7 @@ def process_submessage( try: widget_data = orjson.loads(content) except orjson.JSONDecodeError: - return json_error(_("Invalid json for submessage")) + raise JsonableError(_("Invalid json for submessage")) widget_type = get_widget_type(message_id=message.id) diff --git a/zerver/views/typing.py b/zerver/views/typing.py index 1bd9929c1f..419233c34e 100644 --- a/zerver/views/typing.py +++ b/zerver/views/typing.py @@ -5,7 +5,8 @@ from django.utils.translation import gettext as _ from zerver.decorator import REQ, has_request_variables from zerver.lib.actions import check_send_typing_notification, do_send_stream_typing_notification -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.streams import access_stream_by_id, access_stream_for_send_message from zerver.lib.validator import check_int, check_list, check_string_in from zerver.models import UserProfile @@ -28,14 +29,14 @@ def send_notification_backend( to_length = len(notification_to) if to_length == 0: - return json_error(_("Empty 'to' list")) + raise JsonableError(_("Empty 'to' list")) if message_type == "stream": if to_length > 1: - return json_error(_("Cannot send to multiple streams")) + raise JsonableError(_("Cannot send to multiple streams")) if topic is None: - return json_error(_("Missing topic")) + raise JsonableError(_("Missing topic")) stream_id = notification_to[0] # Verify that the user has access to the stream and has diff --git a/zerver/views/upload.py b/zerver/views/upload.py index d0ec8e0ae3..453b07b12f 100644 --- a/zerver/views/upload.py +++ b/zerver/views/upload.py @@ -7,7 +7,8 @@ from django.utils.cache import patch_cache_control from django.utils.translation import gettext as _ from django_sendfile import sendfile -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.upload import ( INLINE_MIME_TYPES, check_upload_within_quota, @@ -104,23 +105,23 @@ def serve_file( def serve_local_file_unauthed(request: HttpRequest, token: str, filename: str) -> HttpResponse: path_id = get_local_file_path_id_from_token(token) if path_id is None: - return json_error(_("Invalid token")) + raise JsonableError(_("Invalid token")) if path_id.split("/")[-1] != filename: - return json_error(_("Invalid filename")) + raise JsonableError(_("Invalid filename")) return serve_local(request, path_id, url_only=False) def upload_file_backend(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: if len(request.FILES) == 0: - return json_error(_("You must specify a file to upload")) + raise JsonableError(_("You must specify a file to upload")) if len(request.FILES) != 1: - return json_error(_("You may only upload one file at a time")) + raise JsonableError(_("You may only upload one file at a time")) user_file = list(request.FILES.values())[0] file_size = user_file.size if settings.MAX_FILE_UPLOAD_SIZE * 1024 * 1024 < file_size: - return json_error( + raise JsonableError( _("Uploaded file is larger than the allowed limit of {} MiB").format( settings.MAX_FILE_UPLOAD_SIZE, ) diff --git a/zerver/views/user_groups.py b/zerver/views/user_groups.py index 9015267b69..0a60be4185 100644 --- a/zerver/views/user_groups.py +++ b/zerver/views/user_groups.py @@ -14,7 +14,7 @@ from zerver.lib.actions import ( ) from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.user_groups import ( access_user_group_by_id, get_memberships_of_users, @@ -58,7 +58,7 @@ def edit_user_group( description: str = REQ(default=""), ) -> HttpResponse: if not (name or description): - return json_error(_("No new data supplied")) + raise JsonableError(_("No new data supplied")) user_group = access_user_group_by_id(user_group_id, user_profile) @@ -93,7 +93,7 @@ def update_user_group_backend( add: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]), ) -> HttpResponse: if not add and not delete: - return json_error(_('Nothing to do. Specify at least one of "add" or "delete".')) + raise JsonableError(_('Nothing to do. Specify at least one of "add" or "delete".')) thunks = [ lambda: add_members_to_group_backend( diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py index e58c4d0f39..e9f02bb318 100644 --- a/zerver/views/user_settings.py +++ b/zerver/views/user_settings.py @@ -39,7 +39,7 @@ from zerver.lib.email_validation import ( from zerver.lib.i18n import get_available_language_codes from zerver.lib.rate_limiter import RateLimited from zerver.lib.request import JsonableError -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.send_email import FromAddress, send_email from zerver.lib.upload import upload_avatar_image from zerver.lib.validator import check_bool, check_int, check_int_in, check_string_in @@ -98,12 +98,12 @@ def json_change_settings( new_password: str = REQ(default=""), ) -> HttpResponse: if not (full_name or new_password or email): - return json_error(_("Please fill out all fields.")) + raise JsonableError(_("Please fill out all fields.")) if new_password != "": return_data: Dict[str, Any] = {} if email_belongs_to_ldap(user_profile.realm, user_profile.delivery_email): - return json_error(_("Your Zulip password is managed in LDAP")) + raise JsonableError(_("Your Zulip password is managed in LDAP")) try: if not authenticate( @@ -113,18 +113,18 @@ def json_change_settings( realm=user_profile.realm, return_data=return_data, ): - return json_error(_("Wrong password!")) + raise JsonableError(_("Wrong password!")) except RateLimited as e: assert e.secs_to_freedom is not None secs_to_freedom = int(e.secs_to_freedom) - return json_error( + raise JsonableError( _("You're making too many attempts! Try again in {} seconds.").format( secs_to_freedom ), ) if not check_password_strength(new_password): - return json_error(_("New password is too weak!")) + raise JsonableError(_("New password is too weak!")) do_change_password(user_profile, new_password) # In Django 1.10, password changes invalidates sessions, see @@ -146,14 +146,14 @@ def json_change_settings( new_email = email.strip() if user_profile.delivery_email != new_email and new_email != "": if user_profile.realm.email_changes_disabled and not user_profile.is_realm_admin: - return json_error(_("Email address changes are disabled in this organization.")) + raise JsonableError(_("Email address changes are disabled in this organization.")) error = validate_email_is_valid( new_email, get_realm_email_validator(user_profile.realm), ) if error: - return json_error(error) + raise JsonableError(error) try: validate_email_not_already_in_realm( @@ -162,7 +162,7 @@ def json_change_settings( verbose=False, ) except ValidationError as e: - return json_error(e.message) + raise JsonableError(e.message) do_start_email_change_process(user_profile, new_email) result["account_email"] = _("Check your email for a confirmation link. ") @@ -291,14 +291,14 @@ def json_change_notify_settings( def set_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: if len(request.FILES) != 1: - return json_error(_("You must upload exactly one avatar.")) + raise JsonableError(_("You must upload exactly one avatar.")) if avatar_changes_disabled(user_profile.realm) and not user_profile.is_realm_admin: - return json_error(str(AVATAR_CHANGES_DISABLED_ERROR)) + raise JsonableError(str(AVATAR_CHANGES_DISABLED_ERROR)) user_file = list(request.FILES.values())[0] if (settings.MAX_AVATAR_FILE_SIZE_MIB * 1024 * 1024) < user_file.size: - return json_error( + raise JsonableError( _("Uploaded file is larger than the allowed limit of {} MiB").format( settings.MAX_AVATAR_FILE_SIZE_MIB, ) @@ -315,7 +315,7 @@ def set_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> HttpR def delete_avatar_backend(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: if avatar_changes_disabled(user_profile.realm) and not user_profile.is_realm_admin: - return json_error(str(AVATAR_CHANGES_DISABLED_ERROR)) + raise JsonableError(str(AVATAR_CHANGES_DISABLED_ERROR)) do_change_avatar_fields( user_profile, UserProfile.AVATAR_FROM_GRAVATAR, acting_user=user_profile diff --git a/zerver/views/users.py b/zerver/views/users.py index 613bfc15a6..e76ab41ada 100644 --- a/zerver/views/users.py +++ b/zerver/views/users.py @@ -32,7 +32,7 @@ from zerver.lib.email_validation import email_allowed_for_realm from zerver.lib.exceptions import CannotDeactivateLastUserError, OrganizationOwnerRequired from zerver.lib.integrations import EMBEDDED_BOTS from zerver.lib.request import REQ, JsonableError, has_request_variables -from zerver.lib.response import json_error, json_success +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.upload import upload_avatar_image @@ -96,7 +96,7 @@ def deactivate_user_backend( if target.is_realm_owner and not user_profile.is_realm_owner: raise OrganizationOwnerRequired() if check_last_owner(target): - return json_error(_("Cannot deactivate the only organization owner")) + raise JsonableError(_("Cannot deactivate the only organization owner")) return _deactivate_user_profile_backend(request, user_profile, target) @@ -182,7 +182,7 @@ def update_user_backend( raise OrganizationOwnerRequired() if target.role == UserProfile.ROLE_REALM_OWNER and check_last_owner(user_profile): - return json_error( + raise JsonableError( _("The owner permission cannot be removed from the only organization owner.") ) do_change_user_role(target, role, acting_user=user_profile) @@ -278,11 +278,11 @@ def patch_bot_backend( try: owner = get_user_profile_by_id_in_realm(bot_owner_id, user_profile.realm) except UserProfile.DoesNotExist: - return json_error(_("Failed to change owner, no such user")) + raise JsonableError(_("Failed to change owner, no such user")) if not owner.is_active: - return json_error(_("Failed to change owner, user is deactivated")) + raise JsonableError(_("Failed to change owner, user is deactivated")) if owner.is_bot: - return json_error(_("Failed to change owner, bots can't own other bots")) + raise JsonableError(_("Failed to change owner, bots can't own other bots")) previous_owner = bot.bot_owner if previous_owner != owner: @@ -321,7 +321,7 @@ def patch_bot_backend( avatar_source = UserProfile.AVATAR_FROM_USER do_change_avatar_fields(bot, avatar_source, acting_user=user_profile) else: - return json_error(_("You may only upload one file at a time")) + raise JsonableError(_("You may only upload one file at a time")) json_result = dict( full_name=bot.full_name, @@ -384,7 +384,7 @@ def add_bot_backend( try: email = f"{short_name}@{user_profile.realm.get_bot_domain()}" except InvalidFakeEmailDomain: - return json_error( + raise JsonableError( _( "Can't create bots until FAKE_EMAIL_DOMAIN is correctly configured.\n" "Please contact your server administrator." @@ -394,16 +394,16 @@ def add_bot_backend( if bot_type == UserProfile.EMBEDDED_BOT: if not settings.EMBEDDED_BOTS_ENABLED: - return json_error(_("Embedded bots are not enabled.")) + raise JsonableError(_("Embedded bots are not enabled.")) if service_name not in [bot.name for bot in EMBEDDED_BOTS]: - return json_error(_("Invalid embedded bot name.")) + raise JsonableError(_("Invalid embedded bot name.")) if not form.is_valid(): # We validate client-side as well - return json_error(_("Bad name or username")) + raise JsonableError(_("Bad name or username")) try: get_user_by_delivery_email(email, user_profile.realm) - return json_error(_("Username already in use")) + raise JsonableError(_("Username already in use")) except UserProfile.DoesNotExist: pass @@ -419,7 +419,7 @@ def add_bot_backend( if len(request.FILES) == 0: avatar_source = UserProfile.AVATAR_FROM_GRAVATAR elif len(request.FILES) != 1: - return json_error(_("You may only upload one file at a time")) + raise JsonableError(_("You may only upload one file at a time")) else: avatar_source = UserProfile.AVATAR_FROM_USER @@ -569,12 +569,12 @@ def create_user_backend( full_name_raw: str = REQ("full_name"), ) -> HttpResponse: if not user_profile.can_create_users: - return json_error(_("User not authorized for this query")) + raise JsonableError(_("User not authorized for this query")) full_name = check_full_name(full_name_raw) form = CreateUserForm({"full_name": full_name, "email": email}) if not form.is_valid(): - return json_error(_("Bad name or username")) + raise JsonableError(_("Bad name or username")) # Check that the new user's email address belongs to the admin's realm # (Since this is an admin API, we don't require the user to have been @@ -583,24 +583,24 @@ def create_user_backend( try: email_allowed_for_realm(email, user_profile.realm) except DomainNotAllowedForRealmError: - return json_error( + raise JsonableError( _("Email '{email}' not allowed in this organization").format( email=email, ) ) except DisposableEmailError: - return json_error(_("Disposable email addresses are not allowed in this organization")) + raise JsonableError(_("Disposable email addresses are not allowed in this organization")) except EmailContainsPlusError: - return json_error(_("Email addresses containing + are not allowed.")) + raise JsonableError(_("Email addresses containing + are not allowed.")) try: get_user_by_delivery_email(email, user_profile.realm) - return json_error(_("Email '{}' already in use").format(email)) + raise JsonableError(_("Email '{}' already in use").format(email)) except UserProfile.DoesNotExist: pass if not check_password_strength(password): - return json_error(PASSWORD_TOO_WEAK_ERROR) + raise JsonableError(PASSWORD_TOO_WEAK_ERROR) target_user = do_create_user(email, password, realm, full_name, acting_user=user_profile) return json_success({"user_id": target_user.id}) diff --git a/zerver/views/video_calls.py b/zerver/views/video_calls.py index 1b27a2976c..03d348dd15 100644 --- a/zerver/views/video_calls.py +++ b/zerver/views/video_calls.py @@ -25,7 +25,7 @@ from zerver.decorator import REQ, has_request_variables, zulip_login_required from zerver.lib.actions import do_set_zoom_token from zerver.lib.exceptions import ErrorCode, JsonableError from zerver.lib.pysa import mark_sanitized -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.subdomains import get_subdomain from zerver.lib.url_encoding import add_query_arg_to_redirect_url, add_query_to_redirect_url from zerver.lib.validator import check_dict, check_string @@ -212,7 +212,7 @@ def join_bigbluebutton( checksum: str = REQ(), ) -> HttpResponse: if settings.BIG_BLUE_BUTTON_URL is None or settings.BIG_BLUE_BUTTON_SECRET is None: - return json_error(_("Big Blue Button is not configured.")) + raise JsonableError(_("Big Blue Button is not configured.")) else: try: response = requests.get( @@ -230,14 +230,14 @@ def join_bigbluebutton( ) response.raise_for_status() except requests.RequestException: - return json_error(_("Error connecting to the Big Blue Button server.")) + raise JsonableError(_("Error connecting to the Big Blue Button server.")) payload = ElementTree.fromstring(response.text) if payload.find("messageKey").text == "checksumError": - return json_error(_("Error authenticating to the Big Blue Button server.")) + raise JsonableError(_("Error authenticating to the Big Blue Button server.")) if payload.find("returncode").text != "SUCCESS": - return json_error(_("Big Blue Button server returned an unexpected error.")) + raise JsonableError(_("Big Blue Button server returned an unexpected error.")) join_params = urlencode( # type: ignore[type-var] # https://github.com/python/typeshed/issues/4234 { diff --git a/zerver/views/zephyr.py b/zerver/views/zephyr.py index 32bb00ecd5..939d1dd319 100644 --- a/zerver/views/zephyr.py +++ b/zerver/views/zephyr.py @@ -12,9 +12,10 @@ from django.utils.translation import gettext as _ from zerver.decorator import authenticated_json_view from zerver.lib.ccache import make_ccache +from zerver.lib.exceptions import JsonableError from zerver.lib.pysa import mark_sanitized from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.users import get_api_key from zerver.models import UserProfile @@ -32,9 +33,9 @@ def webathena_kerberos_login( ) -> HttpResponse: global kerberos_alter_egos if cred is None: - return json_error(_("Could not find Kerberos credential")) + raise JsonableError(_("Could not find Kerberos credential")) if not user_profile.realm.webathena_enabled: - return json_error(_("Webathena login not enabled")) + raise JsonableError(_("Webathena login not enabled")) try: parsed_cred = orjson.loads(cred) @@ -55,7 +56,7 @@ def webathena_kerberos_login( # credential cache file. ccache = mark_sanitized(ccache) except Exception: - return json_error(_("Invalid Kerberos cache")) + raise JsonableError(_("Invalid Kerberos cache")) # TODO: Send these data via (say) RabbitMQ try: @@ -71,6 +72,6 @@ def webathena_kerberos_login( ) except subprocess.CalledProcessError: logging.exception("Error updating the user's ccache", stack_info=True) - return json_error(_("We were unable to set up mirroring for you")) + raise JsonableError(_("We were unable to set up mirroring for you")) return json_success() diff --git a/zerver/webhooks/freshstatus/view.py b/zerver/webhooks/freshstatus/view.py index 92c022aa6f..023f7bdd78 100644 --- a/zerver/webhooks/freshstatus/view.py +++ b/zerver/webhooks/freshstatus/view.py @@ -6,7 +6,8 @@ from django.utils.translation import ugettext as _ from zerver.decorator import REQ, has_request_variables, webhook_view from zerver.lib.actions import send_rate_limited_pm_notification_to_bot_owner -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.send_email import FromAddress from zerver.lib.webhooks.common import check_send_webhook_message, get_setup_webhook_message from zerver.models import UserProfile @@ -84,7 +85,7 @@ def api_freshstatus_webhook( ).strip() send_rate_limited_pm_notification_to_bot_owner(user_profile, user_profile.realm, message) - return json_error(_("Invalid payload")) + raise JsonableError(_("Invalid payload")) check_send_webhook_message(request, user_profile, subject, body) return json_success() diff --git a/zerver/webhooks/front/view.py b/zerver/webhooks/front/view.py index b328165f67..2a97fa1fa1 100644 --- a/zerver/webhooks/front/view.py +++ b/zerver/webhooks/front/view.py @@ -4,8 +4,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -143,7 +144,7 @@ def api_front_webhook( event = payload["type"] if event not in EVENT_FUNCTION_MAPPER: - return json_error(_("Unknown webhook request")) + raise JsonableError(_("Unknown webhook request")) topic = payload["conversation"]["id"] body = get_body_based_on_event(event)(payload) diff --git a/zerver/webhooks/ifttt/view.py b/zerver/webhooks/ifttt/view.py index f6a9be6a0a..5b7a887f8a 100644 --- a/zerver/webhooks/ifttt/view.py +++ b/zerver/webhooks/ifttt/view.py @@ -4,8 +4,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -23,10 +24,10 @@ def api_iftt_app_webhook( if topic is None: topic = payload.get("subject") # Backwards-compatibility if topic is None: - return json_error(_("Topic can't be empty")) + raise JsonableError(_("Topic can't be empty")) if content is None: - return json_error(_("Content can't be empty")) + raise JsonableError(_("Content can't be empty")) check_send_webhook_message(request, user_profile, topic, content) return json_success() diff --git a/zerver/webhooks/librato/view.py b/zerver/webhooks/librato/view.py index 3065d8059c..2936965a54 100644 --- a/zerver/webhooks/librato/view.py +++ b/zerver/webhooks/librato/view.py @@ -6,8 +6,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -169,7 +170,7 @@ def api_librato_webhook( attachments = [] if not attachments and not payload: - return json_error(_("Malformed JSON input")) + raise JsonableError(_("Malformed JSON input")) message_handler = LibratoWebhookHandler(payload, attachments) topic = message_handler.generate_topic() @@ -177,7 +178,7 @@ def api_librato_webhook( try: content = message_handler.handle() except Exception as e: - return json_error(str(e)) + raise JsonableError(str(e)) check_send_webhook_message(request, user_profile, topic, content) return json_success() diff --git a/zerver/webhooks/newrelic/view.py b/zerver/webhooks/newrelic/view.py index 174e17785a..110db4c039 100644 --- a/zerver/webhooks/newrelic/view.py +++ b/zerver/webhooks/newrelic/view.py @@ -5,8 +5,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message, unix_milliseconds_to_timestamp from zerver.models import UserProfile @@ -46,7 +47,7 @@ def api_newrelic_webhook( unix_time = payload.get("timestamp", None) if unix_time is None: - return json_error(_("The newrelic webhook requires timestamp in milliseconds")) + raise JsonableError(_("The newrelic webhook requires timestamp in milliseconds")) info["iso_timestamp"] = unix_milliseconds_to_timestamp(unix_time, "newrelic") @@ -62,7 +63,7 @@ def api_newrelic_webhook( elif "closed" in info["status"]: content = DEFAULT_TEMPLATE.format(**info) else: - return json_error( + raise JsonableError( _("The newrelic webhook requires current_state be in [open|acknowledged|closed]") ) diff --git a/zerver/webhooks/pivotal/view.py b/zerver/webhooks/pivotal/view.py index 8c372ba4e1..7f2ed55bf3 100644 --- a/zerver/webhooks/pivotal/view.py +++ b/zerver/webhooks/pivotal/view.py @@ -8,9 +8,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view -from zerver.lib.exceptions import UnsupportedWebhookEventType +from zerver.lib.exceptions import JsonableError, UnsupportedWebhookEventType from zerver.lib.request import has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -176,7 +176,7 @@ def api_pivotal_webhook(request: HttpRequest, user_profile: UserProfile) -> Http subject, content = api_pivotal_webhook_v5(request, user_profile) if not content: - return json_error(_("Unable to handle Pivotal payload")) + raise JsonableError(_("Unable to handle Pivotal payload")) check_send_webhook_message(request, user_profile, subject, content) return json_success() diff --git a/zerver/webhooks/slack/view.py b/zerver/webhooks/slack/view.py index 339ceb8409..3c3837503d 100644 --- a/zerver/webhooks/slack/view.py +++ b/zerver/webhooks/slack/view.py @@ -3,8 +3,9 @@ from django.utils.translation import gettext as _ from zerver.decorator import webhook_view from zerver.lib.actions import check_send_stream_message +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.models import UserProfile ZULIP_MESSAGE_TEMPLATE = "**{message_sender}**: `{text}`" @@ -24,7 +25,7 @@ def api_slack_webhook( ) -> HttpRequest: if channels_map_to_topics not in list(VALID_OPTIONS.values()): - return json_error(_("Error: channels_map_to_topics parameter other than 0 or 1")) + raise JsonableError(_("Error: channels_map_to_topics parameter other than 0 or 1")) if channels_map_to_topics == VALID_OPTIONS["SHOULD_BE_MAPPED"]: subject = f"channel: {channel_name}" diff --git a/zerver/webhooks/uptimerobot/view.py b/zerver/webhooks/uptimerobot/view.py index 2165d27f84..6a7e3d8f91 100644 --- a/zerver/webhooks/uptimerobot/view.py +++ b/zerver/webhooks/uptimerobot/view.py @@ -5,7 +5,8 @@ from django.utils.translation import ugettext as _ from zerver.decorator import REQ, has_request_variables, webhook_view from zerver.lib.actions import send_rate_limited_pm_notification_to_bot_owner -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.send_email import FromAddress from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -46,7 +47,7 @@ def api_uptimerobot_webhook( ).strip() send_rate_limited_pm_notification_to_bot_owner(user_profile, user_profile.realm, message) - return json_error(_("Invalid payload")) + raise JsonableError(_("Invalid payload")) check_send_webhook_message(request, user_profile, subject, body) return json_success() diff --git a/zerver/webhooks/wordpress/view.py b/zerver/webhooks/wordpress/view.py index fe2839eff9..09496d8968 100644 --- a/zerver/webhooks/wordpress/view.py +++ b/zerver/webhooks/wordpress/view.py @@ -3,8 +3,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -46,7 +47,7 @@ def api_wordpress_webhook( data = WP_LOGIN_TEMPLATE.format(name=user_login) else: - return json_error(_("Unknown WordPress webhook action: {}").format(hook)) + raise JsonableError(_("Unknown WordPress webhook action: {}").format(hook)) topic = "WordPress notification" diff --git a/zerver/webhooks/zabbix/view.py b/zerver/webhooks/zabbix/view.py index c5b9f527aa..c5ce0884f1 100644 --- a/zerver/webhooks/zabbix/view.py +++ b/zerver/webhooks/zabbix/view.py @@ -5,7 +5,8 @@ from django.utils.translation import gettext as _ from zerver.decorator import REQ, has_request_variables, webhook_view from zerver.lib.actions import send_rate_limited_pm_notification_to_bot_owner -from zerver.lib.response import json_error, json_success +from zerver.lib.exceptions import JsonableError +from zerver.lib.response import json_success from zerver.lib.send_email import FromAddress from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -45,7 +46,7 @@ def api_zabbix_webhook( ).strip() send_rate_limited_pm_notification_to_bot_owner(user_profile, user_profile.realm, message) - return json_error(_("Invalid payload")) + raise JsonableError(_("Invalid payload")) check_send_webhook_message(request, user_profile, subject, body) return json_success() diff --git a/zerver/webhooks/zapier/view.py b/zerver/webhooks/zapier/view.py index 65cc966c70..23eaa94d4e 100644 --- a/zerver/webhooks/zapier/view.py +++ b/zerver/webhooks/zapier/view.py @@ -4,8 +4,9 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import gettext as _ from zerver.decorator import webhook_view +from zerver.lib.exceptions import JsonableError from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message from zerver.models import UserProfile @@ -35,10 +36,10 @@ def api_zapier_webhook( if topic is None: topic = payload.get("subject") # Backwards-compatibility if topic is None: - return json_error(_("Topic can't be empty")) + raise JsonableError(_("Topic can't be empty")) if content is None: - return json_error(_("Content can't be empty")) + raise JsonableError(_("Content can't be empty")) check_send_webhook_message(request, user_profile, topic, content) return json_success() diff --git a/zilencer/views.py b/zilencer/views.py index d9e7272f31..ea1ef1b84b 100644 --- a/zilencer/views.py +++ b/zilencer/views.py @@ -19,7 +19,7 @@ from zerver.lib.push_notifications import ( send_apple_push_notification, ) from zerver.lib.request import REQ, has_request_variables -from zerver.lib.response import json_error, json_success +from zerver.lib.response import json_success from zerver.lib.validator import ( check_bool, check_capped_string, @@ -147,7 +147,7 @@ def unregister_remote_push_device( token=token, kind=token_kind, user_id=user_id, server=server ).delete() if deleted[0] == 0: - return json_error(err_("Token does not exist")) + raise JsonableError(err_("Token does not exist")) return json_success()