New 'latest' endpoint to serve latest comments.
This commit is contained in:
parent
1de758887a
commit
1633d9261e
@ -110,6 +110,10 @@ gravatar-url
|
|||||||
Url for gravatar images. The "{}" is where the email hash will be placed.
|
Url for gravatar images. The "{}" is where the email hash will be placed.
|
||||||
Defaults to "https://www.gravatar.com/avatar/{}?d=identicon"
|
Defaults to "https://www.gravatar.com/avatar/{}?d=identicon"
|
||||||
|
|
||||||
|
latest-enabled
|
||||||
|
If True it will enable the ``/latest`` endpoint. Optional, defaults
|
||||||
|
to False.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
|
.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
|
||||||
|
@ -83,6 +83,19 @@ plain :
|
|||||||
pass plain=1 to get the raw comment text, defaults to 0.
|
pass plain=1 to get the raw comment text, defaults to 0.
|
||||||
|
|
||||||
|
|
||||||
|
Get the latest N comments for all threads:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
GET /latest?limit=N
|
||||||
|
|
||||||
|
The N parameter limits how many of the latest comments to retrieve; it's
|
||||||
|
mandatory, and must be an integer greater than 0.
|
||||||
|
|
||||||
|
This endpoint needs to be enabled in the configuration (see the
|
||||||
|
``latest-enabled`` option in the ``general`` section).
|
||||||
|
|
||||||
|
|
||||||
Create comment
|
Create comment
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ class TestComments(unittest.TestCase):
|
|||||||
conf.set("general", "dbpath", self.path)
|
conf.set("general", "dbpath", self.path)
|
||||||
conf.set("guard", "enabled", "off")
|
conf.set("guard", "enabled", "off")
|
||||||
conf.set("hash", "algorithm", "none")
|
conf.set("hash", "algorithm", "none")
|
||||||
|
conf.set("general", "latest-enabled", "true")
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
|
||||||
class App(Isso, core.Mixin):
|
class App(Isso, core.Mixin):
|
||||||
@ -451,6 +452,48 @@ class TestComments(unittest.TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
rv["text"], '<p>This is <strong>mark</strong><em>down</em></p>')
|
rv["text"], '<p>This is <strong>mark</strong><em>down</em></p>')
|
||||||
|
|
||||||
|
def testLatestOk(self):
|
||||||
|
# load some comments in a mix of posts
|
||||||
|
saved = []
|
||||||
|
for idx, post_id in enumerate([1, 2, 2, 1, 2, 1, 3, 1, 4, 2, 3, 4, 1, 2]):
|
||||||
|
text = 'text-{}'.format(idx)
|
||||||
|
post_uri = 'test-{}'.format(post_id)
|
||||||
|
self.post('/new?uri=' + post_uri, data=json.dumps({'text': text}))
|
||||||
|
saved.append((post_uri, text))
|
||||||
|
|
||||||
|
response = self.get('/latest?limit=5')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
body = loads(response.data)
|
||||||
|
expected_items = saved[-5:] # latest 5
|
||||||
|
for reply, expected in zip(body, expected_items):
|
||||||
|
expected_uri, expected_text = expected
|
||||||
|
self.assertIn(expected_text, reply['text'])
|
||||||
|
self.assertEqual(expected_uri, reply['uri'])
|
||||||
|
|
||||||
|
def testLatestWithoutLimit(self):
|
||||||
|
response = self.get('/latest')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def testLatestBadLimitNaN(self):
|
||||||
|
response = self.get('/latest?limit=WAT')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def testLatestBadLimitNegative(self):
|
||||||
|
response = self.get('/latest?limit=-12')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def testLatestBadLimitZero(self):
|
||||||
|
response = self.get('/latest?limit=0')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def testLatestNotEnabled(self):
|
||||||
|
# disable the endpoint
|
||||||
|
self.conf.set("general", "latest-enabled", "false")
|
||||||
|
|
||||||
|
response = self.get('/latest?limit=5')
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
|
||||||
class TestModeratedComments(unittest.TestCase):
|
class TestModeratedComments(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import collections
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import functools
|
import functools
|
||||||
@ -112,6 +113,7 @@ class API(object):
|
|||||||
('count', ('GET', '/count')),
|
('count', ('GET', '/count')),
|
||||||
('counts', ('POST', '/count')),
|
('counts', ('POST', '/count')),
|
||||||
('feed', ('GET', '/feed')),
|
('feed', ('GET', '/feed')),
|
||||||
|
('latest', ('GET', '/latest')),
|
||||||
('view', ('GET', '/id/<int:id>')),
|
('view', ('GET', '/id/<int:id>')),
|
||||||
('edit', ('PUT', '/id/<int:id>')),
|
('edit', ('PUT', '/id/<int:id>')),
|
||||||
('delete', ('DELETE', '/id/<int:id>')),
|
('delete', ('DELETE', '/id/<int:id>')),
|
||||||
@ -1136,3 +1138,89 @@ class API(object):
|
|||||||
counts=comment_mode_count,
|
counts=comment_mode_count,
|
||||||
order_by=order_by, asc=asc,
|
order_by=order_by, asc=asc,
|
||||||
isso_host_script=isso_host_script)
|
isso_host_script=isso_host_script)
|
||||||
|
"""
|
||||||
|
@api {get} /latest latest
|
||||||
|
@apiGroup Comment
|
||||||
|
@apiDescription
|
||||||
|
Get the latest comments from the system, no matter which thread
|
||||||
|
|
||||||
|
@apiParam {number} limit
|
||||||
|
The quantity of last comments to retrieve
|
||||||
|
|
||||||
|
@apiExample {curl} Get the latest 5 comments
|
||||||
|
curl 'https://comments.example.com/latest?limit=5'
|
||||||
|
|
||||||
|
@apiUse commentResponse
|
||||||
|
|
||||||
|
@apiSuccessExample Example result:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"website": null,
|
||||||
|
"uri": "/some",
|
||||||
|
"author": null,
|
||||||
|
"parent": null,
|
||||||
|
"created": 1464912312.123416,
|
||||||
|
"text": " <p>I want to use MySQL</p>",
|
||||||
|
"dislikes": 0,
|
||||||
|
"modified": null,
|
||||||
|
"mode": 1,
|
||||||
|
"id": 3,
|
||||||
|
"likes": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"website": null,
|
||||||
|
"uri": "/other",
|
||||||
|
"author": null,
|
||||||
|
"parent": null,
|
||||||
|
"created": 1464914341.312426,
|
||||||
|
"text": " <p>I want to use MySQL</p>",
|
||||||
|
"dislikes": 0,
|
||||||
|
"modified": null,
|
||||||
|
"mode": 1,
|
||||||
|
"id": 4,
|
||||||
|
"likes": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def latest(self, environ, request):
|
||||||
|
# if the feature is not allowed, don't present the endpoint
|
||||||
|
if not self.conf.getboolean("latest-enabled"):
|
||||||
|
return NotFound()
|
||||||
|
|
||||||
|
# get and check the limit
|
||||||
|
bad_limit_msg = "Query parameter 'limit' is mandatory (integer, >0)"
|
||||||
|
try:
|
||||||
|
limit = int(request.args['limit'])
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
return BadRequest(bad_limit_msg)
|
||||||
|
if limit <= 0:
|
||||||
|
return BadRequest(bad_limit_msg)
|
||||||
|
|
||||||
|
# retrieve the latest N comments from the DB
|
||||||
|
all_comments_gen = self.comments.fetchall(limit=None, order_by='created', mode='1')
|
||||||
|
comments = collections.deque(all_comments_gen, maxlen=limit)
|
||||||
|
|
||||||
|
# prepare a special set of fields (except text which is rendered specifically)
|
||||||
|
fields = {
|
||||||
|
'author',
|
||||||
|
'created',
|
||||||
|
'dislikes',
|
||||||
|
'id',
|
||||||
|
'likes',
|
||||||
|
'mode',
|
||||||
|
'modified',
|
||||||
|
'parent',
|
||||||
|
'text',
|
||||||
|
'uri',
|
||||||
|
'website',
|
||||||
|
}
|
||||||
|
|
||||||
|
# process the retrieved comments and build results
|
||||||
|
result = []
|
||||||
|
for comment in comments:
|
||||||
|
processed = {key: comment[key] for key in fields}
|
||||||
|
processed['text'] = self.isso.render(comment['text'])
|
||||||
|
result.append(processed)
|
||||||
|
|
||||||
|
return JSON(result, 200)
|
||||||
|
@ -59,6 +59,10 @@ gravatar = false
|
|||||||
# default url for gravatar. {} is where the hash will be placed
|
# default url for gravatar. {} is where the hash will be placed
|
||||||
gravatar-url = https://www.gravatar.com/avatar/{}?d=identicon
|
gravatar-url = https://www.gravatar.com/avatar/{}?d=identicon
|
||||||
|
|
||||||
|
# enable the "/latest" endpoint, that serves comment for multiple posts (not
|
||||||
|
# needing to previously know the posts URIs)
|
||||||
|
latest-enabled = false
|
||||||
|
|
||||||
[admin]
|
[admin]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user