diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2016-08-16 22:31:45 +0200 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2016-08-16 22:31:45 +0200 |
commit | 5c39adb7313e0696aabb4b71196ab7b0b378c359 (patch) | |
tree | ac44b67890509338b984f8cbf11660dc77cdd0fd | |
parent | 089ff2f2046fdbaf9531726eea1f8c6726ebf0c0 (diff) | |
download | PeerTube-5c39adb7313e0696aabb4b71196ab7b0b378c359.tar.gz PeerTube-5c39adb7313e0696aabb4b71196ab7b0b378c359.tar.zst PeerTube-5c39adb7313e0696aabb4b71196ab7b0b378c359.zip |
Server: add user list sort/pagination
-rw-r--r-- | server/controllers/api/v1/users.js | 20 | ||||
-rw-r--r-- | server/initializers/checker.js | 2 | ||||
-rw-r--r-- | server/initializers/constants.js | 1 | ||||
-rw-r--r-- | server/middlewares/sort.js | 7 | ||||
-rw-r--r-- | server/middlewares/validators/sort.js | 11 | ||||
-rw-r--r-- | server/models/user.js | 16 | ||||
-rw-r--r-- | server/models/utils.js | 30 | ||||
-rw-r--r-- | server/models/video.js | 4 | ||||
-rw-r--r-- | server/tests/api/check-params.js | 26 | ||||
-rw-r--r-- | server/tests/api/users.js | 83 | ||||
-rw-r--r-- | server/tests/utils/users.js | 15 |
11 files changed, 199 insertions, 16 deletions
diff --git a/server/controllers/api/v1/users.js b/server/controllers/api/v1/users.js index 704df770c..975e25e68 100644 --- a/server/controllers/api/v1/users.js +++ b/server/controllers/api/v1/users.js | |||
@@ -11,6 +11,10 @@ const logger = require('../../../helpers/logger') | |||
11 | const middlewares = require('../../../middlewares') | 11 | const middlewares = require('../../../middlewares') |
12 | const admin = middlewares.admin | 12 | const admin = middlewares.admin |
13 | const oAuth = middlewares.oauth | 13 | const oAuth = middlewares.oauth |
14 | const pagination = middlewares.pagination | ||
15 | const sort = middlewares.sort | ||
16 | const validatorsPagination = middlewares.validators.pagination | ||
17 | const validatorsSort = middlewares.validators.sort | ||
14 | const validatorsUsers = middlewares.validators.users | 18 | const validatorsUsers = middlewares.validators.users |
15 | 19 | ||
16 | const User = mongoose.model('User') | 20 | const User = mongoose.model('User') |
@@ -18,9 +22,16 @@ const Video = mongoose.model('Video') | |||
18 | 22 | ||
19 | const router = express.Router() | 23 | const router = express.Router() |
20 | 24 | ||
21 | router.get('/', listUsers) | ||
22 | router.get('/me', oAuth.authenticate, getUserInformation) | 25 | router.get('/me', oAuth.authenticate, getUserInformation) |
23 | 26 | ||
27 | router.get('/', | ||
28 | validatorsPagination.pagination, | ||
29 | validatorsSort.usersSort, | ||
30 | sort.setUsersSort, | ||
31 | pagination.setPagination, | ||
32 | listUsers | ||
33 | ) | ||
34 | |||
24 | router.post('/', | 35 | router.post('/', |
25 | oAuth.authenticate, | 36 | oAuth.authenticate, |
26 | admin.ensureIsAdmin, | 37 | admin.ensureIsAdmin, |
@@ -73,10 +84,10 @@ function getUserInformation (req, res, next) { | |||
73 | } | 84 | } |
74 | 85 | ||
75 | function listUsers (req, res, next) { | 86 | function listUsers (req, res, next) { |
76 | User.list(function (err, usersList) { | 87 | User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) { |
77 | if (err) return next(err) | 88 | if (err) return next(err) |
78 | 89 | ||
79 | res.json(getFormatedUsers(usersList)) | 90 | res.json(getFormatedUsers(usersList, usersTotal)) |
80 | }) | 91 | }) |
81 | } | 92 | } |
82 | 93 | ||
@@ -145,7 +156,7 @@ function success (req, res, next) { | |||
145 | 156 | ||
146 | // --------------------------------------------------------------------------- | 157 | // --------------------------------------------------------------------------- |
147 | 158 | ||
148 | function getFormatedUsers (users) { | 159 | function getFormatedUsers (users, usersTotal) { |
149 | const formatedUsers = [] | 160 | const formatedUsers = [] |
150 | 161 | ||
151 | users.forEach(function (user) { | 162 | users.forEach(function (user) { |
@@ -153,6 +164,7 @@ function getFormatedUsers (users) { | |||
153 | }) | 164 | }) |
154 | 165 | ||
155 | return { | 166 | return { |
167 | total: usersTotal, | ||
156 | data: formatedUsers | 168 | data: formatedUsers |
157 | } | 169 | } |
158 | } | 170 | } |
diff --git a/server/initializers/checker.js b/server/initializers/checker.js index 871d3cac2..4b6997547 100644 --- a/server/initializers/checker.js +++ b/server/initializers/checker.js | |||
@@ -39,7 +39,7 @@ function clientsExist (callback) { | |||
39 | } | 39 | } |
40 | 40 | ||
41 | function usersExist (callback) { | 41 | function usersExist (callback) { |
42 | User.count(function (err, totalUsers) { | 42 | User.countTotal(function (err, totalUsers) { |
43 | if (err) return callback(err) | 43 | if (err) return callback(err) |
44 | 44 | ||
45 | return callback(null, totalUsers !== 0) | 45 | return callback(null, totalUsers !== 0) |
diff --git a/server/initializers/constants.js b/server/initializers/constants.js index 416356400..cd2e0cfb9 100644 --- a/server/initializers/constants.js +++ b/server/initializers/constants.js | |||
@@ -63,6 +63,7 @@ const SEEDS_IN_PARALLEL = 3 | |||
63 | 63 | ||
64 | // Sortable columns per schema | 64 | // Sortable columns per schema |
65 | const SORTABLE_COLUMNS = { | 65 | const SORTABLE_COLUMNS = { |
66 | USERS: [ 'username', '-username', 'createdDate', '-createdDate' ], | ||
66 | VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ] | 67 | VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ] |
67 | } | 68 | } |
68 | 69 | ||
diff --git a/server/middlewares/sort.js b/server/middlewares/sort.js index 9f52290a6..8ed157805 100644 --- a/server/middlewares/sort.js +++ b/server/middlewares/sort.js | |||
@@ -1,9 +1,16 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const sortMiddleware = { | 3 | const sortMiddleware = { |
4 | setUsersSort: setUsersSort, | ||
4 | setVideosSort: setVideosSort | 5 | setVideosSort: setVideosSort |
5 | } | 6 | } |
6 | 7 | ||
8 | function setUsersSort (req, res, next) { | ||
9 | if (!req.query.sort) req.query.sort = '-createdDate' | ||
10 | |||
11 | return next() | ||
12 | } | ||
13 | |||
7 | function setVideosSort (req, res, next) { | 14 | function setVideosSort (req, res, next) { |
8 | if (!req.query.sort) req.query.sort = '-createdDate' | 15 | if (!req.query.sort) req.query.sort = '-createdDate' |
9 | 16 | ||
diff --git a/server/middlewares/validators/sort.js b/server/middlewares/validators/sort.js index 56b63cc8b..37b34ef52 100644 --- a/server/middlewares/validators/sort.js +++ b/server/middlewares/validators/sort.js | |||
@@ -5,9 +5,20 @@ const constants = require('../../initializers/constants') | |||
5 | const logger = require('../../helpers/logger') | 5 | const logger = require('../../helpers/logger') |
6 | 6 | ||
7 | const validatorsSort = { | 7 | const validatorsSort = { |
8 | usersSort: usersSort, | ||
8 | videosSort: videosSort | 9 | videosSort: videosSort |
9 | } | 10 | } |
10 | 11 | ||
12 | function usersSort (req, res, next) { | ||
13 | const sortableColumns = constants.SORTABLE_COLUMNS.USERS | ||
14 | |||
15 | req.checkQuery('sort', 'Should have correct sortable column').optional().isIn(sortableColumns) | ||
16 | |||
17 | logger.debug('Checking sort parameters', { parameters: req.query }) | ||
18 | |||
19 | checkErrors(req, res, next) | ||
20 | } | ||
21 | |||
11 | function videosSort (req, res, next) { | 22 | function videosSort (req, res, next) { |
12 | const sortableColumns = constants.SORTABLE_COLUMNS.VIDEOS | 23 | const sortableColumns = constants.SORTABLE_COLUMNS.VIDEOS |
13 | 24 | ||
diff --git a/server/models/user.js b/server/models/user.js index d289da19a..c9c35b3e2 100644 --- a/server/models/user.js +++ b/server/models/user.js | |||
@@ -1,10 +1,15 @@ | |||
1 | const mongoose = require('mongoose') | 1 | const mongoose = require('mongoose') |
2 | 2 | ||
3 | const customUsersValidators = require('../helpers/custom-validators').users | 3 | const customUsersValidators = require('../helpers/custom-validators').users |
4 | const modelUtils = require('./utils') | ||
4 | 5 | ||
5 | // --------------------------------------------------------------------------- | 6 | // --------------------------------------------------------------------------- |
6 | 7 | ||
7 | const UserSchema = mongoose.Schema({ | 8 | const UserSchema = mongoose.Schema({ |
9 | createdDate: { | ||
10 | type: Date, | ||
11 | default: Date.now | ||
12 | }, | ||
8 | password: String, | 13 | password: String, |
9 | username: String, | 14 | username: String, |
10 | role: String | 15 | role: String |
@@ -19,9 +24,9 @@ UserSchema.methods = { | |||
19 | } | 24 | } |
20 | 25 | ||
21 | UserSchema.statics = { | 26 | UserSchema.statics = { |
22 | count: count, | 27 | countTotal: countTotal, |
23 | getByUsernameAndPassword: getByUsernameAndPassword, | 28 | getByUsernameAndPassword: getByUsernameAndPassword, |
24 | list: list, | 29 | listForApi: listForApi, |
25 | loadById: loadById, | 30 | loadById: loadById, |
26 | loadByUsername: loadByUsername | 31 | loadByUsername: loadByUsername |
27 | } | 32 | } |
@@ -30,7 +35,7 @@ mongoose.model('User', UserSchema) | |||
30 | 35 | ||
31 | // --------------------------------------------------------------------------- | 36 | // --------------------------------------------------------------------------- |
32 | 37 | ||
33 | function count (callback) { | 38 | function countTotal (callback) { |
34 | return this.count(callback) | 39 | return this.count(callback) |
35 | } | 40 | } |
36 | 41 | ||
@@ -38,8 +43,9 @@ function getByUsernameAndPassword (username, password) { | |||
38 | return this.findOne({ username: username, password: password }) | 43 | return this.findOne({ username: username, password: password }) |
39 | } | 44 | } |
40 | 45 | ||
41 | function list (callback) { | 46 | function listForApi (start, count, sort, callback) { |
42 | return this.find(callback) | 47 | const query = {} |
48 | return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) | ||
43 | } | 49 | } |
44 | 50 | ||
45 | function loadById (id, callback) { | 51 | function loadById (id, callback) { |
diff --git a/server/models/utils.js b/server/models/utils.js new file mode 100644 index 000000000..a961e8c5b --- /dev/null +++ b/server/models/utils.js | |||
@@ -0,0 +1,30 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | const parallel = require('async/parallel') | ||
4 | |||
5 | const utils = { | ||
6 | listForApiWithCount: listForApiWithCount | ||
7 | } | ||
8 | |||
9 | function listForApiWithCount (query, start, count, sort, callback) { | ||
10 | const self = this | ||
11 | |||
12 | parallel([ | ||
13 | function (asyncCallback) { | ||
14 | self.find(query).skip(start).limit(count).sort(sort).exec(asyncCallback) | ||
15 | }, | ||
16 | function (asyncCallback) { | ||
17 | self.count(query, asyncCallback) | ||
18 | } | ||
19 | ], function (err, results) { | ||
20 | if (err) return callback(err) | ||
21 | |||
22 | const data = results[0] | ||
23 | const total = results[1] | ||
24 | return callback(null, data, total) | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | // --------------------------------------------------------------------------- | ||
29 | |||
30 | module.exports = utils | ||
diff --git a/server/models/video.js b/server/models/video.js index a5540d127..63afc2efe 100644 --- a/server/models/video.js +++ b/server/models/video.js | |||
@@ -197,7 +197,7 @@ function getDurationFromFile (videoPath, callback) { | |||
197 | 197 | ||
198 | function listForApi (start, count, sort, callback) { | 198 | function listForApi (start, count, sort, callback) { |
199 | const query = {} | 199 | const query = {} |
200 | return modelUtils.findWithCount.call(this, query, start, count, sort, callback) | 200 | return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) |
201 | } | 201 | } |
202 | 202 | ||
203 | function listByUrlAndMagnet (fromUrl, magnetUri, callback) { | 203 | function listByUrlAndMagnet (fromUrl, magnetUri, callback) { |
@@ -234,7 +234,7 @@ function search (value, field, start, count, sort, callback) { | |||
234 | query[field] = new RegExp(value) | 234 | query[field] = new RegExp(value) |
235 | } | 235 | } |
236 | 236 | ||
237 | modelUtils.findWithCount.call(this, query, start, count, sort, callback) | 237 | modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) |
238 | } | 238 | } |
239 | 239 | ||
240 | function seedAllExisting (callback) { | 240 | function seedAllExisting (callback) { |
diff --git a/server/tests/api/check-params.js b/server/tests/api/check-params.js index 882948fac..fc8b0a42a 100644 --- a/server/tests/api/check-params.js +++ b/server/tests/api/check-params.js | |||
@@ -459,6 +459,32 @@ describe('Test parameters validator', function () { | |||
459 | let userId = null | 459 | let userId = null |
460 | let userAccessToken = null | 460 | let userAccessToken = null |
461 | 461 | ||
462 | describe('When listing users', function () { | ||
463 | it('Should fail with a bad start pagination', function (done) { | ||
464 | request(server.url) | ||
465 | .get(path) | ||
466 | .query({ start: 'hello' }) | ||
467 | .set('Accept', 'application/json') | ||
468 | .expect(400, done) | ||
469 | }) | ||
470 | |||
471 | it('Should fail with a bad count pagination', function (done) { | ||
472 | request(server.url) | ||
473 | .get(path) | ||
474 | .query({ count: 'hello' }) | ||
475 | .set('Accept', 'application/json') | ||
476 | .expect(400, done) | ||
477 | }) | ||
478 | |||
479 | it('Should fail with an incorrect sort', function (done) { | ||
480 | request(server.url) | ||
481 | .get(path) | ||
482 | .query({ sort: 'hello' }) | ||
483 | .set('Accept', 'application/json') | ||
484 | .expect(400, done) | ||
485 | }) | ||
486 | }) | ||
487 | |||
462 | describe('When adding a new user', function () { | 488 | describe('When adding a new user', function () { |
463 | it('Should fail with a too small username', function (done) { | 489 | it('Should fail with a too small username', function (done) { |
464 | const data = { | 490 | const data = { |
diff --git a/server/tests/api/users.js b/server/tests/api/users.js index a2557d2ab..c6c892bf2 100644 --- a/server/tests/api/users.js +++ b/server/tests/api/users.js | |||
@@ -209,17 +209,92 @@ describe('Test users', function () { | |||
209 | usersUtils.getUsersList(server.url, function (err, res) { | 209 | usersUtils.getUsersList(server.url, function (err, res) { |
210 | if (err) throw err | 210 | if (err) throw err |
211 | 211 | ||
212 | const users = res.body.data | 212 | const result = res.body |
213 | const total = result.total | ||
214 | const users = result.data | ||
213 | 215 | ||
216 | expect(total).to.equal(2) | ||
214 | expect(users).to.be.an('array') | 217 | expect(users).to.be.an('array') |
215 | expect(users.length).to.equal(2) | 218 | expect(users.length).to.equal(2) |
216 | 219 | ||
217 | const rootUser = users[0] | 220 | const user = users[0] |
221 | expect(user.username).to.equal('user_1') | ||
222 | |||
223 | const rootUser = users[1] | ||
218 | expect(rootUser.username).to.equal('root') | 224 | expect(rootUser.username).to.equal('root') |
225 | userId = user.id | ||
226 | |||
227 | done() | ||
228 | }) | ||
229 | }) | ||
230 | |||
231 | it('Should list only the first user by username asc', function (done) { | ||
232 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, 'username', function (err, res) { | ||
233 | if (err) throw err | ||
234 | |||
235 | const result = res.body | ||
236 | const total = result.total | ||
237 | const users = result.data | ||
238 | |||
239 | expect(total).to.equal(2) | ||
240 | expect(users.length).to.equal(1) | ||
219 | 241 | ||
220 | const user = users[1] | 242 | const user = users[0] |
243 | expect(user.username).to.equal('root') | ||
244 | |||
245 | done() | ||
246 | }) | ||
247 | }) | ||
248 | |||
249 | it('Should list only the first user by username desc', function (done) { | ||
250 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-username', function (err, res) { | ||
251 | if (err) throw err | ||
252 | |||
253 | const result = res.body | ||
254 | const total = result.total | ||
255 | const users = result.data | ||
256 | |||
257 | expect(total).to.equal(2) | ||
258 | expect(users.length).to.equal(1) | ||
259 | |||
260 | const user = users[0] | ||
221 | expect(user.username).to.equal('user_1') | 261 | expect(user.username).to.equal('user_1') |
222 | userId = user.id | 262 | |
263 | done() | ||
264 | }) | ||
265 | }) | ||
266 | |||
267 | it('Should list only the second user by createdDate desc', function (done) { | ||
268 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 1, '-createdDate', function (err, res) { | ||
269 | if (err) throw err | ||
270 | |||
271 | const result = res.body | ||
272 | const total = result.total | ||
273 | const users = result.data | ||
274 | |||
275 | expect(total).to.equal(2) | ||
276 | expect(users.length).to.equal(1) | ||
277 | |||
278 | const user = users[0] | ||
279 | expect(user.username).to.equal('user_1') | ||
280 | |||
281 | done() | ||
282 | }) | ||
283 | }) | ||
284 | |||
285 | it('Should list all the users by createdDate asc', function (done) { | ||
286 | usersUtils.getUsersListPaginationAndSort(server.url, 0, 2, 'createdDate', function (err, res) { | ||
287 | if (err) throw err | ||
288 | |||
289 | const result = res.body | ||
290 | const total = result.total | ||
291 | const users = result.data | ||
292 | |||
293 | expect(total).to.equal(2) | ||
294 | expect(users.length).to.equal(2) | ||
295 | |||
296 | expect(users[0].username).to.equal('root') | ||
297 | expect(users[1].username).to.equal('user_1') | ||
223 | 298 | ||
224 | done() | 299 | done() |
225 | }) | 300 | }) |
diff --git a/server/tests/utils/users.js b/server/tests/utils/users.js index 3b560e409..0cf4e4adb 100644 --- a/server/tests/utils/users.js +++ b/server/tests/utils/users.js | |||
@@ -6,6 +6,7 @@ const usersUtils = { | |||
6 | createUser: createUser, | 6 | createUser: createUser, |
7 | getUserInformation: getUserInformation, | 7 | getUserInformation: getUserInformation, |
8 | getUsersList: getUsersList, | 8 | getUsersList: getUsersList, |
9 | getUsersListPaginationAndSort: getUsersListPaginationAndSort, | ||
9 | removeUser: removeUser, | 10 | removeUser: removeUser, |
10 | updateUser: updateUser | 11 | updateUser: updateUser |
11 | } | 12 | } |
@@ -52,6 +53,20 @@ function getUsersList (url, end) { | |||
52 | .end(end) | 53 | .end(end) |
53 | } | 54 | } |
54 | 55 | ||
56 | function getUsersListPaginationAndSort (url, start, count, sort, end) { | ||
57 | const path = '/api/v1/users' | ||
58 | |||
59 | request(url) | ||
60 | .get(path) | ||
61 | .query({ start: start }) | ||
62 | .query({ count: count }) | ||
63 | .query({ sort: sort }) | ||
64 | .set('Accept', 'application/json') | ||
65 | .expect(200) | ||
66 | .expect('Content-Type', /json/) | ||
67 | .end(end) | ||
68 | } | ||
69 | |||
55 | function removeUser (url, userId, accessToken, expectedStatus, end) { | 70 | function removeUser (url, userId, accessToken, expectedStatus, end) { |
56 | if (!end) { | 71 | if (!end) { |
57 | end = expectedStatus | 72 | end = expectedStatus |