]>
Commit | Line | Data |
---|---|---|
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 | }; |