Server: add ability to update a video
authorChocobozzz <florian.bigard@gmail.com>
Thu, 29 Dec 2016 18:07:05 +0000 (19:07 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Thu, 29 Dec 2016 18:07:05 +0000 (19:07 +0100)
server/controllers/api/videos.js
server/lib/friends.js
server/middlewares/validators/videos.js
server/models/video.js
server/tests/api/check-params.js
server/tests/api/single-pod.js
server/tests/utils/videos.js

index f29edac743f11ca8b62ef41b2fa5acf8609a666b..1b306d1cf038644f09f77adcacb37c35e14f9f13 100644 (file)
@@ -50,6 +50,12 @@ router.get('/',
   pagination.setPagination,
   listVideos
 )
+router.put('/:id',
+  oAuth.authenticate,
+  reqFiles,
+  validatorsVideos.videosUpdate,
+  updateVideo
+)
 router.post('/',
   oAuth.authenticate,
   reqFiles,
@@ -165,7 +171,7 @@ function addVideo (req, res, next) {
     },
 
     function sendToFriends (t, video, callback) {
-      video.toRemoteJSON(function (err, remoteVideo) {
+      video.toAddRemoteJSON(function (err, remoteVideo) {
         if (err) return callback(err)
 
         // Now we'll add the video's meta data to our friends
@@ -193,6 +199,83 @@ function addVideo (req, res, next) {
   })
 }
 
+function updateVideo (req, res, next) {
+  let videoInstance = res.locals.video
+  const videoInfosToUpdate = req.body
+
+  waterfall([
+
+    function startTransaction (callback) {
+      db.sequelize.transaction().asCallback(function (err, t) {
+        return callback(err, t)
+      })
+    },
+
+    function findOrCreateTags (t, callback) {
+      if (videoInfosToUpdate.tags) {
+        db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
+          return callback(err, t, tagInstances)
+        })
+      } else {
+        return callback(null, t, null)
+      }
+    },
+
+    function updateVideoIntoDB (t, tagInstances, callback) {
+      const options = { transaction: t }
+
+      if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name)
+      if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description)
+
+      // Add tags association
+      videoInstance.save(options).asCallback(function (err) {
+        if (err) return callback(err)
+
+        return callback(err, t, tagInstances)
+      })
+    },
+
+    function associateTagsToVideo (t, tagInstances, callback) {
+      if (tagInstances) {
+        const options = { transaction: t }
+
+        videoInstance.setTags(tagInstances, options).asCallback(function (err) {
+          videoInstance.Tags = tagInstances
+
+          return callback(err, t)
+        })
+      } else {
+        return callback(null, t)
+      }
+    },
+
+    function sendToFriends (t, callback) {
+      const json = videoInstance.toUpdateRemoteJSON()
+
+      // Now we'll update the video's meta data to our friends
+      friends.updateVideoToFriends(json)
+
+      return callback(null, t)
+    }
+
+  ], function andFinally (err, t) {
+    if (err) {
+      logger.error('Cannot insert the video.')
+
+      // Abort transaction?
+      if (t) t.rollback()
+
+      return next(err)
+    }
+
+    // Commit transaction
+    t.commit()
+
+    // TODO : include Location of the new video -> 201
+    return res.type('json').status(204).end()
+  })
+}
+
 function getVideo (req, res, next) {
   db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) {
     if (err) return next(err)
index ad9e4fdaef8d3da067b74b61cae55ed2999808f8..589b79660579af21660349ebdbcd8fc3ab1dfaf0 100644 (file)
@@ -14,6 +14,7 @@ const requests = require('../helpers/requests')
 
 const friends = {
   addVideoToFriends,
+  updateVideoToFriends,
   hasFriends,
   getMyCertificate,
   makeFriends,
@@ -26,6 +27,10 @@ function addVideoToFriends (video) {
   createRequest('add', constants.REQUEST_ENDPOINTS.VIDEOS, video)
 }
 
+function updateVideoToFriends (video) {
+  createRequest('update', constants.REQUEST_ENDPOINTS.VIDEOS, video)
+}
+
 function hasFriends (callback) {
   db.Pod.countAll(function (err, count) {
     if (err) return callback(err)
@@ -127,7 +132,7 @@ function sendOwnedVideosToPod (podId) {
     }
 
     videosList.forEach(function (video) {
-      video.toRemoteJSON(function (err, remoteVideo) {
+      video.toAddRemoteJSON(function (err, remoteVideo) {
         if (err) {
           logger.error('Cannot convert video to remote.', { error: err })
           // Don't break the process
index 7e90ca047f16c66bfb4f55cda3290afc857ba7e7..09a188c765ae3dac558601e2e1b0b5582ea602f0 100644 (file)
@@ -8,6 +8,7 @@ const logger = require('../../helpers/logger')
 
 const validatorsVideos = {
   videosAdd,
+  videosUpdate,
   videosGet,
   videosRemove,
   videosSearch
@@ -41,22 +42,26 @@ function videosAdd (req, res, next) {
   })
 }
 
-function videosGet (req, res, next) {
+function videosUpdate (req, res, next) {
   req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid()
+  req.checkBody('description', 'Should have a valid description').optional().isVideoDescriptionValid()
+  req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid()
 
-  logger.debug('Checking videosGet parameters', { parameters: req.params })
+  logger.debug('Checking videosUpdate parameters', { parameters: req.body })
 
   checkErrors(req, res, function () {
-    db.Video.load(req.params.id, function (err, video) {
-      if (err) {
-        logger.error('Error in videosGet request validator.', { error: err })
-        return res.sendStatus(500)
-      }
+    checkVideoExists(req.params.id, res, next)
+  })
+}
 
-      if (!video) return res.status(404).send('Video not found')
+function videosGet (req, res, next) {
+  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
 
-      next()
-    })
+  logger.debug('Checking videosGet parameters', { parameters: req.params })
+
+  checkErrors(req, res, function () {
+    checkVideoExists(req.params.id, res, next)
   })
 }
 
@@ -94,3 +99,19 @@ function videosSearch (req, res, next) {
 // ---------------------------------------------------------------------------
 
 module.exports = validatorsVideos
+
+// ---------------------------------------------------------------------------
+
+function checkVideoExists (id, res, callback) {
+  db.Video.loadAndPopulateAuthorAndPodAndTags(id, function (err, video) {
+    if (err) {
+      logger.error('Error in video request validator.', { error: err })
+      return res.sendStatus(500)
+    }
+
+    if (!video) return res.status(404).send('Video not found')
+
+    res.locals.video = video
+    callback()
+  })
+}
index 0e84e89867f722d88e0c6d236986b4faa6646d15..14fbe2f719ab2ca79dafdf28b3d2235217ce52e7 100644 (file)
@@ -127,7 +127,8 @@ module.exports = function (sequelize, DataTypes) {
         getTorrentName,
         isOwned,
         toFormatedJSON,
-        toRemoteJSON
+        toAddRemoteJSON,
+        toUpdateRemoteJSON
       },
       hooks: {
         beforeValidate,
@@ -334,7 +335,7 @@ function toFormatedJSON () {
   return json
 }
 
-function toRemoteJSON (callback) {
+function toAddRemoteJSON (callback) {
   const self = this
 
   // Get thumbnail data to send to the other pod
@@ -362,6 +363,22 @@ function toRemoteJSON (callback) {
   })
 }
 
+function toUpdateRemoteJSON (callback) {
+  const json = {
+    name: this.name,
+    description: this.description,
+    infoHash: this.infoHash,
+    remoteId: this.id,
+    author: this.Author.name,
+    duration: this.duration,
+    tags: map(this.Tags, 'name'),
+    createdAt: this.createdAt,
+    extname: this.extname
+  }
+
+  return json
+}
+
 // ------------------------------ STATICS ------------------------------
 
 function generateThumbnailFromData (video, thumbnailData, callback) {
index 9aecc372077c69daab3fd3640d14c18e3788e2c9..e8f2aa82110a21746a916359c7995160b1cf8169 100644 (file)
@@ -10,6 +10,7 @@ const loginUtils = require('../utils/login')
 const requestsUtils = require('../utils/requests')
 const serversUtils = require('../utils/servers')
 const usersUtils = require('../utils/users')
+const videosUtils = require('../utils/videos')
 
 describe('Test parameters validator', function () {
   let server = null
@@ -439,6 +440,106 @@ describe('Test parameters validator', function () {
       })
     })
 
+    describe('When updating a video', function () {
+      let videoId
+
+      before(function (done) {
+        videosUtils.getVideosList(server.url, function (err, res) {
+          if (err) throw err
+
+          videoId = res.body.data[0].id
+
+          return done()
+        })
+      })
+
+      it('Should fail with nothing', function (done) {
+        const data = {}
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail without a valid uuid', function (done) {
+        const data = {
+          description: 'my super description',
+          tags: [ 'tag1', 'tag2' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + 'blabla', server.accessToken, data, done)
+      })
+
+      it('Should fail with an unknown id', function (done) {
+        const data = {
+          description: 'my super description',
+          tags: [ 'tag1', 'tag2' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + '4da6fde3-88f7-4d16-b119-108df5630b06', server.accessToken, data, done)
+      })
+
+      it('Should fail with a long name', function (done) {
+        const data = {
+          name: 'My very very very very very very very very very very very very very very very very long name',
+          description: 'my super description',
+          tags: [ 'tag1', 'tag2' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with a long description', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description which is very very very very very very very very very very very very very very' +
+                       'very very very very very very very very very very very very very very very very very very very very very' +
+                       'very very very very very very very very very very very very very very very long',
+          tags: [ 'tag1', 'tag2' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with too many tags', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description',
+          tags: [ 'tag1', 'tag2', 'tag3', 'tag4' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with not enough tags', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description',
+          tags: [ ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with a tag length too low', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description',
+          tags: [ 'tag1', 't' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with a tag length too big', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description',
+          tags: [ 'mysupertagtoolong', 'tag1' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+
+      it('Should fail with malformed tags', function (done) {
+        const data = {
+          name: 'my super name',
+          description: 'my super description',
+          tags: [ 'my tag' ]
+        }
+        requestsUtils.makePutBodyRequest(server.url, path + videoId, server.accessToken, data, done)
+      })
+    })
+
     describe('When getting a video', function () {
       it('Should return the list of the videos with nothing', function (done) {
         request(server.url)
index 66b762f82a7766f7bbc2eadbfc29a5e098eb8a10..57146900de47cb6994fe85709518c855f2bc29c7 100644 (file)
@@ -495,10 +495,86 @@ describe('Test a single pod', function () {
       expect(videos[2].name === 'video_short2.webm name')
       expect(videos[3].name === 'video_short3.webm name')
 
+      videoId = videos[3].id
+
       done()
     })
   })
 
+  it('Should update a video', function (done) {
+    const name = 'my super video updated'
+    const description = 'my super description updated'
+    const tags = [ 'tagup1', 'tagup2' ]
+
+    videosUtils.updateVideo(server.url, server.accessToken, videoId, name, description, tags, done)
+  })
+
+  it('Should have the video updated', function (done) {
+    videosUtils.getVideo(server.url, videoId, function (err, res) {
+      if (err) throw err
+
+      const video = res.body
+
+      expect(video.name).to.equal('my super video updated')
+      expect(video.description).to.equal('my super description updated')
+      expect(video.podHost).to.equal('localhost:9001')
+      expect(video.author).to.equal('root')
+      expect(video.isLocal).to.be.true
+      expect(video.tags).to.deep.equal([ 'tagup1', 'tagup2' ])
+      expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
+
+      done()
+    })
+  })
+
+  it('Should update only the tags of a video', function (done) {
+    const tags = [ 'tag1', 'tag2', 'supertag' ]
+
+    videosUtils.updateVideo(server.url, server.accessToken, videoId, null, null, tags, function (err) {
+      if (err) throw err
+
+      videosUtils.getVideo(server.url, videoId, function (err, res) {
+        if (err) throw err
+
+        const video = res.body
+
+        expect(video.name).to.equal('my super video updated')
+        expect(video.description).to.equal('my super description updated')
+        expect(video.podHost).to.equal('localhost:9001')
+        expect(video.author).to.equal('root')
+        expect(video.isLocal).to.be.true
+        expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'supertag' ])
+        expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
+
+        done()
+      })
+    })
+  })
+
+  it('Should update only the description of a video', function (done) {
+    const description = 'hello everybody'
+
+    videosUtils.updateVideo(server.url, server.accessToken, videoId, null, description, null, function (err) {
+      if (err) throw err
+
+      videosUtils.getVideo(server.url, videoId, function (err, res) {
+        if (err) throw err
+
+        const video = res.body
+
+        expect(video.name).to.equal('my super video updated')
+        expect(video.description).to.equal('hello everybody')
+        expect(video.podHost).to.equal('localhost:9001')
+        expect(video.author).to.equal('root')
+        expect(video.isLocal).to.be.true
+        expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'supertag' ])
+        expect(miscsUtils.dateIsValid(video.createdAt)).to.be.true
+
+        done()
+      })
+    })
+  })
+
   after(function (done) {
     process.kill(-server.app.pid)
 
index 5c120597f0ac1dc762a196259139d945c8706fd9..beafd3cf559ec3b737c0a50a3815f2367817a5db 100644 (file)
@@ -15,7 +15,8 @@ const videosUtils = {
   searchVideoWithPagination,
   searchVideoWithSort,
   testVideoImage,
-  uploadVideo
+  uploadVideo,
+  updateVideo
 }
 
 // ---------------------- Export functions --------------------
@@ -194,6 +195,31 @@ function uploadVideo (url, accessToken, name, description, tags, fixture, specia
      .end(end)
 }
 
+function updateVideo (url, accessToken, id, name, description, tags, specialStatus, end) {
+  if (!end) {
+    end = specialStatus
+    specialStatus = 204
+  }
+
+  const path = '/api/v1/videos/' + id
+
+  const req = request(url)
+              .put(path)
+              .set('Accept', 'application/json')
+              .set('Authorization', 'Bearer ' + accessToken)
+
+  if (name) req.field('name', name)
+  if (description) req.field('description', description)
+
+  if (tags) {
+    for (let i = 0; i < tags.length; i++) {
+      req.field('tags[' + i + ']', tags[i])
+    }
+  }
+
+  req.expect(specialStatus).end(end)
+}
+
 // ---------------------------------------------------------------------------
 
 module.exports = videosUtils