/comment/ -> /1.0/

This commit is contained in:
posativ 2012-10-23 21:09:51 +02:00
parent ac6d88f61e
commit 39899dda81
5 changed files with 52 additions and 52 deletions

View File

@ -38,33 +38,33 @@ please file in a bug report \*including\* your dump.
## API ## API
### fetch comments for /foo-bar/ To fetch all comments for a path, run
$ curl -H "Accept: application/json" http://example.org/comment/foo-bar/ $ curl -H "Accept: application/json" http://example.org/comment/foo-bar/
### comment at /foo-bar/ To write a comment, you have to POST a JSON dictionary with the following key-value
pairs. Text is mandatory otherwise you'll get a 400 Bad Request. You'll also get
a 400 when your JSON is invalid.
$ curl -H "Accept: application/json" -X POST -d \ Let's say you want to comment on /foo-bar/
$ curl http://example.org/comment/foo-bar/new -H "Accept: application/json" -X POST -d \
'{ '{
"text": "Lorem ipsum ...", "text": "Lorem ipsum ...",
# optional
"name": "Hans", "email": "foo@bla.org", "website": "http://blog/log/" "name": "Hans", "email": "foo@bla.org", "website": "http://blog/log/"
}' http://example.org/comment/foo-bar/new }'
### modify 12. comment at /foo-bar/ This will set a cookie, that expires in a few minutes (15 minutes per default). This
cookie allows you do modify or delete your comment. Don't try to modify that cookie,
it is cryptographically signed. If your cookie is outdated or modified, you'll get
a 403 Forbidden.
$ curl -H "Accept: application/json" -X PUT -d ... http://example.org/comment/foo-bar/12 For each comment you'll post, you get an unique cookie. Let's try to remove your comment:
You can only modify your own comment in a given time range (defaults to 15 minutes). $ curl -H ... -X DELETE http://example.org/comment/foo-bar/1
### delete 2nd comment at /foo-bar/ If your comment has been referenced by another comment, your comment will be cleared but
not deleted to retain depending comments.
$ curl -H ... -X DELETE http://example.org/comment/foo-bar/2
You can only delete your own comment in a given time range (defaults to 15 minutes). If
your comment has been referenced by another comment, your comment will be cleared but not
deleted to maintain depending comments.
## Alternatives ## Alternatives

View File

@ -55,10 +55,10 @@ url_map = Map([
# comment API, note that the client side quotes the URL, but this is # comment API, note that the client side quotes the URL, but this is
# actually unnecessary. PEP 333 aka WSGI always unquotes PATH_INFO. # actually unnecessary. PEP 333 aka WSGI always unquotes PATH_INFO.
url('/comment/<re(".+"):path>/', 'comment.get', ['GET']), url('/1.0/<re(".+"):path>/', 'comment.get', ['GET']),
url('/comment/<re(".+"):path>/new', 'comment.create', ['POST']), url('/1.0/<re(".+"):path>/new', 'comment.create', ['POST']),
url('/comment/<re(".+"):path>/<int:id>', 'comment.get', ['GET']), url('/1.0/<re(".+"):path>/<int:id>', 'comment.get', ['GET']),
url('/comment/<re(".+"):path>/<int:id>', 'comment.modify', ['PUT', 'DELETE']), url('/1.0/<re(".+"):path>/<int:id>', 'comment.modify', ['PUT', 'DELETE']),
], converters={'re': RegexConverter}) ], converters={'re': RegexConverter})

View File

@ -72,7 +72,7 @@ def modify(app, environ, request, path, id):
abort(403) abort(403)
if not (rv[0] == '*' or rv[0:2] == [path, id]): if not (rv[0] == '*' or rv[0:2] == [path, id]):
abort(401) abort(403)
if request.method == 'PUT': if request.method == 'PUT':
try: try:

View File

