/comment/ -> /1.0/
This commit is contained in:
parent
ac6d88f61e
commit
39899dda81
32
Readme.md
32
Readme.md
@ -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
|
||||||
|
|
||||||
|
@ -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})
|
||||||
|
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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: {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user