Add ability to sort videos list
authorChocobozzz <florian.bigard@gmail.com>
Tue, 17 May 2016 19:03:00 +0000 (21:03 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Tue, 17 May 2016 19:03:00 +0000 (21:03 +0200)
server/controllers/api/v1/videos.js
server/initializers/constants.js
server/middlewares/index.js
server/middlewares/reqValidators/index.js
server/middlewares/reqValidators/pagination.js
server/middlewares/reqValidators/sort.js [new file with mode: 0644]
server/middlewares/sort.js [new file with mode: 0644]
server/models/videos.js
server/tests/api/checkParams.js
server/tests/api/singlePod.js
server/tests/api/utils.js

index c6dbfbaf27d6305337f95d8501327cdca88544fa..73e496ace0a4ccee34a5950cc2a37befe21c2aa4 100644 (file)
@@ -15,7 +15,9 @@ const oAuth2 = middlewares.oauth2
 const pagination = middlewares.pagination
 const reqValidator = middlewares.reqValidators
 const reqValidatorPagination = reqValidator.pagination
+const reqValidatorSort = reqValidator.sort
 const reqValidatorVideos = reqValidator.videos
+const sort = middlewares.sort
 const utils = require('../../../helpers/utils')
 const Videos = require('../../../models/videos') // model
 const videos = require('../../../lib/videos')
@@ -47,6 +49,8 @@ const thumbnailsDir = path.join(__dirname, '..', '..', '..', '..', config.get('s
 
 router.get('/',
   reqValidatorPagination.pagination,
+  reqValidatorSort.videosSort,
+  sort.setVideosSort,
   pagination.setPagination,
   listVideos
 )
@@ -68,6 +72,8 @@ router.delete('/:id',
 router.get('/search/:name',
   reqValidatorVideos.videosSearch,
   reqValidatorPagination.pagination,
+  reqValidatorSort.videosSort,
+  sort.setVideosSort,
   pagination.setPagination,
   searchVideos
 )
@@ -173,7 +179,7 @@ function getVideos (req, res, next) {
 }
 
 function listVideos (req, res, next) {
-  Videos.list(req.query.start, req.query.count, function (err, videosList) {
+  Videos.list(req.query.start, req.query.count, req.query.sort, function (err, videosList) {
     if (err) return next(err)
 
     res.json(getFormatedVideos(videosList))
@@ -231,7 +237,7 @@ function removeVideo (req, res, next) {
 }
 
 function searchVideos (req, res, next) {
-  Videos.search(req.params.name, req.query.start, req.query.count, function (err, videosList) {
+  Videos.search(req.params.name, req.query.start, req.query.count, req.query.sort, function (err, videosList) {
     if (err) return next(err)
 
     res.json(getFormatedVideos(videosList))
index 78bee5efece2f0793e647c6f3d1f9a184860ab6e..4350bb892653ce778ddc2de7dec59ccdd36f51eb 100644 (file)
@@ -26,6 +26,11 @@ const PODS_SCORE = {
 // Number of retries we make for the make retry requests (to friends...)
 let REQUEST_RETRIES = 10
 
+// Sortable columns per schema
+const SORTABLE_COLUMNS = {
+  VIDEOS: [ 'name', '-name', 'duration', '-duration', 'createdDate', '-createdDate' ]
+}
+
 // Videos thumbnail size
 const THUMBNAILS_SIZE = '200x110'
 
@@ -51,6 +56,7 @@ module.exports = {
   PAGINATION_COUNT_DEFAULT: PAGINATION_COUNT_DEFAULT,
   PODS_SCORE: PODS_SCORE,
   REQUEST_RETRIES: REQUEST_RETRIES,
+  SORTABLE_COLUMNS: SORTABLE_COLUMNS,
   THUMBNAILS_SIZE: THUMBNAILS_SIZE,
   THUMBNAILS_STATIC_PATH: THUMBNAILS_STATIC_PATH
 }
index f0fad3418302e4dd267796cffaba6fe7e1a44b6d..35858da2c8e411f72f648103012c596fc1b12fd9 100644 (file)
@@ -3,12 +3,14 @@
 const oauth2 = require('./oauth2')
 const pagination = require('./pagination')
 const reqValidatorsMiddleware = require('./reqValidators')
+const sort = require('./sort')
 const secureMiddleware = require('./secure')
 
 const middlewares = {
   oauth2: oauth2,
   pagination: pagination,
   reqValidators: reqValidatorsMiddleware,
+  sort: sort,
   secure: secureMiddleware
 }
 
index b732a27b64603db77bea75bcc5fe4e348b3ccfbb..be68f6a297e4f12c20345a13a28aee6dbe5e60e1 100644 (file)
@@ -3,12 +3,14 @@
 const paginationReqValidators = require('./pagination')
 const podsReqValidators = require('./pods')
 const remoteReqValidators = require('./remote')
+const sortReqValidators = require('./sort')
 const videosReqValidators = require('./videos')
 
 const reqValidators = {
   pagination: paginationReqValidators,
   pods: podsReqValidators,
   remote: remoteReqValidators,
+  sort: sortReqValidators,
   videos: videosReqValidators
 }
 
index ca8375396f4f1c4aa43a9d90d9b7befa85f42214..e598f269a8abf34401257fcc343c905d3754541e 100644 (file)
@@ -8,10 +8,10 @@ const reqValidatorsPagination = {
 }
 
 function pagination (req, res, next) {
-  req.checkParams('start', 'Should have a number start').optional().isInt()
-  req.checkParams('count', 'Should have a number count').optional().isInt()
+  req.checkQuery('start', 'Should have a number start').optional().isInt()
+  req.checkQuery('count', 'Should have a number count').optional().isInt()
 
-  logger.debug('Checking pagination parameters', { parameters: req.params })
+  logger.debug('Checking pagination parameters', { parameters: req.query })
 
   checkErrors(req, res, next)
 }
diff --git a/server/middlewares/reqValidators/sort.js b/server/middlewares/reqValidators/sort.js
new file mode 100644 (file)
index 0000000..06e680e
--- /dev/null
@@ -0,0 +1,23 @@
+'use strict'
+
+const checkErrors = require('./utils').checkErrors
+const constants = require('../../initializers/constants')
+const logger = require('../../helpers/logger')
+
+const reqValidatorsSort = {
+  videosSort: videosSort
+}
+
+function videosSort (req, res, next) {
+  const sortableColumns = constants.SORTABLE_COLUMNS.VIDEOS
+
+  req.checkQuery('sort', 'Should have correct sortable column').optional().isIn(sortableColumns)
+
+  logger.debug('Checking sort parameters', { parameters: req.query })
+
+  checkErrors(req, res, next)
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = reqValidatorsSort
diff --git a/server/middlewares/sort.js b/server/middlewares/sort.js
new file mode 100644 (file)
index 0000000..9f52290
--- /dev/null
@@ -0,0 +1,15 @@
+'use strict'
+
+const sortMiddleware = {
+  setVideosSort: setVideosSort
+}
+
+function setVideosSort (req, res, next) {
+  if (!req.query.sort) req.query.sort = '-createdDate'
+
+  return next()
+}
+
+// ---------------------------------------------------------------------------
+
+module.exports = sortMiddleware
index 250ad3952b27e297712c75c0cf9da3bedc96edc2..9cac8eddabd9e40129811ba359eebe1a449b1992 100644 (file)
@@ -80,8 +80,9 @@ function get (id, callback) {
   })
 }
 
-function list (start, count, callback) {
-  VideosDB.find({}).skip(start).limit(start + count).exec(function (err, videosList) {
+function list (start, count, sort, callback) {
+  VideosDB.find({}).skip(start).limit(start + count).sort(sort)
+  .exec(function (err, videosList) {
     if (err) {
       logger.error('Cannot get the list of the videos.')
       return callback(err)
@@ -129,8 +130,8 @@ function removeByIds (ids, callback) {
   VideosDB.remove({ _id: { $in: ids } }, callback)
 }
 
-function search (name, start, count, callback) {
-  VideosDB.find({ name: new RegExp(name) }).skip(start).limit(start + count)
+function search (name, start, count, sort, callback) {
+  VideosDB.find({ name: new RegExp(name) }).skip(start).limit(start + count).sort(sort)
   .exec(function (err, videos) {
     if (err) {
       logger.error('Cannot search the videos.')
index b63091910e37a50e38801b28236c2e4fe4b2a010..a109aba47aae22c969575103b58050ea9305cf42 100644 (file)
@@ -130,6 +130,32 @@ describe('Test parameters validator', function () {
   describe('Of the videos API', function () {
     const path = '/api/v1/videos/'
 
+    describe('When listing a video', function () {
+      it('Should fail with a bad start pagination', function (done) {
+        request(server.url)
+          .get(path)
+          .query({ start: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
+
+      it('Should fail with a bad count pagination', function (done) {
+        request(server.url)
+          .get(path)
+          .query({ count: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
+
+      it('Should fail with an incorrect sort', function (done) {
+        request(server.url)
+          .get(path)
+          .query({ sort: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
+    })
+
     describe('When searching a video', function () {
       it('Should fail with nothing', function (done) {
         request(server.url)
@@ -137,6 +163,30 @@ describe('Test parameters validator', function () {
           .set('Accept', 'application/json')
           .expect(400, done)
       })
+
+      it('Should fail with a bad start pagination', function (done) {
+        request(server.url)
+          .get(pathUtils.join(path, 'search', 'test'))
+          .query({ start: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
+
+      it('Should fail with a bad count pagination', function (done) {
+        request(server.url)
+          .get(pathUtils.join(path, 'search', 'test'))
+          .query({ count: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
+
+      it('Should fail with an incorrect sort', function (done) {
+        request(server.url)
+          .get(pathUtils.join(path, 'search', 'test'))
+          .query({ sort: 'hello' })
+          .set('Accept', 'application/json')
+          .expect(400, done)
+      })
     })
 
     describe('When adding a video', function () {
index 72002b6313eeaa6d8f7bb4d6608789dc463876fd..1a53ada3ace5d15256793a51f12ba202f5536dc6 100644 (file)
@@ -314,6 +314,39 @@ describe('Test a single pod', function () {
     })
   })
 
+  it('Should list and sort by name in descending order', function (done) {
+    utils.getVideosListSort(server.url, '-name', function (err, res) {
+      if (err) throw err
+
+      const videos = res.body
+      expect(videos.length).to.equal(6)
+      expect(videos[5].name === 'video_short.mp4 name')
+      expect(videos[4].name === 'video_short.ogv name')
+      expect(videos[3].name === 'video_short.webm name')
+      expect(videos[2].name === 'video_short1.webm name')
+      expect(videos[1].name === 'video_short2.webm name')
+      expect(videos[0].name === 'video_short3.webm name')
+
+      done()
+    })
+  })
+
+  it('Should search and sort by name in ascending order', function (done) {
+    utils.searchVideoWithSort(server.url, 'webm', 'name', function (err, res) {
+      if (err) throw err
+
+      const videos = res.body
+      expect(videos.length).to.equal(4)
+
+      expect(videos[0].name === 'video_short.webm name')
+      expect(videos[1].name === 'video_short1.webm name')
+      expect(videos[2].name === 'video_short2.webm name')
+      expect(videos[3].name === 'video_short3.webm name')
+
+      done()
+    })
+  })
+
   after(function (done) {
     process.kill(-server.app.pid)
     process.kill(-webtorrent.app.pid)
index d505cb5d9d5728e0a1a5ca913551f887ef0c8659..ac43946cda82c54082fce2831fda818a2b074778 100644 (file)
@@ -14,6 +14,7 @@ const testUtils = {
   getVideo: getVideo,
   getVideosList: getVideosList,
   getVideosListPagination: getVideosListPagination,
+  getVideosListSort: getVideosListSort,
   login: login,
   loginAndGetAccessToken: loginAndGetAccessToken,
   makeFriends: makeFriends,
@@ -23,6 +24,7 @@ const testUtils = {
   runServer: runServer,
   searchVideo: searchVideo,
   searchVideoWithPagination: searchVideoWithPagination,
+  searchVideoWithSort: searchVideoWithSort,
   testImage: testImage,
   uploadVideo: uploadVideo
 }
@@ -89,6 +91,18 @@ function getVideosListPagination (url, start, count, end) {
     .end(end)
 }
 
+function getVideosListSort (url, sort, end) {
+  const path = '/api/v1/videos'
+
+  request(url)
+    .get(path)
+    .query({ sort: sort })
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+    .end(end)
+}
+
 function login (url, client, user, expectedStatus, end) {
   if (!end) {
     end = expectedStatus
@@ -300,6 +314,18 @@ function searchVideoWithPagination (url, search, start, count, end) {
     .end(end)
 }
 
+function searchVideoWithSort (url, search, sort, end) {
+  const path = '/api/v1/videos'
+
+  request(url)
+    .get(path + '/search/' + search)
+    .query({ sort: sort })
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+    .end(end)
+}
+
 function testImage (url, videoName, imagePath, callback) {
   request(url)
     .get(imagePath)