@ -67,7 +67,7 @@
} }
$.ajax({ $.ajax({
url: '/comment/' + encodeURIComponent(window.location.pathname) + '/new', url: '/1.0/' + encodeURIComponent(window.location.pathname) + '/new',
method: 'POST', method: 'POST',
type: 'json', type: 'json',
headers: { headers: {
@ -168,7 +168,7 @@
$('#isso_' + post['id'] + ' > footer .delete').on('click', function(event) { $('#isso_' + post['id'] + ' > footer .delete').on('click', function(event) {
$.ajax({ $.ajax({
url: '/comment/' + encodeURIComponent(window.location.pathname) + '/' + post['id'], url: '/1.0/' + encodeURIComponent(window.location.pathname) + '/' + post['id'],
method: 'DELETE', method: 'DELETE',
error: function(resp) { error: function(resp) {
alert('Mööp!'); alert('Mööp!');
@ -196,7 +196,7 @@
var fetch = function(thread) { var fetch = function(thread) {
var rv = $.ajax({ var rv = $.ajax({
url: '/comment/' + encodeURIComponent(window.location.pathname) + '/', url: '/1.0/' + encodeURIComponent(window.location.pathname) + '/',
method: 'GET', method: 'GET',
type: 'json', type: 'json',
headers: { headers: {

View File

@ -29,8 +29,8 @@ class TestComments(unittest.TestCase):
def testGet(self): def testGet(self):
self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...'))) self.post('/1.0/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
r = self.get('/comment/path/1') r = self.get('/1.0/path/1')
assert r.status_code == 200 assert r.status_code == 200
rv = json.loads(r.data) rv = json.loads(r.data)
@ -40,7 +40,7 @@ class TestComments(unittest.TestCase):
def testCreate(self): def testCreate(self):
rv = self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...'))) rv = self.post('/1.0/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
assert rv.status_code == 201 assert rv.status_code == 201
assert len(filter(lambda header: header[0] == 'Set-Cookie', rv.headers)) == 1 assert len(filter(lambda header: header[0] == 'Set-Cookie', rv.headers)) == 1
@ -54,9 +54,9 @@ class TestComments(unittest.TestCase):
def testCreateAndGetMultiple(self): def testCreateAndGetMultiple(self):
for i in range(20): for i in range(20):
self.post('/comment/path/new', data=json.dumps(comment(text='Spam'))) self.post('/1.0/path/new', data=json.dumps(comment(text='Spam')))
r = self.get('/comment/path/') r = self.get('/1.0/path/')
assert r.status_code == 200 assert r.status_code == 200
rv = json.loads(r.data) rv = json.loads(r.data)
@ -64,17 +64,17 @@ class TestComments(unittest.TestCase):
def testGetInvalid(self): def testGetInvalid(self):
assert self.get('/comment/path/123').status_code == 404 assert self.get('/1.0/path/123').status_code == 404
assert self.get('/comment/path/spam/123').status_code == 404 assert self.get('/1.0/path/spam/123').status_code == 404
assert self.get('/comment/foo/').status_code == 404 assert self.get('/1.0/foo/').status_code == 404
def testUpdate(self): def testUpdate(self):
self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...'))) self.post('/1.0/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
self.put('/comment/path/1', data=json.dumps(comment( self.put('/1.0/path/1', data=json.dumps(comment(
text='Hello World', author='me', website='http://example.com/'))) text='Hello World', author='me', website='http://example.com/')))
r = self.get('/comment/path/1') r = self.get('/1.0/path/1')
assert r.status_code == 200 assert r.status_code == 200
rv = json.loads(r.data) rv = json.loads(r.data)
@ -85,45 +85,45 @@ class TestComments(unittest.TestCase):
def testDelete(self): def testDelete(self):
self.post('/comment/path/new', data=json.dumps(comment(text='Lorem ipsum ...'))) self.post('/1.0/path/new', data=json.dumps(comment(text='Lorem ipsum ...')))
r = self.delete('/comment/path/1') r = self.delete('/1.0/path/1')
assert r.status_code == 200 assert r.status_code == 200
assert json.loads(r.data) == None assert json.loads(r.data) == None
assert self.get('/comment/path/1').status_code == 404 assert self.get('/1.0/path/1').status_code == 404
def testDeleteWithReference(self): def testDeleteWithReference(self):
client = Client(self.app, Response) client = Client(self.app, Response)
resp = client.post('/comment/path/new', data=json.dumps(comment(text='First'))) resp = client.post('/1.0/path/new', data=json.dumps(comment(text='First')))
self.post('/comment/path/new', data=json.dumps(comment(text='Second', parent=1))) self.post('/1.0/path/new', data=json.dumps(comment(text='Second', parent=1)))
r = client.delete('/comment/path/1') r = client.delete('/1.0/path/1')
assert r.status_code == 200 assert r.status_code == 200
assert Comment(**json.loads(r.data)).deleted assert Comment(**json.loads(r.data)).deleted
assert self.get('/comment/path/1').status_code == 200 assert self.get('/1.0/path/1').status_code == 200
assert self.get('/comment/path/2').status_code == 200 assert self.get('/1.0/path/2').status_code == 200
def testPathVariations(self): def testPathVariations(self):
paths = ['/sub/path/', '/path.html', '/sub/path.html', '%2Fpath/%2F'] paths = ['/sub/path/', '/path.html', '/sub/path.html', '%2Fpath/%2F', '/']
for path in paths: for path in paths:
assert self.post('/comment/' + path + '/new', assert self.post('/1.0/' + path + '/new',
data=json.dumps(comment(text='...'))).status_code == 201 data=json.dumps(comment(text='...'))).status_code == 201
for path in paths: for path in paths:
assert self.get('/comment/' + path) assert self.get('/1.0/' + path)
assert self.get('/comment/' + path + '/1') assert self.get('/1.0/' + path + '/1')
def testDeleteAndCreateByDifferentUsersButSamePostId(self): def testDeleteAndCreateByDifferentUsersButSamePostId(self):
mallory = Client(self.app, Response) mallory = Client(self.app, Response)
mallory.post('/comment/path/new', data=json.dumps(comment(text='Foo'))) mallory.post('/1.0/path/new', data=json.dumps(comment(text='Foo')))
mallory.delete('/comment/path/1') mallory.delete('/1.0/path/1')
bob = Client(self.app, Response) bob = Client(self.app, Response)
bob.post('/comment/path/new', data=json.dumps(comment(text='Bar'))) bob.post('/1.0/path/new', data=json.dumps(comment(text='Bar')))
assert mallory.delete('/comment/path/1').status_code == 403 assert mallory.delete('/1.0/path/1').status_code == 403
assert bob.delete('/comment/path/1').status_code == 200 assert bob.delete('/1.0/path/1').status_code == 200