]> git.r.bdr.sh - rbdr/dasein/blob - lib/handlers/comments.js
Merge branch 'release/1.0.0'
[rbdr/dasein] / lib / handlers / comments.js
1 'use strict';
2
3 const Joi = require('joi');
4 const Pify = require('pify');
5 const Redis = require('redis');
6 const UUID = require('uuid/v4');
7
8 const internals = {};
9
10 internals.kPostsPrefix = 'posts';
11 internals.kCommentsPrefix = 'comments';
12 internals.kMaxCommentSize = 255;
13
14 internals.kCommentsSchema = Joi.object().keys({
15 uuid: Joi.string().required(),
16 content: Joi.string().max(internals.kMaxCommentSize).required(),
17 timestamp: Joi.number().integer().required(),
18 userId: Joi.string().required(),
19 userName: Joi.string().required(),
20 userImage: Joi.string().required()
21 });
22
23 /**
24 * Handles the HTTP requests for comment related operations
25 *
26 * @class CommentsHandler
27 * @param {Dasein.tConfiguration} config The configuration to
28 * initialize.
29 */
30 module.exports = internals.CommentsHandler = class CommentsHandler {
31 constructor(config) {
32
33 this._ttl = config.ttl;
34 this._redis = Redis.createClient(config.redis);
35
36 // Log an error if it happens.
37 this._redis.on('error', (err) => {
38
39 console.error(err);
40 });
41 }
42
43 /**
44 * Fetches all available comments
45 *
46 * @function findAll
47 * @memberof CommentsHandler
48 * @instance
49 * @return {generator} a koa compatible handler generator function
50 */
51 findAll() {
52
53 const self = this;
54
55 return function * (postId) {
56
57 if (!this.state.user) {
58 return this.throw('Unauthorized', 401);
59 }
60
61 const scan = Pify(self._redis.scan.bind(self._redis));
62 const hgetall = Pify(self._redis.hgetall.bind(self._redis));
63
64 const commentsKey = `${internals.kCommentsPrefix}:${postId}:*`;
65 let keys = [];
66 let nextCursor = 0;
67 let currentKeys = null;
68
69 do {
70 [nextCursor, currentKeys] = yield scan(nextCursor || 0, 'MATCH', commentsKey);
71 keys = keys.concat(currentKeys);
72 } while (nextCursor > 0);
73
74 const comments = yield keys.map((key) => hgetall(key));
75
76 this.body = comments.sort((a, b) => a.timestamp - b.timestamp);
77 };
78 }
79
80 /**
81 * Creates a comment
82 *
83 * @function create
84 * @memberof CommentsHandler
85 * @instance
86 * @return {generator} a koa compatible handler generator function
87 */
88 create() {
89
90 const self = this;
91
92 return function * (postId) {
93
94 if (!this.state.user) {
95 return this.throw('Unauthorized', 401);
96 }
97
98 const hmset = Pify(self._redis.hmset.bind(self._redis));
99 const hgetall = Pify(self._redis.hgetall.bind(self._redis));
100 const expire = Pify(self._redis.expire.bind(self._redis));
101
102 const uuid = UUID();
103 const timestamp = Date.now();
104 const user = this.state.user;
105
106 const postKey = `${internals.kPostsPrefix}:${postId}`;
107 const commentKey = `${internals.kCommentsPrefix}:${postId}:${uuid}`;
108
109 const comment = {
110 uuid,
111 content: this.request.body.content,
112 timestamp,
113 userId: user.screen_name,
114 userName: user.name,
115 userImage: user.profile_image_url_https
116 };
117
118 yield self._validate(comment).catch((err) => {
119
120 this.throw(err.message, 422);
121 });
122
123 yield hmset(commentKey, comment);
124 yield expire(commentKey, self._ttl * 100); // this is me being lazy :(
125 // comments will last at most 100 bumps
126 // but will disappear eventually
127 yield expire(postKey, self._ttl); // bumps the parent comment TTL
128
129 this.body = yield hgetall(commentKey);
130 };
131 }
132
133 // Validates the comment schema
134
135 _validate(comment) {
136
137 const validate = Pify(Joi.validate.bind(Joi));
138 return validate(comment, internals.kCommentsSchema);
139 }
140 };