Remove rest_dispatch hack and optimize imports.

For a long time, rest_dispatch has had this hack where we have to
create a copy of it in each views file using it, in order to directly
access the globals list in that file.  This removes that hack, instead
making rest_dispatch just use Django's import_string to access the
target method to use.

[tweaked and reorganized from acrefoot's original branch in various
ways by tabbott]
This commit is contained in:
acrefoot
2016-06-23 17:26:09 -07:00
committed by Tim Abbott
parent aebd84cb1b
commit e4ed9195dc
16 changed files with 106 additions and 159 deletions

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import
from typing import Any, Dict from typing import Any, Dict
from django.utils.module_loading import import_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views.decorators.csrf import csrf_exempt, csrf_protect
@@ -14,8 +15,8 @@ from django.conf import settings
METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH') METHODS = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH')
@csrf_exempt @csrf_exempt
def rest_dispatch(request, globals_list, **kwargs): def rest_dispatch(request, **kwargs):
# type: (HttpRequest, Dict[str, Any], **Any) -> HttpResponse # type: (HttpRequest, **Any) -> HttpResponse
"""Dispatch to a REST API endpoint. """Dispatch to a REST API endpoint.
Unauthenticated endpoints should not use this, as authentication is verified Unauthenticated endpoints should not use this, as authentication is verified
@@ -35,9 +36,8 @@ def rest_dispatch(request, globals_list, **kwargs):
Any keyword args that are *not* HTTP methods are passed through to the Any keyword args that are *not* HTTP methods are passed through to the
target function. target function.
Note that we search views.py globals for the function to call, so never Never make a urls.py pattern put user input into a variable called GET, POST,
make a urls.py pattern put user input into a variable called GET, POST, etc, as that is where we route HTTP verbs to target functions.
etc.
""" """
supported_methods = {} # type: Dict[str, Any] supported_methods = {} # type: Dict[str, Any]
# duplicate kwargs so we can mutate the original as we go # duplicate kwargs so we can mutate the original as we go
@@ -60,7 +60,7 @@ def rest_dispatch(request, globals_list, **kwargs):
method_to_use = request.META["zulip.emulated_method"] method_to_use = request.META["zulip.emulated_method"]
if method_to_use in supported_methods: if method_to_use in supported_methods:
target_function = globals_list[supported_methods[method_to_use]] target_function = import_string(supported_methods[method_to_use])
# Set request._query for update_activity_user(), which is called # Set request._query for update_activity_user(), which is called
# by some of the later wrappers. # by some of the later wrappers.

View File

@@ -20,8 +20,8 @@ class URLResolutionTest(TestCase):
if not (hasattr(pattern, "_callback_str") and hasattr(pattern, "default_args")): if not (hasattr(pattern, "_callback_str") and hasattr(pattern, "default_args")):
continue continue
for view in pattern.default_args.values(): for func_string in pattern.default_args.values():
module_name = pattern._callback_str.replace(".rest_dispatch", "") module_name, view = func_string.rsplit('.', 1)
self.check_function_exists(module_name, view) self.check_function_exists(module_name, view)
# Tests function-based views declared in urls.urlpatterns for # Tests function-based views declared in urls.urlpatterns for

View File

@@ -1,7 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from six import text_type from six import text_type
@@ -21,9 +20,6 @@ from typing import Union, Optional, Iterable, Sequence, List, Any
import time import time
import ujson import ujson
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
@internal_notify_view @internal_notify_view
def notify(request): def notify(request):
# type: (HttpRequest) -> HttpResponse # type: (HttpRequest) -> HttpResponse

View File

@@ -70,8 +70,6 @@ import hashlib
import hmac import hmac
from zproject.jinja2 import render_to_response from zproject.jinja2 import render_to_response
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
def name_changes_disabled(realm): def name_changes_disabled(realm):
# type: (Optional[Realm]) -> bool # type: (Optional[Realm]) -> bool

View File

