]>
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.kMaxPostSize = 255; | |
12 | ||
13 | internals.kPostsSchema = Joi.object().keys({ | |
14 | uuid: Joi.string().required(), | |
15 | content: Joi.string().max(internals.kMaxPostSize).required(), | |
16 | timestamp: Joi.number().integer().required(), | |
17 | userId: Joi.string().required(), | |
18 | userName: Joi.string().required(), | |
19 | userImage: Joi.string().required() | |
20 | }); | |
21 | ||
22 | /** | |
23 | * Handles the HTTP requests for posts related operations | |
24 | * | |
25 | * @class PostsHandler | |
26 | * @param {Dasein.tConfiguration} config The configuration to | |
27 | * initialize. | |
28 | */ | |
29 | module.exports = internals.PostsHandler = class PostsHandler { | |
30 | constructor(config) { | |
31 | ||
32 | this._ttl = config.ttl; | |
33 | this._redis = Redis.createClient(config.redis); | |
34 | ||
35 | // Log an error if it happens. | |
36 | this._redis.on('error', (err) => { | |
37 | ||
38 | console.error(err); | |
39 | }); | |
40 | } | |
41 | ||
42 | /** | |
43 | * Fetches all available posts | |
44 | * | |
45 | * @function findAll | |
46 | * @memberof PostsHandler | |
47 | * @instance | |
48 | * @return {generator} a koa compatible handler generator function | |
49 | */ | |
50 | findAll() { | |
51 | ||
52 | const self = this; | |
53 | ||
54 | return function * () { | |
55 | ||
56 | if (!this.state.user) { | |
57 | return this.throw('Unauthorized', 401); | |
58 | } | |
59 | ||
60 | const scan = Pify(self._redis.scan.bind(self._redis)); | |
61 | const hgetall = Pify(self._redis.hgetall.bind(self._redis)); | |
62 | ||
63 | let keys = []; | |
64 | let nextCursor = 0; | |
65 | let currentKeys = null; | |
66 | ||
67 | do { | |
68 | [nextCursor, currentKeys] = yield scan(nextCursor || 0, 'MATCH', `${internals.kPostsPrefix}:*`); | |
69 | keys = keys.concat(currentKeys); | |
70 | } while (nextCursor > 0); | |
71 | ||
72 | const posts = yield keys.map((key) => hgetall(key)); | |
73 | ||
74 | this.body = posts.sort((a, b) => b.timestamp - a.timestamp); | |
75 | }; | |
76 | } | |
77 | ||
78 | /** | |
79 | * Fetches a single post | |
80 | * | |
81 | * @function find | |
82 | * @memberof PostsHandler | |
83 | * @instance | |
84 | * @return {generator} a koa compatible handler generator function | |
85 | */ | |
86 | find() { | |
87 | ||
88 | const self = this; | |
89 | ||
90 | return function * (uuid) { | |
91 | ||
92 | if (!this.state.user) { | |
93 | return this.throw('Unauthorized', 401); | |
94 | } | |
95 | ||
96 | const hgetall = Pify(self._redis.hgetall.bind(self._redis)); | |
97 | ||
98 | const postKey = `${internals.kPostsPrefix}:${uuid}`; | |
99 | ||
100 | const post = yield hgetall(postKey); | |
101 | ||
102 | if (!post) { | |
103 | this.throw('Post not found', 404); | |
104 | } | |
105 | ||
106 | this.body = post; | |
107 | }; | |
108 | } | |
109 | ||
110 | /** | |
111 | * Creates a post | |
112 | * | |
113 | * @function create | |
114 | * @memberof PostsHandler | |
115 | * @instance | |
116 | * @return {generator} a koa compatible handler generator function | |
117 | */ | |
118 | create() { | |
119 | ||
120 | const self = this; | |
121 | ||
122 | return function * () { | |
123 | ||
124 | if (!this.state.user) { | |
125 | return this.throw('Unauthorized', 401); | |
126 | } | |
127 | ||
128 | const hmset = Pify(self._redis.hmset.bind(self._redis)); | |
129 | const hgetall = Pify(self._redis.hgetall.bind(self._redis)); | |
130 | const expire = Pify(self._redis.expire.bind(self._redis)); | |
131 | ||
132 | const uuid = UUID(); | |
133 | const timestamp = Date.now(); | |
134 | const user = this.state.user; | |
135 | ||
136 | const postKey = `${internals.kPostsPrefix}:${uuid}`; | |
137 | ||
138 | const post = { | |
139 | uuid, | |
140 | content: this.request.body.content, | |
141 | timestamp, | |
142 | userId: user.screen_name, | |
143 | userName: user.name, | |
144 | userImage: user.profile_image_url_https | |
145 | }; | |
146 | ||
147 | yield self._validate(post).catch((err) => { | |
148 | ||
149 | this.throw(err.message, 422); | |
150 | }); | |
151 | ||
152 | yield hmset(postKey, post); | |
153 | yield expire(postKey, self._ttl); | |
154 | ||
155 | this.body = yield hgetall(postKey); | |
156 | }; | |
157 | } | |
158 | ||
159 | /** | |
160 | * Deletes a post | |
161 | * | |
162 | * @function delete | |
163 | * @memberof PostsHandler | |
164 | * @instance | |
165 | * @return {generator} a koa compatible handler generator function | |
166 | */ | |
167 | delete() { | |
168 | ||
169 | return function * () {}; | |
170 | } | |
171 | ||
172 | // Validates the post schema | |
173 | ||
174 | _validate(post) { | |
175 | ||
176 | const validate = Pify(Joi.validate.bind(Joi)); | |
177 | return validate(post, internals.kPostsSchema); | |
178 | } | |
179 | }; |