Rename/reorganize our urls to be more consistent.

(imported from commit ca3cc7ccd5d7da83a9c60968527378ee1118648e)
This commit is contained in:
Tim Abbott
2012-10-16 15:42:40 -04:00
parent fab64fd7b0
commit 3e994c16b7
8 changed files with 64 additions and 59 deletions

View File

@@ -8,27 +8,32 @@ import os.path
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^$', 'zephyr.views.home', name='home'), url(r'^$', 'zephyr.views.home', name='home'),
url(r'^update$', 'zephyr.views.update', name='update'),
url(r'^get_updates$', 'zephyr.views.get_updates', name='get_updates'),
url(r'^api/v1/get_messages$', 'zephyr.views.api_get_messages', name='api_get_messages'),
url(r'^api/v1/get_public_streams$', 'zephyr.views.api_get_public_streams', name='api_get_public_streams'),
url(r'^api/v1/get_subscriptions$', 'zephyr.views.api_get_subscriptions', name='api_get_subscriptions'),
url(r'^api/v1/subscribe$', 'zephyr.views.api_subscribe', name='api_subscribe'),
url(r'^api/v1/send_message$', 'zephyr.views.api_send_message', name='api_send_message'),
url(r'^send_message/', 'zephyr.views.send_message', name='send_message'),
# We have two entries for accounts/login to allow reverses on the Django # We have two entries for accounts/login to allow reverses on the Django
# view we're wrapping to continue to function. # view we're wrapping to continue to function.
url(r'^accounts/login/', 'zephyr.views.login_page', {'template_name': 'zephyr/login.html'}), url(r'^accounts/login/', 'zephyr.views.login_page', {'template_name': 'zephyr/login.html'}),
url(r'^accounts/login/', 'django.contrib.auth.views.login', {'template_name': 'zephyr/login.html'}), url(r'^accounts/login/', 'django.contrib.auth.views.login', {'template_name': 'zephyr/login.html'}),
url(r'^accounts/logout/', 'django.contrib.auth.views.logout', {'template_name': 'zephyr/index.html'}), url(r'^accounts/logout/', 'django.contrib.auth.views.logout', {'template_name': 'zephyr/index.html'}),
url(r'^settings/change/$', 'zephyr.views.change_settings', name='change_settings'),
url(r'^json/subscriptions/list$', 'zephyr.views.json_list_subscriptions', name='list_subscriptions'), # These are json format views used by the web client. They require a logged in browser.
url(r'^json/subscriptions/remove$', 'zephyr.views.json_remove_subscription', name='remove_subscription'), url(r'^json/update_pointer$', 'zephyr.views.json_update_pointer', name='json_update_pointer'),
url(r'^json/get_updates$', 'zephyr.views.json_get_updates', name='json_get_updates'),
url(r'^json/send_message/', 'zephyr.views.json_send_message', name='json_send_message'),
url(r'^json/settings/change/$', 'zephyr.views.json_change_settings', name='json_change_settings'),
url(r'^json/subscriptions/list$', 'zephyr.views.json_list_subscriptions', name='json_list_subscriptions'),
url(r'^json/subscriptions/remove$', 'zephyr.views.json_remove_subscription', name='json_remove_subscription'),
url(r'^json/subscriptions/add$', 'zephyr.views.json_add_subscription', name='json_add_subscription'),
url(r'^json/subscriptions/exists/(?P<stream>.*)$', 'zephyr.views.json_stream_exists', name='json_stream_exists'),
# These are json format views used by the API. They require an API key.
url(r'^api/v1/get_messages$', 'zephyr.views.api_get_messages', name='api_get_messages'),
url(r'^api/v1/get_public_streams$', 'zephyr.views.api_get_public_streams', name='api_get_public_streams'),
url(r'^api/v1/get_subscriptions$', 'zephyr.views.api_get_subscriptions', name='api_get_subscriptions'),
url(r'^api/v1/subscribe$', 'zephyr.views.api_subscribe', name='api_subscribe'),
url(r'^api/v1/send_message$', 'zephyr.views.api_send_message', name='api_send_message'),
url(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/static/favicon.ico'}), url(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/static/favicon.ico'}),
url(r'^json/subscriptions/add$', 'zephyr.views.json_add_subscription', name='add_subscription'),
url(r'^static/(?P<path>.*)$', 'django.views.static.serve', url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': os.path.join(settings.SITE_ROOT, '..', 'zephyr', 'static/')}), {'document_root': os.path.join(settings.SITE_ROOT, '..', 'zephyr', 'static/')}),
url(r'^subscriptions/exists/(?P<stream>.*)$', 'zephyr.views.stream_exists', name='stream_exists'),
# Uncomment the admin/doc line below to enable admin documentation: # Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')), # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
@@ -40,7 +45,7 @@ urlpatterns = patterns('',
if settings.ALLOW_REGISTER: if settings.ALLOW_REGISTER:
urlpatterns += patterns('', urlpatterns += patterns('',
url(r'^accounts/home/', 'zephyr.views.accounts_home', name='accounts_home'), url(r'^accounts/home/', 'zephyr.views.accounts_home', name='accounts_home'),
url(r'^accounts/register/', 'zephyr.views.register', name='register'), url(r'^accounts/register/', 'zephyr.views.accounts_register', name='accounts_register'),
url(r'^accounts/send_confirm/(?P<email>[\S]+)?', 'django.views.generic.simple.direct_to_template', {'template': 'zephyr/accounts_send_confirm.html'}, name='send_confirm'), url(r'^accounts/send_confirm/(?P<email>[\S]+)?', 'django.views.generic.simple.direct_to_template', {'template': 'zephyr/accounts_send_confirm.html'}, name='send_confirm'),
url(r'^accounts/do_confirm/(?P<confirmation_key>[\w]+)', 'confirmation.views.confirm', name='confirm'), url(r'^accounts/do_confirm/(?P<confirmation_key>[\w]+)', 'confirmation.views.confirm', name='confirm'),
) )

View File

@@ -53,7 +53,7 @@
<div class="close composebox-close" onclick="hide_compose()">&times;</div> <div class="close composebox-close" onclick="hide_compose()">&times;</div>
<div class="message_comp"> <div class="message_comp">
<div class="alert" id="send-status"></div> <div class="alert" id="send-status"></div>
<form action="/send_message/" method="post" class="zephyr"> <form action="/json/send_message/" method="post" class="zephyr">
<input type="hidden" name="type" value="stream" id="new_message_type"/> <input type="hidden" name="type" value="stream" id="new_message_type"/>
{% csrf_token %} {% csrf_token %}
<table class="compose_table"> <table class="compose_table">

View File

@@ -6,7 +6,7 @@
<div id="current_settings" class="span9"> <div id="current_settings" class="span9">
<h1>Settings</h1> <h1>Settings</h1>
<form action="/settings/change/" method="post" class="settings">{% csrf_token %} <form action="/json/settings/change/" method="post" class="settings">{% csrf_token %}
<label>Email</label> <label>Email</label>
<b>{{ user_profile.user.email }}</b> <a href="/settings/change-email">(change?)</a><br /><br /> <b>{{ user_profile.user.email }}</b> <a href="/settings/change-email">(change?)</a><br /><br />
<div id="Photo"> <div id="Photo">

View File

@@ -89,7 +89,7 @@ class Command(BaseCommand):
try: try:
# Application is an instance of Django's standard wsgi handler. # Application is an instance of Django's standard wsgi handler.
application = web.Application([(r"/get_updates", AsyncDjangoHandler), application = web.Application([(r"/json/get_updates", AsyncDjangoHandler),
(r"/api/v1/get_messages", AsyncDjangoHandler), (r"/api/v1/get_messages", AsyncDjangoHandler),
(r".*", FallbackHandler, dict(fallback=django_app)), (r".*", FallbackHandler, dict(fallback=django_app)),
], debug=django.conf.settings.DEBUG) ], debug=django.conf.settings.DEBUG)

View File

@@ -100,7 +100,7 @@ function submit_buttons() {
function check_stream_for_send(stream_name) { function check_stream_for_send(stream_name) {
var okay = true; var okay = true;
$.ajax({ $.ajax({
url: "subscriptions/exists/" + stream_name, url: "/json/subscriptions/exists/" + stream_name,
async: false, async: false,
success: function (data) { success: function (data) {
if (data === "False") { if (data === "False") {

View File

@@ -210,7 +210,7 @@ function update_selected_message(message) {
// doesn't permanently affect where you are. // doesn't permanently affect where you are.
// //
// We also don't want to post if there's no effective change. // We also don't want to post if there's no effective change.
$.post("update", {pointer: new_selected_id}); $.post("/json/update_pointer", {pointer: new_selected_id});
} }
selected_message_id = new_selected_id; selected_message_id = new_selected_id;
selected_message = message; selected_message = message;
@@ -585,7 +585,7 @@ var get_updates_timeout;
function get_updates() { function get_updates() {
get_updates_xhr = $.ajax({ get_updates_xhr = $.ajax({
type: 'POST', type: 'POST',
url: 'get_updates', url: '/json/get_updates',
data: received, data: received,
dataType: 'json', dataType: 'json',
timeout: 10*60*1000, // 10 minutes in ms timeout: 10*60*1000, // 10 minutes in ms

View File

@@ -5,7 +5,7 @@ from django.db.models import Q
from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, \ from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, \
filter_by_subscriptions, Realm, do_send_message filter_by_subscriptions, Realm, do_send_message
from zephyr.views import get_updates from zephyr.views import json_get_updates
from zephyr.decorator import TornadoAsyncException from zephyr.decorator import TornadoAsyncException
import datetime import datetime
@@ -109,7 +109,7 @@ class PublicURLTest(TestCase):
Pages that should return a 200 when not logged in. Pages that should return a 200 when not logged in.
""" """
urls = {200: ["/accounts/home/", "/accounts/login/", "/accounts/logout/"], urls = {200: ["/accounts/home/", "/accounts/login/", "/accounts/logout/"],
302: ["/", "/send_message/", "/json/subscriptions/list", 302: ["/", "/json/send_message/", "/json/subscriptions/list",
"/json/subscriptions/remove", "/json/subscriptions/add"], "/json/subscriptions/remove", "/json/subscriptions/add"],
400: ["/accounts/register/"], 400: ["/accounts/register/"],
} }
@@ -270,40 +270,40 @@ class PointerTest(AuthedTestCase):
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
result = self.client.post("/update", {"pointer": 1}) result = self.client.post("/json/update_pointer", {"pointer": 1})
self.assert_json_success(result) self.assert_json_success(result)
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, 1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, 1)
def test_missing_pointer(self): def test_missing_pointer(self):
""" """
Posting json to /update which does not contain a pointer key/value pair Posting json to /json/update_pointer which does not contain a pointer key/value pair
returns a 400 and error message. returns a 400 and error message.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
result = self.client.post("/update", {"foo": 1}) result = self.client.post("/json/update_pointer", {"foo": 1})
self.assert_json_error(result, "Missing pointer") self.assert_json_error(result, "Missing pointer")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
def test_invalid_pointer(self): def test_invalid_pointer(self):
""" """
Posting json to /update with an invalid pointer returns a 400 and error Posting json to /json/update_pointer with an invalid pointer returns a 400 and error
message. message.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
result = self.client.post("/update", {"pointer": "foo"}) result = self.client.post("/json/update_pointer", {"pointer": "foo"})
self.assert_json_error(result, "Invalid pointer: must be an integer") self.assert_json_error(result, "Invalid pointer: must be an integer")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
def test_pointer_out_of_range(self): def test_pointer_out_of_range(self):
""" """
Posting json to /update with an out of range (< 0) pointer returns a 400 Posting json to /json/update_pointer with an out of range (< 0) pointer returns a 400
and error message. and error message.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
result = self.client.post("/update", {"pointer": -2}) result = self.client.post("/json/update_pointer", {"pointer": -2})
self.assert_json_error(result, "Invalid pointer value") self.assert_json_error(result, "Invalid pointer value")
self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1) self.assertEquals(self.get_userprofile("hamlet@humbughq.com").pointer, -1)
@@ -316,10 +316,10 @@ class MessagePOSTTest(AuthedTestCase):
successful. successful.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
result = self.client.post("/send_message/", {"type": "stream", result = self.client.post("/json/send_message/", {"type": "stream",
"stream": "Verona", "stream": "Verona",
"content": "Test message", "content": "Test message",
"subject": "Test subject"}) "subject": "Test subject"})
self.assert_json_success(result) self.assert_json_success(result)
def test_message_to_nonexistent_stream(self): def test_message_to_nonexistent_stream(self):
@@ -329,10 +329,10 @@ class MessagePOSTTest(AuthedTestCase):
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
self.assertFalse(Stream.objects.filter(name="nonexistent_stream")) self.assertFalse(Stream.objects.filter(name="nonexistent_stream"))
result = self.client.post("/send_message/", {"type": "stream", result = self.client.post("/json/send_message/", {"type": "stream",
"stream": "nonexistent_stream", "stream": "nonexistent_stream",
"content": "Test message", "content": "Test message",
"subject": "Test subject"}) "subject": "Test subject"})
self.assert_json_success(result) self.assert_json_success(result)
self.assertTrue(Stream.objects.filter(name="nonexistent_stream")) self.assertTrue(Stream.objects.filter(name="nonexistent_stream"))
@@ -341,9 +341,9 @@ class MessagePOSTTest(AuthedTestCase):
Sending a personal message to a valid username is successful. Sending a personal message to a valid username is successful.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
result = self.client.post("/send_message/", {"type": "personal", result = self.client.post("/json/send_message/", {"type": "personal",
"content": "Test message", "content": "Test message",
"recipient": "othello@humbughq.com"}) "recipient": "othello@humbughq.com"})
self.assert_json_success(result) self.assert_json_success(result)
def test_personal_message_to_nonexistent_user(self): def test_personal_message_to_nonexistent_user(self):
@@ -351,9 +351,9 @@ class MessagePOSTTest(AuthedTestCase):
Sending a personal message to an invalid email returns error JSON. Sending a personal message to an invalid email returns error JSON.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
result = self.client.post("/send_message/", {"type": "personal", result = self.client.post("/json/send_message/", {"type": "personal",
"content": "Test message", "content": "Test message",
"recipient": "nonexistent"}) "recipient": "nonexistent"})
self.assert_json_error(result, "Invalid email 'nonexistent'") self.assert_json_error(result, "Invalid email 'nonexistent'")
def test_invalid_type(self): def test_invalid_type(self):
@@ -361,9 +361,9 @@ class MessagePOSTTest(AuthedTestCase):
Sending a message of unknown type returns error JSON. Sending a message of unknown type returns error JSON.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
result = self.client.post("/send_message/", {"type": "invalid type", result = self.client.post("/json/send_message/", {"type": "invalid type",
"content": "Test message", "content": "Test message",
"recipient": "othello@humbughq.com"}) "recipient": "othello@humbughq.com"})
self.assert_json_error(result, "Invalid message type") self.assert_json_error(result, "Invalid message type")
class DummyHandler(object): class DummyHandler(object):
@@ -387,9 +387,9 @@ class POSTRequestMock(object):
class GetUpdatesTest(AuthedTestCase): class GetUpdatesTest(AuthedTestCase):
fixtures = ['messages.json'] fixtures = ['messages.json']
def test_get_updates(self): def test_json_get_updates(self):
""" """
get_updates returns messages with IDs greater than the json_get_updates returns messages with IDs greater than the
last_received ID. last_received ID.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
@@ -402,10 +402,10 @@ class GetUpdatesTest(AuthedTestCase):
self.assertTrue(message.id > 1) self.assertTrue(message.id > 1)
request = POSTRequestMock({"last": str(1), "first": str(1)}, user, callback) request = POSTRequestMock({"last": str(1), "first": str(1)}, user, callback)
# get_updates returns None, which raises an exception in the # json_get_updates returns None, which raises an exception in the
# @asynchronous decorator, which raises a TornadoAsyncException. So this # @asynchronous decorator, which raises a TornadoAsyncException. So this
# is expected, but should probably change. # is expected, but should probably change.
self.assertRaises(TornadoAsyncException, get_updates, request) self.assertRaises(TornadoAsyncException, json_get_updates, request)
def test_beyond_last_message(self): def test_beyond_last_message(self):
""" """
@@ -426,12 +426,12 @@ class GetUpdatesTest(AuthedTestCase):
messages.extend(data) messages.extend(data)
request = POSTRequestMock({"last": str(last_received), "first": "1"}, user, callback) request = POSTRequestMock({"last": str(last_received), "first": "1"}, user, callback)
self.assertRaises(TornadoAsyncException, get_updates, request) self.assertRaises(TornadoAsyncException, json_get_updates, request)
self.assertEquals(len(messages), 0) self.assertEquals(len(messages), 0)
def test_missing_last_received(self): def test_missing_last_received(self):
""" """
Calling get_updates without a last_received key/value pair Calling json_get_updates without a last_received key/value pair
returns a 400 and error message. returns a 400 and error message.
""" """
self.login("hamlet@humbughq.com", "hamlet") self.login("hamlet@humbughq.com", "hamlet")
@@ -444,4 +444,4 @@ class GetUpdatesTest(AuthedTestCase):
self.assertTrue(message.id > 1) self.assertTrue(message.id > 1)
request = POSTRequestMock({}, user, callback) request = POSTRequestMock({}, user, callback)
self.assert_json_error(get_updates(request), "Missing message range") self.assert_json_error(json_get_updates(request), "Missing message range")

View File

@@ -78,7 +78,7 @@ def get_stream(stream_name, realm):
return None return None
@require_post @require_post
def register(request): def accounts_register(request):
key = request.POST['key'] key = request.POST['key']
email = Confirmation.objects.get(confirmation_key=key).content_object.email email = Confirmation.objects.get(confirmation_key=key).content_object.email
company_name = email.split('@')[-1] company_name = email.split('@')[-1]
@@ -183,7 +183,7 @@ def home(request):
@login_required @login_required
@require_post @require_post
def update(request): def json_update_pointer(request):
user_profile = UserProfile.objects.get(user=request.user) user_profile = UserProfile.objects.get(user=request.user)
pointer = request.POST.get('pointer') pointer = request.POST.get('pointer')
if not pointer: if not pointer:
@@ -282,7 +282,7 @@ def get_updates_backend(request, user_profile, handler, **kwargs):
@login_required @login_required
@asynchronous @asynchronous
@require_post @require_post
def get_updates(request, handler): def json_get_updates(request, handler):
if not ('last' in request.POST and 'first' in request.POST): if not ('last' in request.POST and 'first' in request.POST):
return json_error("Missing message range") return json_error("Missing message range")
user_profile = UserProfile.objects.get(user=request.user) user_profile = UserProfile.objects.get(user=request.user)
@@ -309,7 +309,7 @@ def api_send_message(request, user_profile):
@login_required @login_required
@require_post @require_post
def send_message(request): def json_send_message(request):
user_profile = UserProfile.objects.get(user=request.user) user_profile = UserProfile.objects.get(user=request.user)
if 'time' in request.POST: if 'time' in request.POST:
return json_error("Invalid field 'time'") return json_error("Invalid field 'time'")
@@ -549,7 +549,7 @@ def add_subscriptions_backend(request, user_profile, streams):
@login_required @login_required
@require_post @require_post
def change_settings(request): def json_change_settings(request):
user_profile = UserProfile.objects.get(user=request.user) user_profile = UserProfile.objects.get(user=request.user)
# First validate all the inputs # First validate all the inputs
@@ -593,7 +593,7 @@ def change_settings(request):
return json_success(result) return json_success(result)
@login_required @login_required
def stream_exists(request, stream): def json_stream_exists(request, stream):
if not valid_stream_name(stream): if not valid_stream_name(stream):
return json_error("Invalid characters in stream name") return json_error("Invalid characters in stream name")
return HttpResponse( return HttpResponse(