@@ -1,21 +1,17 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, HttpRequest from django.http import HttpResponse, HttpRequest
from typing import List from typing import List
from zerver.models import UserProfile from zerver.models import UserProfile
from zerver.decorator import authenticated_json_post_view, has_request_variables, REQ from zerver.decorator import has_request_variables, REQ
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.validator import check_list, check_string from zerver.lib.validator import check_list, check_string
from zerver.lib.actions import do_add_alert_words, do_remove_alert_words, do_set_alert_words from zerver.lib.actions import do_add_alert_words, do_remove_alert_words, do_set_alert_words
from zerver.lib.alert_words import user_alert_words from zerver.lib.alert_words import user_alert_words
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
from six import text_type from six import text_type
def list_alert_words(request, user_profile): def list_alert_words(request, user_profile):

View File

@@ -14,7 +14,6 @@ from zerver.decorator import authenticated_api_view, authenticated_json_post_vie
has_request_variables, REQ, JsonableError, \ has_request_variables, REQ, JsonableError, \
to_non_negative_int, to_non_negative_float to_non_negative_int, to_non_negative_float
from django.utils.html import escape as escape_html from django.utils.html import escape as escape_html
from django.views.decorators.csrf import csrf_exempt
from zerver.lib import bugdown from zerver.lib import bugdown
from zerver.lib.actions import recipient_for_emails, do_update_message_flags, \ from zerver.lib.actions import recipient_for_emails, do_update_message_flags, \
compute_mit_user_fullname, compute_irc_user_fullname, compute_jabber_user_fullname, \ compute_mit_user_fullname, compute_irc_user_fullname, compute_jabber_user_fullname, \
@@ -42,10 +41,9 @@ from sqlalchemy.sql import select, join, column, literal_column, literal, and_,
import re import re
import ujson import ujson
from zerver.lib.rest import rest_dispatch as _rest_dispatch
from six.moves import map from six.moves import map
import six import six
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
# This is a Pool that doesn't close connections. Therefore it can be used with # This is a Pool that doesn't close connections. Therefore it can be used with
# existing Django database connections. # existing Django database connections.

View File

@@ -1,16 +1,12 @@
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.views.decorators.csrf import csrf_exempt
from zerver.models import UserProfile from zerver.models import UserProfile
from zerver.lib.response import json_success, json_error from zerver.lib.response import json_success, json_error
from zerver.lib.actions import check_add_realm_emoji, do_remove_realm_emoji from zerver.lib.actions import check_add_realm_emoji, do_remove_realm_emoji
from zerver.lib.rest import rest_dispatch as _rest_dispatch
from six import text_type from six import text_type
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
def list_emoji(request, user_profile): def list_emoji(request, user_profile):
# type: (HttpRequest, UserProfile) -> HttpResponse # type: (HttpRequest, UserProfile) -> HttpResponse

View File

@@ -3,12 +3,10 @@ from typing import Any
from django.conf import settings from django.conf import settings
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from zerver.decorator import authenticated_json_post_view, has_request_variables, REQ, \ from zerver.decorator import authenticated_json_post_view, has_request_variables, REQ, \
to_non_negative_int to_non_negative_int
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.rest import rest_dispatch as _rest_dispatch
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
from zerver.lib.unminify import SourceMap from zerver.lib.unminify import SourceMap
from zerver.lib.utils import statsd, statsd_key from zerver.lib.utils import statsd, statsd_key
@@ -20,8 +18,6 @@ from six import text_type
import subprocess import subprocess
import os import os
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
# Read the source map information for decoding JavaScript backtraces # Read the source map information for decoding JavaScript backtraces
js_source_map = None js_source_map = None
if not (settings.DEBUG or settings.TEST_SUITE): if not (settings.DEBUG or settings.TEST_SUITE):

View File

@@ -5,7 +5,6 @@ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from zerver.lib.request import JsonableError, REQ, has_request_variables from zerver.lib.request import JsonableError, REQ, has_request_variables
from zerver.decorator import authenticated_json_post_view, \ from zerver.decorator import authenticated_json_post_view, \
@@ -29,12 +28,9 @@ from collections import defaultdict
import ujson import ujson
from six.moves import urllib from six.moves import urllib
from zerver.lib.rest import rest_dispatch as _rest_dispatch
import six import six
from six import text_type from six import text_type
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
def list_to_streams(streams_raw, user_profile, autocreate=False, invite_only=False): def list_to_streams(streams_raw, user_profile, autocreate=False, invite_only=False):
# type: (Iterable[text_type], UserProfile, Optional[bool], Optional[bool]) -> Tuple[List[Stream], List[Stream]] # type: (Iterable[text_type], UserProfile, Optional[bool], Optional[bool]) -> Tuple[List[Stream], List[Stream]]
"""Converts plaintext stream names to a list of Streams, validating input in the process """Converts plaintext stream names to a list of Streams, validating input in the process

View File

@@ -1,7 +1,6 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from zerver.decorator import authenticated_json_post_view, has_request_variables, REQ from zerver.decorator import authenticated_json_post_view, has_request_variables, REQ
@@ -10,9 +9,6 @@ from zerver.lib.response import json_error, json_success
from zerver.lib.validator import check_string from zerver.lib.validator import check_string
from zerver.models import UserProfile from zerver.models import UserProfile
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
@authenticated_json_post_view @authenticated_json_post_view
@has_request_variables @has_request_variables
def json_tutorial_send_message(request, user_profile, type=REQ(validator=check_string), def json_tutorial_send_message(request, user_profile, type=REQ(validator=check_string),

View File

@@ -4,7 +4,6 @@ from six import text_type
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@@ -23,9 +22,6 @@ from zerver.lib.upload import upload_avatar_image
from zerver.lib.validator import check_bool from zerver.lib.validator import check_bool
from zerver.models import UserProfile, Realm from zerver.models import UserProfile, Realm
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
def name_changes_disabled(realm): def name_changes_disabled(realm):
# type: (Realm) -> bool # type: (Realm) -> bool
return settings.NAME_CHANGES_DISABLED or realm.name_changes_disabled return settings.NAME_CHANGES_DISABLED or realm.name_changes_disabled

View File

@@ -4,7 +4,6 @@ from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_exempt
from six.moves import map from six.moves import map
from zerver.decorator import has_request_variables, REQ, JsonableError, \ from zerver.decorator import has_request_variables, REQ, JsonableError, \
@@ -21,9 +20,6 @@ from zerver.lib.validator import check_bool
from zerver.models import UserProfile, Stream, Realm, get_user_profile_by_email, \ from zerver.models import UserProfile, Stream, Realm, get_user_profile_by_email, \
get_stream, email_allowed_for_realm get_stream, email_allowed_for_realm
from zerver.lib.rest import rest_dispatch as _rest_dispatch
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
from six import text_type from six import text_type
from typing import Optional, Dict, Any from typing import Optional, Dict, Any

View File

@@ -11,16 +11,15 @@ i18n_urlpatterns = [
] ]
# Zilencer views following the REST API style # Zilencer views following the REST API style
v1_api_and_json_patterns = patterns('zilencer.views', v1_api_and_json_patterns = [
url('^feedback$', 'rest_dispatch', url('^feedback$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'submit_feedback'}), {'POST': 'zilencer.views.submit_feedback'}),
url('^report_error$', 'rest_dispatch', url('^report_error$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'report_error'}), {'POST': 'zilencer.views.report_error'}),
url('^endpoints$', 'lookup_endpoints_for_user'), url('^endpoints$', 'zilencer.views.lookup_endpoints_for_user'),
) ]
urlpatterns = patterns('', urlpatterns = [
url(r'^api/v1/', include(v1_api_and_json_patterns)), url(r'^api/v1/', include(v1_api_and_json_patterns)),
url(r'^json/', include(v1_api_and_json_patterns)), url(r'^json/', include(v1_api_and_json_patterns)),
*i18n_urlpatterns ] + i18n_urlpatterns
)

View File

@@ -11,19 +11,16 @@ from zilencer.models import Deployment
from zerver.decorator import has_request_variables, REQ from zerver.decorator import has_request_variables, REQ
from zerver.lib.actions import internal_send_message from zerver.lib.actions import internal_send_message
from zerver.lib.redis_utils import get_redis_client from zerver.lib.redis_utils import get_redis_client
from zerver.lib.response import json_success, json_error, json_response, json_method_not_allowed from zerver.lib.response import json_success, json_error, json_response
from zerver.lib.rest import rest_dispatch as _rest_dispatch
from zerver.lib.validator import check_dict from zerver.lib.validator import check_dict
from zerver.models import get_realm, get_user_profile_by_email, resolve_email_to_domain, \ from zerver.models import get_realm, get_user_profile_by_email, resolve_email_to_domain, \
UserProfile, Realm UserProfile, Realm
from .error_notify import notify_server_error, notify_browser_error from .error_notify import notify_server_error, notify_browser_error
from django.conf import settings
import time import time
from typing import Dict, Optional, Any from typing import Dict, Optional, Any
rest_dispatch = csrf_exempt((lambda request, *args, **kwargs: _rest_dispatch(request, globals(), *args, **kwargs)))
client = get_redis_client() client = get_redis_client()
def has_enough_time_expired_since_last_message(sender_email, min_delay): def has_enough_time_expired_since_last_message(sender_email, min_delay):

View File

@@ -1,10 +1,4 @@
from django.conf import settings from django.conf.urls import url
from django.conf.urls import patterns, url, include
from django.conf.urls.i18n import i18n_patterns
from django.views.generic import TemplateView, RedirectView
from django.utils.module_loading import import_string
import os.path
import zerver.forms
# Future endpoints should add to urls.py, which includes these legacy urls # Future endpoints should add to urls.py, which includes these legacy urls

View File

@@ -116,118 +116,111 @@ urls = list(i18n_urls)
# See rest_dispatch in zerver.lib.rest for an explanation of auth methods used # See rest_dispatch in zerver.lib.rest for an explanation of auth methods used
# #
# All of these paths are accessed by either a /json or /api prefix # All of these paths are accessed by either a /json or /api prefix
v1_api_and_json_patterns = patterns('zerver.views', v1_api_and_json_patterns = [
# realm-level calls # realm-level calls
url(r'^export$', 'rest_dispatch', url(r'^export$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'export'}), {'GET': 'zerver.views.export'}),
url(r'^realm$', 'rest_dispatch', url(r'^realm$', 'zerver.lib.rest.rest_dispatch',
{'PATCH': 'update_realm'}), {'PATCH': 'zerver.views.update_realm'}),
# Returns a 204, used by desktop app to verify connectivity status # Returns a 204, used by desktop app to verify connectivity status
url(r'generate_204$', 'generate_204'), url(r'generate_204$', 'zerver.views.generate_204'),
) + patterns('zerver.views.realm_emoji',
# realm/emoji -> zerver.views.realm_emoji # realm/emoji -> zerver.views.realm_emoji
url(r'^realm/emoji$', 'rest_dispatch', url(r'^realm/emoji$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'list_emoji', {'GET': 'zerver.views.realm_emoji.list_emoji',
'PUT': 'upload_emoji'}), 'PUT': 'zerver.views.realm_emoji.upload_emoji'}),
url(r'^realm/emoji/(?P<emoji_name>[0-9a-zA-Z.\-_]+(?<![.\-_]))$', 'rest_dispatch', url(r'^realm/emoji/(?P<emoji_name>[0-9a-zA-Z.\-_]+(?<![.\-_]))$', 'zerver.lib.rest.rest_dispatch',
{'DELETE': 'delete_emoji'}), {'DELETE': 'zerver.views.realm_emoji.delete_emoji'}),
) + patterns('zerver.views.users',
# users -> zerver.views.users # users -> zerver.views.users
url(r'^users$', 'rest_dispatch', url(r'^users$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_members_backend', {'GET': 'zerver.views.users.get_members_backend',
'POST': 'create_user_backend'}), 'POST': 'zerver.views.users.create_user_backend'}),
url(r'^users/(?P<email>(?!me)[^/]*)/reactivate$', 'rest_dispatch', url(r'^users/(?P<email>(?!me)[^/]*)/reactivate$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'reactivate_user_backend'}), {'POST': 'zerver.views.users.reactivate_user_backend'}),
url(r'^users/(?P<email>(?!me)[^/]*)$', 'rest_dispatch', url(r'^users/(?P<email>(?!me)[^/]*)$', 'zerver.lib.rest.rest_dispatch',
{'PATCH': 'update_user_backend', {'PATCH': 'zerver.views.users.update_user_backend',
'DELETE': 'deactivate_user_backend'}), 'DELETE': 'zerver.views.users.deactivate_user_backend'}),
url(r'^bots$', 'rest_dispatch', url(r'^bots$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_bots_backend', {'GET': 'zerver.views.users.get_bots_backend',
'POST': 'add_bot_backend'}), 'POST': 'zerver.views.users.add_bot_backend'}),
url(r'^bots/(?P<email>(?!me)[^/]*)/api_key/regenerate$', 'rest_dispatch', url(r'^bots/(?P<email>(?!me)[^/]*)/api_key/regenerate$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'regenerate_bot_api_key'}), {'POST': 'zerver.views.users.regenerate_bot_api_key'}),
url(r'^bots/(?P<email>(?!me)[^/]*)$', 'rest_dispatch', url(r'^bots/(?P<email>(?!me)[^/]*)$', 'zerver.lib.rest.rest_dispatch',
{'PATCH': 'patch_bot_backend', {'PATCH': 'zerver.views.users.patch_bot_backend',
'DELETE': 'deactivate_bot_backend'}), 'DELETE': 'zerver.views.users.deactivate_bot_backend'}),
) + patterns('zerver.views.messages',
# messages -> zerver.views.messages # messages -> zerver.views.messages
# GET returns messages, possibly filtered, POST sends a message # GET returns messages, possibly filtered, POST sends a message
url(r'^messages$', 'rest_dispatch', url(r'^messages$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_old_messages_backend', {'GET': 'zerver.views.messages.get_old_messages_backend',
'PATCH': 'update_message_backend', 'PATCH': 'zerver.views.messages.update_message_backend',
'POST': 'send_message_backend'}), 'POST': 'zerver.views.messages.send_message_backend'}),
url(r'^messages/render$', 'rest_dispatch', url(r'^messages/render$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'render_message_backend'}), {'GET': 'zerver.views.messages.render_message_backend'}),
url(r'^messages/flags$', 'rest_dispatch', url(r'^messages/flags$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'update_message_flags'}), {'POST': 'zerver.views.messages.update_message_flags'}),
) + patterns('zerver.views',
# users/me -> zerver.views # users/me -> zerver.views
url(r'^users/me$', 'rest_dispatch', url(r'^users/me$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_profile_backend'}), {'GET': 'zerver.views.get_profile_backend'}),
url(r'^users/me/pointer$', 'rest_dispatch', url(r'^users/me/pointer$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_pointer_backend', {'GET': 'zerver.views.get_pointer_backend',
'PUT': 'update_pointer_backend'}), 'PUT': 'zerver.views.update_pointer_backend'}),
url(r'^users/me/presence$', 'rest_dispatch', url(r'^users/me/presence$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'update_active_status_backend'}), {'POST': 'zerver.views.update_active_status_backend'}),
# Endpoint used by mobile devices to register their push # Endpoint used by mobile devices to register their push
# notification credentials # notification credentials
url(r'^users/me/apns_device_token$', 'rest_dispatch', url(r'^users/me/apns_device_token$', 'zerver.lib.rest.rest_dispatch',
{'POST' : 'add_apns_device_token', {'POST': 'zerver.views.add_apns_device_token',
'DELETE': 'remove_apns_device_token'}), 'DELETE': 'zerver.views.remove_apns_device_token'}),
url(r'^users/me/android_gcm_reg_id$', 'rest_dispatch', url(r'^users/me/android_gcm_reg_id$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'add_android_reg_id', {'POST': 'zerver.views.add_android_reg_id',
'DELETE': 'remove_android_reg_id'}), 'DELETE': 'zerver.views.remove_android_reg_id'}),
) + patterns('zerver.views.user_settings',
# users/me -> zerver.views.user_settings # users/me -> zerver.views.user_settings
url(r'^users/me/api_key/regenerate$', 'rest_dispatch', url(r'^users/me/api_key/regenerate$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'regenerate_api_key'}), {'POST': 'zerver.views.user_settings.regenerate_api_key'}),
url(r'^users/me/enter-sends$', 'rest_dispatch', url(r'^users/me/enter-sends$', 'zerver.lib.rest.rest_dispatch',
{'POST': 'change_enter_sends'}), {'POST': 'zerver.views.user_settings.change_enter_sends'}),
) + patterns('zerver.views.alert_words',
# users/me/alert_words -> zerver.views.alert_words # users/me/alert_words -> zerver.views.alert_words
url(r'^users/me/alert_words$', 'rest_dispatch', url(r'^users/me/alert_words$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'list_alert_words', {'GET': 'zerver.views.alert_words.list_alert_words',
'POST': 'set_alert_words', 'POST': 'zerver.views.alert_words.set_alert_words',
'PUT': 'add_alert_words', 'PUT': 'zerver.views.alert_words.add_alert_words',
'DELETE': 'remove_alert_words'}), 'DELETE': 'zerver.views.alert_words.remove_alert_words'}),
) + patterns('zerver.views.streams',
# streams -> zerver.views.streams # streams -> zerver.views.streams
url(r'^streams$', 'rest_dispatch', url(r'^streams$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_streams_backend'}), {'GET': 'zerver.views.streams.get_streams_backend'}),
# GET returns "stream info" (undefined currently?), HEAD returns whether stream exists (200 or 404) # GET returns "stream info" (undefined currently?), HEAD returns whether stream exists (200 or 404)
url(r'^streams/(?P<stream_name>.*)/members$', 'rest_dispatch', url(r'^streams/(?P<stream_name>.*)/members$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_subscribers_backend'}), {'GET': 'zerver.views.streams.get_subscribers_backend'}),
url(r'^streams/(?P<stream_name>.*)$', 'rest_dispatch', url(r'^streams/(?P<stream_name>.*)$', 'zerver.lib.rest.rest_dispatch',
{'HEAD': 'stream_exists_backend', {'HEAD': 'zerver.views.streams.stream_exists_backend',
'GET': 'stream_exists_backend', 'GET': 'zerver.views.streams.stream_exists_backend',
'PATCH': 'update_stream_backend', 'PATCH': 'zerver.views.streams.update_stream_backend',
'DELETE': 'deactivate_stream_backend'}), 'DELETE': 'zerver.views.streams.deactivate_stream_backend'}),
url(r'^default_streams$', 'rest_dispatch', url(r'^default_streams$', 'zerver.lib.rest.rest_dispatch',
{'PUT': 'add_default_stream', {'PUT': 'zerver.views.streams.add_default_stream',
'DELETE': 'remove_default_stream'}), 'DELETE': 'zerver.views.streams.remove_default_stream'}),
# GET lists your streams, POST bulk adds, PATCH bulk modifies/removes # GET lists your streams, POST bulk adds, PATCH bulk modifies/removes
url(r'^users/me/subscriptions$', 'rest_dispatch', url(r'^users/me/subscriptions$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'list_subscriptions_backend', {'GET': 'zerver.views.streams.list_subscriptions_backend',
'POST': 'add_subscriptions_backend', 'POST': 'zerver.views.streams.add_subscriptions_backend',
'PATCH': 'update_subscriptions_backend'}), 'PATCH': 'zerver.views.streams.update_subscriptions_backend'}),
) + patterns('zerver.views', # used to register for an event queue in tornado
# Used to register for an event queue in tornado url(r'^register$', 'zerver.lib.rest.rest_dispatch',
url(r'^register$', 'rest_dispatch', {'POST': 'zerver.views.api_events_register'}),
{'POST': 'api_events_register'}),
) + patterns('zerver.tornadoviews',
# events -> zerver.tornadoviews # events -> zerver.tornadoviews
url(r'^events$', 'rest_dispatch', url(r'^events$', 'zerver.lib.rest.rest_dispatch',
{'GET': 'get_events_backend', {'GET': 'zerver.tornadoviews.get_events_backend',
'DELETE': 'cleanup_event_queue'}), 'DELETE': 'zerver.tornadoviews.cleanup_event_queue'}),
) ]
# Include the dual-use patterns twice # Include the dual-use patterns twice
urls += [ urls += [