Move to promises
authorChocobozzz <florian.bigard@gmail.com>
Wed, 5 Jul 2017 11:26:25 +0000 (13:26 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Wed, 5 Jul 2017 12:14:16 +0000 (14:14 +0200)
Closes https://github.com/Chocobozzz/PeerTube/issues/74

88 files changed:
package.json
scripts/danger/clean/cleaner.ts
scripts/reset-password.ts
scripts/test.sh
scripts/update-host.ts
server.ts
server/controllers/api/oauth-clients.ts
server/controllers/api/pods.ts
server/controllers/api/remote/pods.ts
server/controllers/api/remote/videos.ts
server/controllers/api/request-schedulers.ts
server/controllers/api/users.ts
server/controllers/api/videos/abuse.ts
server/controllers/api/videos/blacklist.ts
server/controllers/api/videos/index.ts
server/controllers/api/videos/rate.ts
server/controllers/client.ts
server/helpers/core-utils.ts
server/helpers/database-utils.ts
server/helpers/peertube-crypto.ts
server/helpers/requests.ts
server/helpers/utils.ts
server/initializers/checker.ts
server/initializers/database.ts
server/initializers/installer.ts
server/initializers/migrations/0005-email-pod.ts
server/initializers/migrations/0010-email-user.ts
server/initializers/migrations/0015-video-views.ts
server/initializers/migrations/0020-video-likes.ts
server/initializers/migrations/0025-video-dislikes.ts
server/initializers/migrations/0030-video-category.ts
server/initializers/migrations/0035-video-licence.ts
server/initializers/migrations/0040-video-nsfw.ts
server/initializers/migrations/0045-user-display-nsfw.ts
server/initializers/migrations/0050-video-language.ts
server/initializers/migrator.ts
server/lib/friends.ts
server/lib/jobs/handlers/index.ts
server/lib/jobs/handlers/video-transcoder.ts
server/lib/jobs/job-scheduler.ts
server/lib/oauth-model.ts
server/lib/request/abstract-request-scheduler.ts
server/lib/request/request-scheduler.ts
server/lib/request/request-video-event-scheduler.ts
server/lib/request/request-video-qadu-scheduler.ts
server/middlewares/secure.ts
server/middlewares/validators/pods.ts
server/middlewares/validators/users.ts
server/middlewares/validators/videos.ts
server/models/application/application-interface.ts
server/models/application/application.ts
server/models/job/job-interface.ts
server/models/job/job.ts
server/models/oauth/oauth-client-interface.ts
server/models/oauth/oauth-client.ts
server/models/oauth/oauth-token-interface.ts
server/models/oauth/oauth-token.ts
server/models/pod/pod-interface.ts
server/models/pod/pod.ts
server/models/request/abstract-request-interface.ts [new file with mode: 0644]
server/models/request/index.ts
server/models/request/request-interface.ts
server/models/request/request-to-pod-interface.ts
server/models/request/request-to-pod.ts
server/models/request/request-video-event-interface.ts
server/models/request/request-video-event.ts
server/models/request/request-video-qadu-interface.ts
server/models/request/request-video-qadu.ts
server/models/request/request.ts
server/models/user/user-interface.ts
server/models/user/user-video-rate-interface.ts
server/models/user/user-video-rate.ts
server/models/user/user.ts
server/models/video/author-interface.ts
server/models/video/author.ts
server/models/video/tag-interface.ts
server/models/video/tag.ts
server/models/video/video-abuse-interface.ts
server/models/video/video-abuse.ts
server/models/video/video-blacklist-interface.ts
server/models/video/video-blacklist.ts
server/models/video/video-interface.ts
server/models/video/video-tag.ts
server/models/video/video.ts
server/tests/api/friends-advanced.js
shared/models/index.ts
shared/models/result-list.model.ts [new file with mode: 0644]
yarn.lock

index 93193fe2f3fe0077bd7e0203535515b95f9fe8cb..b3ea40ad307f72a2aae0d774abc23e153b62e9db 100644 (file)
@@ -49,6 +49,7 @@
     "async": "^2.0.0",
     "bcrypt": "^1.0.2",
     "bittorrent-tracker": "^9.0.0",
+    "bluebird": "^3.5.0",
     "body-parser": "^1.12.4",
     "concurrently": "^3.1.0",
     "config": "^1.14.0",
@@ -78,7 +79,7 @@
     "scripty": "^1.5.0",
     "sequelize": "4.0.0-2",
     "ts-node": "^3.0.6",
-    "typescript": "^2.3.4",
+    "typescript": "^2.4.1",
     "validator": "^7.0.0",
     "winston": "^2.1.1",
     "ws": "^2.0.0"
@@ -95,7 +96,7 @@
     "@types/mkdirp": "^0.3.29",
     "@types/morgan": "^1.7.32",
     "@types/multer": "^0.0.34",
-    "@types/node": "^7.0.18",
+    "@types/node": "^8.0.3",
     "@types/request": "^0.0.44",
     "@types/sequelize": "^4.0.55",
     "@types/validator": "^6.2.0",
index d1dba46509ef3826930be076df224ec0c8f921d9..d80b8d3237ce8fc11fac916f56876a8c247359ba 100644 (file)
@@ -1,25 +1,28 @@
-import * as eachSeries from 'async/eachSeries'
 import * as rimraf from 'rimraf'
+import * as Promise from 'bluebird'
 
 import { CONFIG } from '../../../server/initializers/constants'
 import { database as db } from '../../../server/initializers/database'
 
-db.init(true, function () {
-  db.sequelize.drop().asCallback(function (err) {
-    if (err) throw err
-
+db.init(true)
+  .then(() => {
+    return db.sequelize.drop()
+  })
+  .then(() => {
     console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
 
     const STORAGE = CONFIG.STORAGE
-    eachSeries(Object.keys(STORAGE), function (storage, callbackEach) {
+    Promise.mapSeries(Object.keys(STORAGE), storage => {
       const storageDir = STORAGE[storage]
 
-      rimraf(storageDir, function (err) {
-        console.info('%s deleted.', storageDir)
-        return callbackEach(err)
+      return new Promise((res, rej) => {
+        rimraf(storageDir, function (err) {
+          if (err) return rej(err)
+
+          console.info('%s deleted.', storageDir)
+          return res()
+        })
       })
-    }, function () {
-      process.exit(0)
     })
+    .then(() => process.exit(0))
   })
-})
index 50e11c69c0195fbe4b7d446e7c0864d0ae5e30d2..09f27bfa49b043fb406c57b872caa9322a584e7a 100755 (executable)
@@ -11,13 +11,11 @@ if (program.user === undefined) {
   process.exit(-1)
 }
 
-db.init(true, function () {
-  db.User.loadByUsername(program.user, function (err, user) {
-    if (err) {
-      console.error(err)
-      return
-    }
-
+db.init(true)
+  .then(() => {
+    return db.User.loadByUsername(program.user)
+  })
+  .then(user => {
     if (!user) {
       console.error('User unknown.')
       return
@@ -40,15 +38,9 @@ db.init(true, function () {
     rl.on('line', function (password) {
       user.password = password
 
-      user.save().asCallback(function (err) {
-        if (err) {
-          console.error(err)
-        } else {
-          console.log('User password updated.')
-        }
-
-        process.exit(0)
-      })
+      user.save()
+        .then(() => console.log('User password updated.'))
+        .catch(err => console.error(err))
+        .finally(() => process.exit(0))
     })
   })
-})
index 6e8e38659ea67f6557212e8526547cff35747e14..6c6312d5268009446417f9ac3c3d97184bcbdd26 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env sh
+#!/bin/bash
 
 npm run build:server
 
@@ -6,5 +6,5 @@ cd client || exit -1
 npm test || exit -1
 
 cd .. || exit -1
-npm run tslint -- --type-check --project ./tsconfig.json -c ./tslint.json server.ts server/**/*.ts || exit -1
+npm run tslint -- --type-check --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" || exit -1
 mocha --bail server/tests
index 0f6af094280eba035ef948d8d910f00abe4b82c6..23e8d5ef35ca7234ba723b044885be4cfa551e13 100755 (executable)
@@ -5,33 +5,32 @@ import { CONFIG, STATIC_PATHS } from '../server/initializers/constants'
 import { database as db } from '../server/initializers/database'
 import { hasFriends } from '../server/lib/friends'
 
-db.init(true, function () {
-  hasFriends(function (err, itHasFriends) {
-    if (err) throw err
-
+db.init(true)
+  .then(() => {
+    return hasFriends()
+  })
+  .then(itHasFriends => {
     if (itHasFriends === true) {
       console.log('Cannot update host because you have friends!')
       process.exit(-1)
     }
 
     console.log('Updating torrent files.')
-    db.Video.list(function (err, videos) {
-      if (err) throw err
-
-      videos.forEach(function (video) {
-        const torrentName = video.id + '.torrent'
-        const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
-        const filename = video.id + video.extname
-
-        const parsed = parseTorrent(readFileSync(torrentPath))
-        parsed.announce = [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOST + '/tracker/socket' ]
-        parsed.urlList = [ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + filename ]
-
-        const buf = parseTorrent.toTorrentFile(parsed)
-        writeFileSync(torrentPath, buf)
-      })
-
-      process.exit(0)
+    return db.Video.list()
+  })
+  .then(videos => {
+    videos.forEach(function (video) {
+      const torrentName = video.id + '.torrent'
+      const torrentPath = CONFIG.STORAGE.TORRENTS_DIR + torrentName
+      const filename = video.id + video.extname
+
+      const parsed = parseTorrent(readFileSync(torrentPath))
+      parsed.announce = [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOST + '/tracker/socket' ]
+      parsed.urlList = [ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + filename ]
+
+      const buf = parseTorrent.toTorrentFile(parsed)
+      writeFileSync(torrentPath, buf)
     })
+
+    process.exit(0)
   })
-})
index 60045865d12e3ad6645eea0a4344244028d89571..e7fa99c90d49f4965a1b315c3ba0438c156c1f58 100644 (file)
--- a/server.ts
+++ b/server.ts
@@ -29,7 +29,7 @@ import { logger } from './server/helpers/logger'
 import { API_VERSION, CONFIG } from './server/initializers/constants'
 // Initialize database and models
 import { database as db } from './server/initializers/database'
-db.init(false, onDatabaseInitDone)
+db.init(false).then(() => onDatabaseInitDone())
 
 // ----------- Checker -----------
 import { checkMissedConfig, checkFFmpeg, checkConfig } from './server/initializers/checker'
@@ -38,11 +38,7 @@ const missed = checkMissedConfig()
 if (missed.length !== 0) {
   throw new Error('Miss some configurations keys : ' + missed)
 }
-checkFFmpeg(function (err) {
-  if (err) {
-    throw err
-  }
-})
+checkFFmpeg()
 
 const errorMessage = checkConfig()
 if (errorMessage !== null) {
@@ -138,12 +134,11 @@ app.use(function (err, req, res, next) {
 function onDatabaseInitDone () {
   const port = CONFIG.LISTEN.PORT
     // Run the migration scripts if needed
-  migrate(function (err) {
-    if (err) throw err
-
-    installApplication(function (err) {
-      if (err) throw err
-
+  migrate()
+    .then(() => {
+      return installApplication()
+    })
+    .then(() => {
       // ----------- Make the server listening -----------
       server.listen(port, function () {
         // Activate the communication with friends
@@ -156,5 +151,4 @@ function onDatabaseInitDone () {
         logger.info('Webserver: %s', CONFIG.WEBSERVER.URL)
       })
     })
-  })
 }
index b9bc0534f24db865741f703dd917472e74e361d5..f7dac598c3d62770049ca656bc27f8d35c9973a0 100644 (file)
@@ -24,16 +24,17 @@ function getLocalClient (req: express.Request, res: express.Response, next: expr
     return res.type('json').status(403).end()
   }
 
-  db.OAuthClient.loadFirstClient(function (err, client) {
-    if (err) return next(err)
-    if (!client) return next(new Error('No client available.'))
-
-    const json: OAuthClientLocal = {
-      client_id: client.clientId,
-      client_secret: client.clientSecret
-    }
-    res.json(json)
-  })
+  db.OAuthClient.loadFirstClient()
+    .then(client => {
+      if (!client) throw new Error('No client available.')
+
+      const json: OAuthClientLocal = {
+        client_id: client.clientId,
+        client_secret: client.clientSecret
+      }
+      res.json(json)
+    })
+    .catch(err => next(err))
 }
 
 // ---------------------------------------------------------------------------
index 283105f6c39c8d4fac140167c2917016ae7b37c9..0f85ab51d9c7235e9acb45ee45a450dd3bf63bd0 100644 (file)
@@ -1,5 +1,4 @@
 import * as express from 'express'
-import { waterfall } from 'async'
 
 import { database as db } from '../../initializers/database'
 import { CONFIG } from '../../initializers'
@@ -57,65 +56,39 @@ export {
 function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
   const informations = req.body
 
-  waterfall<string, Error>([
-    function addPod (callback) {
-      const pod = db.Pod.build(informations)
-      pod.save().asCallback(function (err, podCreated) {
-        // Be sure about the number of parameters for the callback
-        return callback(err, podCreated)
-      })
-    },
-
-    function sendMyVideos (podCreated: PodInstance, callback) {
-      sendOwnedVideosToPod(podCreated.id)
-
-      callback(null)
-    },
-
-    function fetchMyCertificate (callback) {
-      getMyPublicCert(function (err, cert) {
-        if (err) {
-          logger.error('Cannot read cert file.')
-          return callback(err)
-        }
-
-        return callback(null, cert)
-      })
-    }
-  ], function (err, cert) {
-    if (err) return next(err)
-
-    return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
-  })
+  const pod = db.Pod.build(informations)
+  pod.save()
+    .then(podCreated => {
+      return sendOwnedVideosToPod(podCreated.id)
+    })
+    .then(() => {
+      return getMyPublicCert()
+    })
+    .then(cert => {
+      return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
+    })
+    .catch(err => next(err))
 }
 
 function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.Pod.list(function (err, podsList) {
-    if (err) return next(err)
-
-    res.json(getFormatedObjects(podsList, podsList.length))
-  })
+  db.Pod.list()
+    .then(podsList => res.json(getFormatedObjects(podsList, podsList.length)))
+    .catch(err => next(err))
 }
 
 function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
   const hosts = req.body.hosts as string[]
 
-  makeFriends(hosts, function (err) {
-    if (err) {
-      logger.error('Could not make friends.', { error: err })
-      return
-    }
-
-    logger.info('Made friends!')
-  })
+  makeFriends(hosts)
+    .then(() => logger.info('Made friends!'))
+    .catch(err => logger.error('Could not make friends.', { error: err }))
 
+  // Don't wait the process that could be long
   res.type('json').status(204).end()
 }
 
 function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
-  quitFriends(function (err) {
-    if (err) return next(err)
-
-    res.type('json').status(204).end()
-  })
+  quitFriends()
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
 }
index b0d6642c13bfe71edbad706b5b8fd15ee8c7ed4c..6319957d3b7744f5c2e499f516e98398b728bad8 100644 (file)
@@ -1,5 +1,4 @@
 import * as express from 'express'
-import * as waterfall from 'async/waterfall'
 
 import { database as db } from '../../../initializers/database'
 import { checkSignature, signatureValidator } from '../../../middlewares'
@@ -24,17 +23,10 @@ export {
 function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
   const host = req.body.signature.host
 
-  waterfall([
-    function loadPod (callback) {
-      db.Pod.loadByHost(host, callback)
-    },
-
-    function deletePod (pod, callback) {
-      pod.destroy().asCallback(callback)
-    }
-  ], function (err) {
-    if (err) return next(err)
-
-    return res.type('json').status(204).end()
-  })
+  db.Pod.loadByHost(host)
+    .then(pod => {
+      return pod.destroy()
+    })
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
 }
index d9cc08fb41710c13b92d18aaf07b808eb064679f..ebe4eca365c1fffcfe6e9b87d0e7734878048f01 100644 (file)
@@ -1,6 +1,5 @@
 import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { eachSeries, waterfall } from 'async'
+import * as Promise from 'bluebird'
 
 import { database as db } from '../../../initializers/database'
 import {
@@ -16,20 +15,14 @@ import {
   remoteQaduVideosValidator,
   remoteEventsVideosValidator
 } from '../../../middlewares'
-import {
-  logger,
-  commitTransaction,
-  retryTransactionWrapper,
-  rollbackTransaction,
-  startSerializableTransaction
-} from '../../../helpers'
+import { logger, retryTransactionWrapper } from '../../../helpers'
 import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
 import { PodInstance, VideoInstance } from '../../../models'
 
 const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
 
 // Functions to call when processing a remote request
-const functionsHash = {}
+const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
 functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper
 functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
 functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
@@ -72,20 +65,19 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
 
   // We need to process in the same order to keep consistency
   // TODO: optimization
-  eachSeries(requests, function (request: any, callbackEach) {
+  Promise.mapSeries(requests, (request: any) => {
     const data = request.data
 
     // Get the function we need to call in order to process the request
     const fun = functionsHash[request.type]
     if (fun === undefined) {
       logger.error('Unkown remote request type %s.', request.type)
-      return callbackEach(null)
+      return
     }
 
-    fun.call(this, data, fromPod, callbackEach)
-  }, function (err) {
-    if (err) logger.error('Error managing remote videos.', { error: err })
+    return fun.call(this, data, fromPod)
   })
+  .catch(err => logger.error('Error managing remote videos.', { error: err }))
 
   // We don't need to keep the other pod waiting
   return res.type('json').status(204).end()
@@ -95,13 +87,12 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
   const requests = req.body.data
   const fromPod = res.locals.secure.pod
 
-  eachSeries(requests, function (request: any, callbackEach) {
+  Promise.mapSeries(requests, (request: any) => {
     const videoData = request.data
 
-    quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod, callbackEach)
-  }, function (err) {
-    if (err) logger.error('Error managing remote videos.', { error: err })
+    return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
   })
+  .catch(err => logger.error('Error managing remote videos.', { error: err }))
 
   return res.type('json').status(204).end()
 }
@@ -110,414 +101,303 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
   const requests = req.body.data
   const fromPod = res.locals.secure.pod
 
-  eachSeries(requests, function (request: any, callbackEach) {
+  Promise.mapSeries(requests, (request: any) => {
     const eventData = request.data
 
-    processVideosEventsRetryWrapper(eventData, fromPod, callbackEach)
-  }, function (err) {
-    if (err) logger.error('Error managing remote videos.', { error: err })
+    return processVideosEventsRetryWrapper(eventData, fromPod)
   })
+  .catch(err => logger.error('Error managing remote videos.', { error: err }))
 
   return res.type('json').status(204).end()
 }
 
-function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance) {
   const options = {
     arguments: [ eventData, fromPod ],
     errorMessage: 'Cannot process videos events with many retries.'
   }
 
-  retryTransactionWrapper(processVideosEvents, options, finalCallback)
+  return retryTransactionWrapper(processVideosEvents, options)
 }
 
-function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
-  waterfall([
-    startSerializableTransaction,
-
-    function findVideo (t, callback) {
-      fetchOwnedVideo(eventData.remoteId, function (err, videoInstance) {
-        return callback(err, t, videoInstance)
-      })
-    },
+function processVideosEvents (eventData: any, fromPod: PodInstance) {
 
-    function updateVideoIntoDB (t, videoInstance, callback) {
-      const options = { transaction: t }
+  return db.sequelize.transaction(t => {
+    return fetchOwnedVideo(eventData.remoteId)
+      .then(videoInstance => {
+        const options = { transaction: t }
 
-      let columnToUpdate
-      let qaduType
+        let columnToUpdate
+        let qaduType
 
-      switch (eventData.eventType) {
-        case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
-          columnToUpdate = 'views'
-          qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
-          break
+        switch (eventData.eventType) {
+          case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
+            columnToUpdate = 'views'
+            qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
+            break
 
-        case REQUEST_VIDEO_EVENT_TYPES.LIKES:
-          columnToUpdate = 'likes'
-          qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
-          break
+          case REQUEST_VIDEO_EVENT_TYPES.LIKES:
+            columnToUpdate = 'likes'
+            qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
+            break
 
-        case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
-          columnToUpdate = 'dislikes'
-          qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
-          break
+          case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
+            columnToUpdate = 'dislikes'
+            qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
+            break
 
-        default:
-          return callback(new Error('Unknown video event type.'))
-      }
+          default:
+            throw new Error('Unknown video event type.')
+        }
 
-      const query = {}
-      query[columnToUpdate] = eventData.count
+        const query = {}
+        query[columnToUpdate] = eventData.count
 
-      videoInstance.increment(query, options).asCallback(function (err) {
-        return callback(err, t, videoInstance, qaduType)
+        return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
       })
-    },
-
-    function sendQaduToFriends (t, videoInstance, qaduType, callback) {
-      const qadusParams = [
-        {
-          videoId: videoInstance.id,
-          type: qaduType
-        }
-      ]
-
-      quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
-        return callback(err, t)
+      .then(({ videoInstance, qaduType }) => {
+        const qadusParams = [
+          {
+            videoId: videoInstance.id,
+            type: qaduType
+          }
+        ]
+
+        return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
       })
-    },
-
-    commitTransaction
-
-  ], function (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      logger.debug('Cannot process a video event.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
-    logger.info('Remote video event processed for video %s.', eventData.remoteId)
-    return finalCallback(null)
+  })
+  .then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
+  .catch(err => {
+    logger.debug('Cannot process a video event.', { error: err })
+    throw err
   })
 }
 
-function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance) {
   const options = {
     arguments: [ videoData, fromPod ],
     errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
   }
 
-  retryTransactionWrapper(quickAndDirtyUpdateVideo, options, finalCallback)
+  return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
 }
 
-function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance) {
   let videoName
 
-  waterfall([
-    startSerializableTransaction,
-
-    function findVideo (t, callback) {
-      fetchRemoteVideo(fromPod.host, videoData.remoteId, function (err, videoInstance) {
-        return callback(err, t, videoInstance)
-      })
-    },
-
-    function updateVideoIntoDB (t, videoInstance, callback) {
-      const options = { transaction: t }
+  return db.sequelize.transaction(t => {
+    return fetchRemoteVideo(fromPod.host, videoData.remoteId)
+      .then(videoInstance => {
+        const options = { transaction: t }
 
-      videoName = videoInstance.name
+        videoName = videoInstance.name
 
-      if (videoData.views) {
-        videoInstance.set('views', videoData.views)
-      }
+        if (videoData.views) {
+          videoInstance.set('views', videoData.views)
+        }
 
-      if (videoData.likes) {
-        videoInstance.set('likes', videoData.likes)
-      }
+        if (videoData.likes) {
+          videoInstance.set('likes', videoData.likes)
+        }
 
-      if (videoData.dislikes) {
-        videoInstance.set('dislikes', videoData.dislikes)
-      }
+        if (videoData.dislikes) {
+          videoInstance.set('dislikes', videoData.dislikes)
+        }
 
-      videoInstance.save(options).asCallback(function (err) {
-        return callback(err, t)
+        return videoInstance.save(options)
       })
-    },
-
-    commitTransaction
-
-  ], function (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      logger.debug('Cannot quick and dirty update the remote video.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
-    logger.info('Remote video %s quick and dirty updated', videoName)
-    return finalCallback(null)
   })
+  .then(() => logger.info('Remote video %s quick and dirty updated', videoName))
+  .catch(err => logger.debug('Cannot quick and dirty update the remote video.', { error: err }))
 }
 
 // Handle retries on fail
-function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance) {
   const options = {
     arguments: [ videoToCreateData, fromPod ],
     errorMessage: 'Cannot insert the remote video with many retries.'
   }
 
-  retryTransactionWrapper(addRemoteVideo, options, finalCallback)
+  return retryTransactionWrapper(addRemoteVideo, options)
 }
 
-function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance) {
   logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
 
-  waterfall([
-
-    startSerializableTransaction,
-
-    function assertRemoteIdAndHostUnique (t, callback) {
-      db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId, function (err, video) {
-        if (err) return callback(err)
+  return db.sequelize.transaction(t => {
+    return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId)
+      .then(video => {
+        if (video) throw new Error('RemoteId and host pair is not unique.')
 
-        if (video) return callback(new Error('RemoteId and host pair is not unique.'))
-
-        return callback(null, t)
+        return undefined
       })
-    },
-
-    function findOrCreateAuthor (t, callback) {
-      const name = videoToCreateData.author
-      const podId = fromPod.id
-      // This author is from another pod so we do not associate a user
-      const userId = null
+      .then(() => {
+        const name = videoToCreateData.author
+        const podId = fromPod.id
+        // This author is from another pod so we do not associate a user
+        const userId = null
 
-      db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
-        return callback(err, t, authorInstance)
+        return db.Author.findOrCreateAuthor(name, podId, userId, t)
       })
-    },
+      .then(author => {
+        const tags = videoToCreateData.tags
 
-    function findOrCreateTags (t, author, callback) {
-      const tags = videoToCreateData.tags
-
-      db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
-        return callback(err, t, author, tagInstances)
+        return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
       })
-    },
-
-    function createVideoObject (t, author, tagInstances, callback) {
-      const videoData = {
-        name: videoToCreateData.name,
-        remoteId: videoToCreateData.remoteId,
-        extname: videoToCreateData.extname,
-        infoHash: videoToCreateData.infoHash,
-        category: videoToCreateData.category,
-        licence: videoToCreateData.licence,
-        language: videoToCreateData.language,
-        nsfw: videoToCreateData.nsfw,
-        description: videoToCreateData.description,
-        authorId: author.id,
-        duration: videoToCreateData.duration,
-        createdAt: videoToCreateData.createdAt,
-        // FIXME: updatedAt does not seems to be considered by Sequelize
-        updatedAt: videoToCreateData.updatedAt,
-        views: videoToCreateData.views,
-        likes: videoToCreateData.likes,
-        dislikes: videoToCreateData.dislikes
-      }
-
-      const video = db.Video.build(videoData)
-
-      return callback(null, t, tagInstances, video)
-    },
-
-    function generateThumbnail (t, tagInstances, video, callback) {
-      db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
-        if (err) {
-          logger.error('Cannot generate thumbnail from data.', { error: err })
-          return callback(err)
+      .then(({ author, tagInstances }) => {
+        const videoData = {
+          name: videoToCreateData.name,
+          remoteId: videoToCreateData.remoteId,
+          extname: videoToCreateData.extname,
+          infoHash: videoToCreateData.infoHash,
+          category: videoToCreateData.category,
+          licence: videoToCreateData.licence,
+          language: videoToCreateData.language,
+          nsfw: videoToCreateData.nsfw,
+          description: videoToCreateData.description,
+          authorId: author.id,
+          duration: videoToCreateData.duration,
+          createdAt: videoToCreateData.createdAt,
+          // FIXME: updatedAt does not seems to be considered by Sequelize
+          updatedAt: videoToCreateData.updatedAt,
+          views: videoToCreateData.views,
+          likes: videoToCreateData.likes,
+          dislikes: videoToCreateData.dislikes
         }
 
-        return callback(err, t, tagInstances, video)
+        const video = db.Video.build(videoData)
+        return { tagInstances, video }
       })
-    },
-
-    function insertVideoIntoDB (t, tagInstances, video, callback) {
-      const options = {
-        transaction: t
-      }
-
-      video.save(options).asCallback(function (err, videoCreated) {
-        return callback(err, t, tagInstances, videoCreated)
+      .then(({ tagInstances, video }) => {
+        return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
       })
-    },
-
-    function associateTagsToVideo (t, tagInstances, video, callback) {
-      const options = {
-        transaction: t
-      }
+      .then(({ tagInstances, video }) => {
+        const options = {
+          transaction: t
+        }
 
-      video.setTags(tagInstances, options).asCallback(function (err) {
-        return callback(err, t)
+        return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
       })
-    },
-
-    commitTransaction
-
-  ], function (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      // This is just a debug because we will retry the insert
-      logger.debug('Cannot insert the remote video.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
+      .then(({ tagInstances, videoCreated }) => {
+        const options = {
+          transaction: t
+        }
 
-    logger.info('Remote video %s inserted.', videoToCreateData.name)
-    return finalCallback(null)
+        return videoCreated.setTags(tagInstances, options)
+      })
+  })
+  .then(() => logger.info('Remote video %s inserted.', videoToCreateData.name))
+  .catch(err => {
+    logger.debug('Cannot insert the remote video.', { error: err })
+    throw err
   })
 }
 
 // Handle retries on fail
-function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance) {
   const options = {
     arguments: [ videoAttributesToUpdate, fromPod ],
     errorMessage: 'Cannot update the remote video with many retries'
   }
 
-  retryTransactionWrapper(updateRemoteVideo, options, finalCallback)
+  return retryTransactionWrapper(updateRemoteVideo, options)
 }
 
-function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
+function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance) {
   logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
 
-  waterfall([
+  return db.sequelize.transaction(t => {
+    return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
+      .then(videoInstance => {
+        const tags = videoAttributesToUpdate.tags
 
-    startSerializableTransaction,
-
-    function findVideo (t, callback) {
-      fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
-        return callback(err, t, videoInstance)
+        return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
       })
-    },
-
-    function findOrCreateTags (t, videoInstance, callback) {
-      const tags = videoAttributesToUpdate.tags
-
-      db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
-        return callback(err, t, videoInstance, tagInstances)
-      })
-    },
-
-    function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
-      const options = { transaction: t }
-
-      videoInstance.set('name', videoAttributesToUpdate.name)
-      videoInstance.set('category', videoAttributesToUpdate.category)
-      videoInstance.set('licence', videoAttributesToUpdate.licence)
-      videoInstance.set('language', videoAttributesToUpdate.language)
-      videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
-      videoInstance.set('description', videoAttributesToUpdate.description)
-      videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
-      videoInstance.set('duration', videoAttributesToUpdate.duration)
-      videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
-      videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
-      videoInstance.set('extname', videoAttributesToUpdate.extname)
-      videoInstance.set('views', videoAttributesToUpdate.views)
-      videoInstance.set('likes', videoAttributesToUpdate.likes)
-      videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
-
-      videoInstance.save(options).asCallback(function (err) {
-        return callback(err, t, videoInstance, tagInstances)
+      .then(({ videoInstance, tagInstances }) => {
+        const options = { transaction: t }
+
+        videoInstance.set('name', videoAttributesToUpdate.name)
+        videoInstance.set('category', videoAttributesToUpdate.category)
+        videoInstance.set('licence', videoAttributesToUpdate.licence)
+        videoInstance.set('language', videoAttributesToUpdate.language)
+        videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
+        videoInstance.set('description', videoAttributesToUpdate.description)
+        videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
+        videoInstance.set('duration', videoAttributesToUpdate.duration)
+        videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
+        videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
+        videoInstance.set('extname', videoAttributesToUpdate.extname)
+        videoInstance.set('views', videoAttributesToUpdate.views)
+        videoInstance.set('likes', videoAttributesToUpdate.likes)
+        videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
+
+        return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
       })
-    },
-
-    function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
-      const options = { transaction: t }
+      .then(({ videoInstance, tagInstances }) => {
+        const options = { transaction: t }
 
-      videoInstance.setTags(tagInstances, options).asCallback(function (err) {
-        return callback(err, t)
+        return videoInstance.setTags(tagInstances, options)
       })
-    },
-
-    commitTransaction
-
-  ], function (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      // This is just a debug because we will retry the insert
-      logger.debug('Cannot update the remote video.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
-    logger.info('Remote video %s updated', videoAttributesToUpdate.name)
-    return finalCallback(null)
+  })
+  .then(() => logger.info('Remote video %s updated', videoAttributesToUpdate.name))
+  .catch(err => {
+    // This is just a debug because we will retry the insert
+    logger.debug('Cannot update the remote video.', { error: err })
+    throw err
   })
 }
 
-function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance, callback: (err: Error) => void) {
+function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance) {
   // We need the instance because we have to remove some other stuffs (thumbnail etc)
-  fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
-    // Do not return the error, continue the process
-    if (err) return callback(null)
-
-    logger.debug('Removing remote video %s.', video.remoteId)
-    video.destroy().asCallback(function (err) {
-      // Do not return the error, continue the process
-      if (err) {
-        logger.error('Cannot remove remote video with id %s.', videoToRemoveData.remoteId, { error: err })
-      }
-
-      return callback(null)
+  return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
+    .then(video => {
+      logger.debug('Removing remote video %s.', video.remoteId)
+      return video.destroy()
+    })
+    .catch(err => {
+      logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err })
     })
-  })
 }
 
-function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance, callback: (err: Error) => void) {
-  fetchOwnedVideo(reportData.videoRemoteId, function (err, video) {
-    if (err || !video) {
-      if (!err) err = new Error('video not found')
-
-      logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId })
-      // Do not return the error, continue the process
-      return callback(null)
-    }
-
-    logger.debug('Reporting remote abuse for video %s.', video.id)
-
-    const videoAbuseData = {
-      reporterUsername: reportData.reporterUsername,
-      reason: reportData.reportReason,
-      reporterPodId: fromPod.id,
-      videoId: video.id
-    }
+function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance) {
+  return fetchOwnedVideo(reportData.videoRemoteId)
+    .then(video => {
+      logger.debug('Reporting remote abuse for video %s.', video.id)
 
-    db.VideoAbuse.create(videoAbuseData).asCallback(function (err) {
-      if (err) {
-        logger.error('Cannot create remote abuse video.', { error: err })
+      const videoAbuseData = {
+        reporterUsername: reportData.reporterUsername,
+        reason: reportData.reportReason,
+        reporterPodId: fromPod.id,
+        videoId: video.id
       }
 
-      return callback(null)
+      return db.VideoAbuse.create(videoAbuseData)
     })
-  })
+    .catch(err => logger.error('Cannot create remote abuse video.', { error: err }))
 }
 
-function fetchOwnedVideo (id: string, callback: (err: Error, video?: VideoInstance) => void) {
-  db.Video.load(id, function (err, video) {
-    if (err || !video) {
-      if (!err) err = new Error('video not found')
+function fetchOwnedVideo (id: string) {
+  return db.Video.load(id)
+    .then(video => {
+      if (!video) throw new Error('Video not found')
 
+      return video
+    })
+    .catch(err => {
       logger.error('Cannot load owned video from id.', { error: err, id })
-      return callback(err)
-    }
-
-    return callback(null, video)
-  })
+      throw err
+    })
 }
 
-function fetchRemoteVideo (podHost: string, remoteId: string, callback: (err: Error, video?: VideoInstance) => void) {
-  db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
-    if (err || !video) {
-      if (!err) err = new Error('video not found')
+function fetchRemoteVideo (podHost: string, remoteId: string) {
+  return db.Video.loadByHostAndRemoteId(podHost, remoteId)
+    .then(video => {
+      if (!video) throw new Error('Video not found')
 
+      return video
+    })
+    .catch(err => {
       logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId })
-      return callback(err)
-    }
-
-    return callback(null, video)
-  })
+      throw err
+    })
 }
index 8dd849007b95c3d4b5914e2f6488df9960b625db..2a934a51294e412a0901488f43b5681213746be6 100644 (file)
@@ -1,5 +1,5 @@
 import * as express from 'express'
-import { parallel } from 'async'
+import * as Promise from 'bluebird'
 
 import {
   AbstractRequestScheduler,
@@ -27,33 +27,27 @@ export {
 // ---------------------------------------------------------------------------
 
 function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
-  parallel({
+  Promise.props({
     requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
     requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
     requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
-  }, function (err, result) {
-    if (err) return next(err)
-
-    return res.json(result)
   })
+  .then(result => res.json(result))
+  .catch(err => next(err))
 }
 
 // ---------------------------------------------------------------------------
 
-function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler) {
-  return function (callback) {
-    requestScheduler.remainingRequestsCount(function (err, count) {
-      if (err) return callback(err)
-
-      const result: RequestSchedulerStatsAttributes = {
-        totalRequests: count,
-        requestsLimitPods: requestScheduler.limitPods,
-        requestsLimitPerPod: requestScheduler.limitPerPod,
-        remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
-        milliSecondsInterval: requestScheduler.requestInterval
-      }
-
-      return callback(null, result)
-    })
-  }
+function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
+  return requestScheduler.remainingRequestsCount().then(count => {
+    const result: RequestSchedulerStatsAttributes = {
+      totalRequests: count,
+      requestsLimitPods: requestScheduler.limitPods,
+      requestsLimitPerPod: requestScheduler.limitPerPod,
+      remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
+      milliSecondsInterval: requestScheduler.requestInterval
+    }
+
+    return result
+  })
 }
index ce15353eff73e6232b5773920ccdeda8d5d0ef01..6e0bb474a4677df3a63cbcb077e5b0c1ef2376e9 100644 (file)
@@ -1,8 +1,7 @@
 import * as express from 'express'
-import { waterfall } from 'async'
 
 import { database as db } from '../../initializers/database'
-import { CONFIG, USER_ROLES } from '../../initializers'
+import { USER_ROLES } from '../../initializers'
 import { logger, getFormatedObjects } from '../../helpers'
 import {
   authenticate,
@@ -87,78 +86,61 @@ function createUser (req: express.Request, res: express.Response, next: express.
     role: USER_ROLES.USER
   })
 
-  user.save().asCallback(function (err) {
-    if (err) return next(err)
-
-    return res.type('json').status(204).end()
-  })
+  user.save()
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
 }
 
 function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
-    if (err) return next(err)
-
-    return res.json(user.toFormatedJSON())
-  })
+  db.User.loadByUsername(res.locals.oauth.token.user.username)
+    .then(user => res.json(user.toFormatedJSON()))
+    .catch(err => next(err))
 }
 
 function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
   const videoId = '' + req.params.videoId
   const userId = +res.locals.oauth.token.User.id
 
-  db.UserVideoRate.load(userId, videoId, null, function (err, ratingObj) {
-    if (err) return next(err)
-
-    const rating = ratingObj ? ratingObj.type : 'none'
-
-    const json: FormatedUserVideoRate = {
-      videoId,
-      rating
-    }
-    res.json(json)
-  })
+  db.UserVideoRate.load(userId, videoId, null)
+    .then(ratingObj => {
+      const rating = ratingObj ? ratingObj.type : 'none'
+      const json: FormatedUserVideoRate = {
+        videoId,
+        rating
+      }
+      res.json(json)
+    })
+    .catch(err => next(err))
 }
 
 function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
-    if (err) return next(err)
-
-    res.json(getFormatedObjects(usersList, usersTotal))
-  })
+  db.User.listForApi(req.query.start, req.query.count, req.query.sort)
+    .then(resultList => {
+      res.json(getFormatedObjects(resultList.data, resultList.total))
+    })
+    .catch(err => next(err))
 }
 
 function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
-  waterfall([
-    function loadUser (callback) {
-      db.User.loadById(req.params.id, callback)
-    },
-
-    function deleteUser (user, callback) {
-      user.destroy().asCallback(callback)
-    }
-  ], function andFinally (err) {
-    if (err) {
+  db.User.loadById(req.params.id)
+    .then(user => user.destroy())
+    .then(() => res.sendStatus(204))
+    .catch(err => {
       logger.error('Errors when removed the user.', { error: err })
       return next(err)
-    }
-
-    return res.sendStatus(204)
-  })
+    })
 }
 
 function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
-    if (err) return next(err)
-
-    if (req.body.password) user.password = req.body.password
-    if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
+  db.User.loadByUsername(res.locals.oauth.token.user.username)
+    .then(user => {
+      if (req.body.password) user.password = req.body.password
+      if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
 
-    user.save().asCallback(function (err) {
-      if (err) return next(err)
-
-      return res.sendStatus(204)
+      return user.save()
     })
-  })
+    .then(() => res.sendStatus(204))
+    .catch(err => next(err))
 }
 
 function success (req: express.Request, res: express.Response, next: express.NextFunction) {
index 78e8e8b3d7ed9bf188731640c016e7e44d1d5238..fcbd5465f16c77e95be4ca498a8488ecc461823b 100644 (file)
@@ -1,16 +1,11 @@
 import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { waterfall } from 'async'
 
 import { database as db } from '../../../initializers/database'
 import * as friends from '../../../lib/friends'
 import {
   logger,
   getFormatedObjects,
-  retryTransactionWrapper,
-  startSerializableTransaction,
-  commitTransaction,
-  rollbackTransaction
+  retryTransactionWrapper
 } from '../../../helpers'
 import {
   authenticate,
@@ -21,6 +16,7 @@ import {
   setVideoAbusesSort,
   setPagination
 } from '../../../middlewares'
+import { VideoInstance } from '../../../models'
 
 const abuseVideoRouter = express.Router()
 
@@ -48,11 +44,9 @@ export {
 // ---------------------------------------------------------------------------
 
 function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) {
-    if (err) return next(err)
-
-    res.json(getFormatedObjects(abusesList, abusesTotal))
-  })
+  db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
+    .then(result => res.json(getFormatedObjects(result.data, result.total)))
+    .catch(err => next(err))
 }
 
 function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -61,14 +55,12 @@ function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Respon
     errorMessage: 'Cannot report abuse to the video with many retries.'
   }
 
-  retryTransactionWrapper(reportVideoAbuse, options, function (err) {
-    if (err) return next(err)
-
-    return res.type('json').status(204).end()
-  })
+  retryTransactionWrapper(reportVideoAbuse, options)
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
 }
 
-function reportVideoAbuse (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function reportVideoAbuse (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.video
   const reporterUsername = res.locals.oauth.token.User.username
 
@@ -79,40 +71,26 @@ function reportVideoAbuse (req: express.Request, res: express.Response, finalCal
     reporterPodId: null // This is our pod that reported this abuse
   }
 
-  waterfall([
-
-    startSerializableTransaction,
-
-    function createAbuse (t, callback) {
-      db.VideoAbuse.create(abuse).asCallback(function (err, abuse) {
-        return callback(err, t, abuse)
-      })
-    },
-
-    function sendToFriendsIfNeeded (t, abuse, callback) {
-      // We send the information to the destination pod
-      if (videoInstance.isOwned() === false) {
-        const reportData = {
-          reporterUsername,
-          reportReason: abuse.reason,
-          videoRemoteId: videoInstance.remoteId
+  return db.sequelize.transaction(t => {
+    return db.VideoAbuse.create(abuse, { transaction: t })
+      .then(abuse => {
+        // We send the information to the destination pod
+        if (videoInstance.isOwned() === false) {
+          const reportData = {
+            reporterUsername,
+            reportReason: abuse.reason,
+            videoRemoteId: videoInstance.remoteId
+          }
+
+          return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
         }
 
-        friends.reportAbuseVideoToFriend(reportData, videoInstance)
-      }
-
-      return callback(null, t)
-    },
-
-    commitTransaction
-
-  ], function andFinally (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      logger.debug('Cannot update the video.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
-    logger.info('Abuse report for video %s created.', videoInstance.name)
-    return finalCallback(null)
+        return videoInstance
+      })
+  })
+  .then((videoInstance: VideoInstance) => logger.info('Abuse report for video %s created.', videoInstance.name))
+  .catch(err => {
+    logger.debug('Cannot update the video.', { error: err })
+    throw err
   })
 }
index 4b42fc2d703c17a3eb867f2db8b5f4fd4166a15d..e4be6f0f9cb8d11d7763dfbd4a7d1633e50ba946 100644 (file)
@@ -32,12 +32,10 @@ function addVideoToBlacklist (req: express.Request, res: express.Response, next:
     videoId: videoInstance.id
   }
 
-  db.BlacklistedVideo.create(toCreate).asCallback(function (err) {
-    if (err) {
+  db.BlacklistedVideo.create(toCreate)
+    .then(() => res.type('json').status(204).end())
+    .catch(err => {
       logger.error('Errors when blacklisting video ', { error: err })
       return next(err)
-    }
-
-    return res.type('json').status(204).end()
-  })
+    })
 }
index 5e8cf2d2546398119779a9ebd65842f9f3acfaeb..ed1f21d666050696388549bade5564b42796005a 100644 (file)
@@ -1,9 +1,7 @@
 import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import * as fs from 'fs'
+import * as Promise from 'bluebird'
 import * as multer from 'multer'
 import * as path from 'path'
-import { waterfall } from 'async'
 
 import { database as db } from '../../../initializers/database'
 import {
@@ -35,13 +33,12 @@ import {
 } from '../../../middlewares'
 import {
   logger,
-  commitTransaction,
   retryTransactionWrapper,
-  rollbackTransaction,
-  startSerializableTransaction,
   generateRandomString,
-  getFormatedObjects
+  getFormatedObjects,
+  renamePromise
 } from '../../../helpers'
+import { TagInstance } from '../../../models'
 
 import { abuseVideoRouter } from './abuse'
 import { blacklistRouter } from './blacklist'
@@ -60,10 +57,15 @@ const storage = multer.diskStorage({
     if (file.mimetype === 'video/webm') extension = 'webm'
     else if (file.mimetype === 'video/mp4') extension = 'mp4'
     else if (file.mimetype === 'video/ogg') extension = 'ogv'
-    generateRandomString(16, function (err, randomString) {
-      const fieldname = err ? undefined : randomString
-      cb(null, fieldname + '.' + extension)
-    })
+    generateRandomString(16)
+      .then(randomString => {
+        const filename = randomString
+        cb(null, filename + '.' + extension)
+      })
+      .catch(err => {
+        logger.error('Cannot generate random string for file name.', { error: err })
+        throw err
+      })
   }
 })
 
@@ -144,125 +146,97 @@ function addVideoRetryWrapper (req: express.Request, res: express.Response, next
     errorMessage: 'Cannot insert the video with many retries.'
   }
 
-  retryTransactionWrapper(addVideo, options, function (err) {
-    if (err) return next(err)
-
-    // TODO : include Location of the new video -> 201
-    return res.type('json').status(204).end()
-  })
+  retryTransactionWrapper(addVideo, options)
+    .then(() => {
+      // TODO : include Location of the new video -> 201
+      res.type('json').status(204).end()
+    })
+    .catch(err => next(err))
 }
 
-function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File, finalCallback: (err: Error) => void) {
+function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File) {
   const videoInfos = req.body
 
-  waterfall([
-
-    startSerializableTransaction,
+  return db.sequelize.transaction(t => {
+    const user = res.locals.oauth.token.User
 
-    function findOrCreateAuthor (t, callback) {
-      const user = res.locals.oauth.token.User
+    const name = user.username
+    // null because it is OUR pod
+    const podId = null
+    const userId = user.id
 
-      const name = user.username
-      // null because it is OUR pod
-      const podId = null
-      const userId = user.id
+    return db.Author.findOrCreateAuthor(name, podId, userId, t)
+      .then(author => {
+        const tags = videoInfos.tags
+        if (!tags) return { author, tagInstances: undefined }
 
-      db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
-        return callback(err, t, authorInstance)
+        return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
       })
-    },
-
-    function findOrCreateTags (t, author, callback) {
-      const tags = videoInfos.tags
-
-      db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
-        return callback(err, t, author, tagInstances)
+      .then(({ author, tagInstances }) => {
+        const videoData = {
+          name: videoInfos.name,
+          remoteId: null,
+          extname: path.extname(videoFile.filename),
+          category: videoInfos.category,
+          licence: videoInfos.licence,
+          language: videoInfos.language,
+          nsfw: videoInfos.nsfw,
+          description: videoInfos.description,
+          duration: videoFile['duration'], // duration was added by a previous middleware
+          authorId: author.id
+        }
+
+        const video = db.Video.build(videoData)
+        return { author, tagInstances, video }
       })
-    },
-
-    function createVideoObject (t, author, tagInstances, callback) {
-      const videoData = {
-        name: videoInfos.name,
-        remoteId: null,
-        extname: path.extname(videoFile.filename),
-        category: videoInfos.category,
-        licence: videoInfos.licence,
-        language: videoInfos.language,
-        nsfw: videoInfos.nsfw,
-        description: videoInfos.description,
-        duration: videoFile['duration'], // duration was added by a previous middleware
-        authorId: author.id
-      }
-
-      const video = db.Video.build(videoData)
-
-      return callback(null, t, author, tagInstances, video)
-    },
-
-     // Set the videoname the same as the id
-    function renameVideoFile (t, author, tagInstances, video, callback) {
-      const videoDir = CONFIG.STORAGE.VIDEOS_DIR
-      const source = path.join(videoDir, videoFile.filename)
-      const destination = path.join(videoDir, video.getVideoFilename())
-
-      fs.rename(source, destination, function (err) {
-        if (err) return callback(err)
-
-        // This is important in case if there is another attempt
-        videoFile.filename = video.getVideoFilename()
-        return callback(null, t, author, tagInstances, video)
+      .then(({ author, tagInstances, video }) => {
+        const videoDir = CONFIG.STORAGE.VIDEOS_DIR
+        const source = path.join(videoDir, videoFile.filename)
+        const destination = path.join(videoDir, video.getVideoFilename())
+
+        return renamePromise(source, destination)
+          .then(() => {
+            // This is important in case if there is another attempt in the retry process
+            videoFile.filename = video.getVideoFilename()
+            return { author, tagInstances, video }
+          })
       })
-    },
-
-    function insertVideoIntoDB (t, author, tagInstances, video, callback) {
-      const options = { transaction: t }
-
-      // Add tags association
-      video.save(options).asCallback(function (err, videoCreated) {
-        if (err) return callback(err)
+      .then(({ author, tagInstances, video }) => {
+        const options = { transaction: t }
 
-        // Do not forget to add Author informations to the created video
-        videoCreated.Author = author
+        return video.save(options)
+          .then(videoCreated => {
+            // Do not forget to add Author informations to the created video
+            videoCreated.Author = author
 
-        return callback(err, t, tagInstances, videoCreated)
+            return { tagInstances, video: videoCreated }
+          })
       })
-    },
-
-    function associateTagsToVideo (t, tagInstances, video, callback) {
-      const options = { transaction: t }
+      .then(({ tagInstances, video }) => {
+        if (!tagInstances) return video
 
-      video.setTags(tagInstances, options).asCallback(function (err) {
-        video.Tags = tagInstances
-
-        return callback(err, t, video)
+        const options = { transaction: t }
+        return video.setTags(tagInstances, options)
+          .then(() => {
+            video.Tags = tagInstances
+            return video
+          })
       })
-    },
-
-    function sendToFriends (t, video, callback) {
-      // Let transcoding job send the video to friends because the videofile extension might change
-      if (CONFIG.TRANSCODING.ENABLED === true) return callback(null, t)
-
-      video.toAddRemoteJSON(function (err, remoteVideo) {
-        if (err) return callback(err)
-
-        // Now we'll add the video's meta data to our friends
-        addVideoToFriends(remoteVideo, t, function (err) {
-          return callback(err, t)
-        })
+      .then(video => {
+        // Let transcoding job send the video to friends because the videofile extension might change
+        if (CONFIG.TRANSCODING.ENABLED === true) return undefined
+
+        return video.toAddRemoteJSON()
+          .then(remoteVideo => {
+            // Now we'll add the video's meta data to our friends
+            return addVideoToFriends(remoteVideo, t)
+          })
       })
-    },
-
-    commitTransaction
-
-  ], function andFinally (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      // This is just a debug because we will retry the insert
-      logger.debug('Cannot insert the video.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
-    logger.info('Video with name %s created.', videoInfos.name)
-    return finalCallback(null)
+  })
+  .then(() => logger.info('Video with name %s created.', videoInfos.name))
+  .catch((err: Error) => {
+    logger.debug('Cannot insert the video.', { error: err.stack })
+    throw err
   })
 }
 
@@ -272,92 +246,75 @@ function updateVideoRetryWrapper (req: express.Request, res: express.Response, n
     errorMessage: 'Cannot update the video with many retries.'
   }
 
-  retryTransactionWrapper(updateVideo, options, function (err) {
-    if (err) return next(err)
-
-    // TODO : include Location of the new video -> 201
-    return res.type('json').status(204).end()
-  })
+  retryTransactionWrapper(updateVideo, options)
+    .then(() => {
+      // TODO : include Location of the new video -> 201
+      return res.type('json').status(204).end()
+    })
+    .catch(err => next(err))
 }
 
-function updateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function updateVideo (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.video
   const videoFieldsSave = videoInstance.toJSON()
   const videoInfosToUpdate = req.body
 
-  waterfall([
-
-    startSerializableTransaction,
-
-    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 !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
-      if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
-      if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
-      if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
-      if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
-      if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
-
-      videoInstance.save(options).asCallback(function (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 db.sequelize.transaction(t => {
+    let tagsPromise: Promise<TagInstance[]>
+    if (!videoInfosToUpdate.tags) {
+      tagsPromise = Promise.resolve(null)
+    } else {
+      tagsPromise = db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t)
+    }
 
-          return callback(err, t)
-        })
-      } else {
-        return callback(null, t)
-      }
-    },
+    return tagsPromise
+      .then(tagInstances => {
+        const options = {
+          transaction: t
+        }
 
-    function sendToFriends (t, callback) {
-      const json = videoInstance.toUpdateRemoteJSON()
+        if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
+        if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
+        if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
+        if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
+        if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
+        if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
 
-      // Now we'll update the video's meta data to our friends
-      updateVideoToFriends(json, t, function (err) {
-        return callback(err, t)
+        return videoInstance.save(options).then(() => tagInstances)
       })
-    },
-
-    commitTransaction
+      .then(tagInstances => {
+        if (!tagInstances) return
 
-  ], function andFinally (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      logger.debug('Cannot update the video.', { error: err })
+        const options = { transaction: t }
+        return videoInstance.setTags(tagInstances, options)
+          .then(() => {
+            videoInstance.Tags = tagInstances
 
-      // Force fields we want to update
-      // If the transaction is retried, sequelize will think the object has not changed
-      // So it will skip the SQL request, even if the last one was ROLLBACKed!
-      Object.keys(videoFieldsSave).forEach(function (key) {
-        const value = videoFieldsSave[key]
-        videoInstance.set(key, value)
+            return
+          })
       })
+      .then(() => {
+        const json = videoInstance.toUpdateRemoteJSON()
 
-      return rollbackTransaction(err, t, finalCallback)
-    }
-
+        // Now we'll update the video's meta data to our friends
+        return updateVideoToFriends(json, t)
+      })
+  })
+  .then(() => {
     logger.info('Video with name %s updated.', videoInstance.name)
-    return finalCallback(null)
+  })
+  .catch(err => {
+    logger.debug('Cannot update the video.', { error: err })
+
+    // Force fields we want to update
+    // If the transaction is retried, sequelize will think the object has not changed
+    // So it will skip the SQL request, even if the last one was ROLLBACKed!
+    Object.keys(videoFieldsSave).forEach(function (key) {
+      const value = videoFieldsSave[key]
+      videoInstance.set(key, value)
+    })
+
+    throw err
   })
 }
 
@@ -366,20 +323,17 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
 
   if (videoInstance.isOwned()) {
     // The increment is done directly in the database, not using the instance value
-    videoInstance.increment('views').asCallback(function (err) {
-      if (err) {
-        logger.error('Cannot add view to video %d.', videoInstance.id)
-        return
-      }
-
-      // FIXME: make a real view system
-      // For example, only add a view when a user watch a video during 30s etc
-      const qaduParams = {
-        videoId: videoInstance.id,
-        type: REQUEST_VIDEO_QADU_TYPES.VIEWS
-      }
-      quickAndDirtyUpdateVideoToFriends(qaduParams)
-    })
+    videoInstance.increment('views')
+      .then(() => {
+        // FIXME: make a real view system
+        // For example, only add a view when a user watch a video during 30s etc
+        const qaduParams = {
+          videoId: videoInstance.id,
+          type: REQUEST_VIDEO_QADU_TYPES.VIEWS
+        }
+        return quickAndDirtyUpdateVideoToFriends(qaduParams)
+      })
+      .catch(err => logger.error('Cannot add view to video %d.', videoInstance.id, { error: err }))
   } else {
     // Just send the event to our friends
     const eventParams = {
@@ -394,33 +348,24 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
 }
 
 function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
-    if (err) return next(err)
-
-    res.json(getFormatedObjects(videosList, videosTotal))
-  })
+  db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
+    .then(result => res.json(getFormatedObjects(result.data, result.total)))
+    .catch(err => next(err))
 }
 
 function removeVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
   const videoInstance = res.locals.video
 
-  videoInstance.destroy().asCallback(function (err) {
-    if (err) {
+  videoInstance.destroy()
+    .then(() => res.type('json').status(204).end())
+    .catch(err => {
       logger.error('Errors when removed the video.', { error: err })
       return next(err)
-    }
-
-    return res.type('json').status(204).end()
-  })
+    })
 }
 
 function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
-  db.Video.searchAndPopulateAuthorAndPodAndTags(
-    req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
-    function (err, videosList, videosTotal) {
-      if (err) return next(err)
-
-      res.json(getFormatedObjects(videosList, videosTotal))
-    }
-  )
+  db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
+    .then(result => res.json(getFormatedObjects(result.data, result.total)))
+    .catch(err => next(err))
 }
index afdd099f8defdb8d875fadbf26cbe760302e45db..3d119d98baca61c9c246a290c609db046569848d 100644 (file)
@@ -1,14 +1,9 @@
 import * as express from 'express'
-import * as Sequelize from 'sequelize'
-import { waterfall } from 'async'
 
 import { database as db } from '../../../initializers/database'
 import {
   logger,
-  retryTransactionWrapper,
-  startSerializableTransaction,
-  commitTransaction,
-  rollbackTransaction
+  retryTransactionWrapper
 } from '../../../helpers'
 import {
   VIDEO_RATE_TYPES,
@@ -46,137 +41,109 @@ function rateVideoRetryWrapper (req: express.Request, res: express.Response, nex
     errorMessage: 'Cannot update the user video rate.'
   }
 
-  retryTransactionWrapper(rateVideo, options, function (err) {
-    if (err) return next(err)
-
-    return res.type('json').status(204).end()
-  })
+  retryTransactionWrapper(rateVideo, options)
+    .then(() => res.type('json').status(204).end())
+    .catch(err => next(err))
 }
 
-function rateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
+function rateVideo (req: express.Request, res: express.Response) {
   const rateType = req.body.rating
   const videoInstance = res.locals.video
   const userInstance = res.locals.oauth.token.User
 
-  waterfall([
-    startSerializableTransaction,
-
-    function findPreviousRate (t, callback) {
-      db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) {
-        return callback(err, t, previousRate)
-      })
-    },
+  return db.sequelize.transaction(t => {
+    return db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
+      .then(previousRate => {
+        const options = { transaction: t }
 
-    function insertUserRateIntoDB (t, previousRate, callback) {
-      const options = { transaction: t }
+        let likesToIncrement = 0
+        let dislikesToIncrement = 0
 
-      let likesToIncrement = 0
-      let dislikesToIncrement = 0
+        if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
+        else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
 
-      if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
-      else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
+        // There was a previous rate, update it
+        if (previousRate) {
+          // We will remove the previous rate, so we will need to remove it from the video attribute
+          if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
+          else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
 
-      // There was a previous rate, update it
-      if (previousRate) {
-        // We will remove the previous rate, so we will need to remove it from the video attribute
-        if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
-        else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
+          previousRate.type = rateType
 
-        previousRate.type = rateType
+          return previousRate.save(options).then(() => ({ t, likesToIncrement, dislikesToIncrement }))
+        } else { // There was not a previous rate, insert a new one
+          const query = {
+            userId: userInstance.id,
+            videoId: videoInstance.id,
+            type: rateType
+          }
 
-        previousRate.save(options).asCallback(function (err) {
-          return callback(err, t, likesToIncrement, dislikesToIncrement)
-        })
-      } else { // There was not a previous rate, insert a new one
-        const query = {
-          userId: userInstance.id,
-          videoId: videoInstance.id,
-          type: rateType
+          return db.UserVideoRate.create(query, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
         }
-
-        db.UserVideoRate.create(query, options).asCallback(function (err) {
-          return callback(err, t, likesToIncrement, dislikesToIncrement)
-        })
-      }
-    },
-
-    function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) {
-      const options = { transaction: t }
-      const incrementQuery = {
-        likes: likesToIncrement,
-        dislikes: dislikesToIncrement
-      }
-
-      // Even if we do not own the video we increment the attributes
-      // It is usefull for the user to have a feedback
-      videoInstance.increment(incrementQuery, options).asCallback(function (err) {
-        return callback(err, t, likesToIncrement, dislikesToIncrement)
-      })
-    },
-
-    function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
-      // No need for an event type, we own the video
-      if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement)
-
-      const eventsParams = []
-
-      if (likesToIncrement !== 0) {
-        eventsParams.push({
-          videoId: videoInstance.id,
-          type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
-          count: likesToIncrement
-        })
-      }
-
-      if (dislikesToIncrement !== 0) {
-        eventsParams.push({
-          videoId: videoInstance.id,
-          type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
-          count: dislikesToIncrement
-        })
-      }
-
-      addEventsToRemoteVideo(eventsParams, t, function (err) {
-        return callback(err, t, likesToIncrement, dislikesToIncrement)
       })
-    },
-
-    function sendQaduToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
-      // We do not own the video, there is no need to send a quick and dirty update to friends
-      // Our rate was already sent by the addEvent function
-      if (videoInstance.isOwned() === false) return callback(null, t)
-
-      const qadusParams = []
-
-      if (likesToIncrement !== 0) {
-        qadusParams.push({
-          videoId: videoInstance.id,
-          type: REQUEST_VIDEO_QADU_TYPES.LIKES
-        })
-      }
-
-      if (dislikesToIncrement !== 0) {
-        qadusParams.push({
-          videoId: videoInstance.id,
-          type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
-        })
-      }
-
-      quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
-        return callback(err, t)
+      .then(({ likesToIncrement, dislikesToIncrement }) => {
+        const options = { transaction: t }
+        const incrementQuery = {
+          likes: likesToIncrement,
+          dislikes: dislikesToIncrement
+        }
+
+        // Even if we do not own the video we increment the attributes
+        // It is usefull for the user to have a feedback
+        return videoInstance.increment(incrementQuery, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
       })
-    },
+      .then(({ likesToIncrement, dislikesToIncrement }) => {
+        // No need for an event type, we own the video
+        if (videoInstance.isOwned()) return { likesToIncrement, dislikesToIncrement }
+
+        const eventsParams = []
+
+        if (likesToIncrement !== 0) {
+          eventsParams.push({
+            videoId: videoInstance.id,
+            type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
+            count: likesToIncrement
+          })
+        }
+
+        if (dislikesToIncrement !== 0) {
+          eventsParams.push({
+            videoId: videoInstance.id,
+            type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
+            count: dislikesToIncrement
+          })
+        }
 
-    commitTransaction
+        return addEventsToRemoteVideo(eventsParams, t).then(() => ({ likesToIncrement, dislikesToIncrement }))
+      })
+      .then(({ likesToIncrement, dislikesToIncrement }) => {
+        // We do not own the video, there is no need to send a quick and dirty update to friends
+        // Our rate was already sent by the addEvent function
+        if (videoInstance.isOwned() === false) return undefined
+
+        const qadusParams = []
+
+        if (likesToIncrement !== 0) {
+          qadusParams.push({
+            videoId: videoInstance.id,
+            type: REQUEST_VIDEO_QADU_TYPES.LIKES
+          })
+        }
 
-  ], function (err: Error, t: Sequelize.Transaction) {
-    if (err) {
-      // This is just a debug because we will retry the insert
-      logger.debug('Cannot add the user video rate.', { error: err })
-      return rollbackTransaction(err, t, finalCallback)
-    }
+        if (dislikesToIncrement !== 0) {
+          qadusParams.push({
+            videoId: videoInstance.id,
+            type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
+          })
+        }
 
-    logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
-    return finalCallback(null)
+        return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
+      })
+  })
+  .then(() => logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username))
+  .catch(err => {
+    // This is just a debug because we will retry the insert
+    logger.debug('Cannot add the user video rate.', { error: err })
+    throw err
   })
 }
index 503eff75013ab5c58bf80869e3b0b0c24ecde67e..e4d69eae7d3cc70130e8b5959e0abee28615b339 100644 (file)
@@ -1,8 +1,7 @@
-import { parallel } from 'async'
 import * as express from 'express'
-import * as fs from 'fs'
 import { join } from 'path'
 import * as validator from 'validator'
+import * as Promise from 'bluebird'
 
 import { database as db } from '../initializers/database'
 import {
@@ -11,7 +10,7 @@ import {
   STATIC_PATHS,
   STATIC_MAX_AGE
 } from '../initializers'
-import { root } from '../helpers'
+import { root, readFileBufferPromise } from '../helpers'
 import { VideoInstance } from '../models'
 
 const clientsRouter = express.Router()
@@ -95,19 +94,15 @@ function generateWatchHtmlPage (req: express.Request, res: express.Response, nex
   // Let Angular application handle errors
   if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
 
-  parallel({
-    file: function (callback) {
-      fs.readFile(indexPath, callback)
-    },
+  Promise.all([
+    readFileBufferPromise(indexPath),
+    db.Video.loadAndPopulateAuthorAndPodAndTags(videoId)
+  ])
+  .then(([ file, video ]) => {
+    file = file as Buffer
+    video = video as VideoInstance
 
-    video: function (callback) {
-      db.Video.loadAndPopulateAuthorAndPodAndTags(videoId, callback)
-    }
-  }, function (err: Error, result: { file: Buffer, video: VideoInstance }) {
-    if (err) return next(err)
-
-    const html = result.file.toString()
-    const video = result.video
+    const html = file.toString()
 
     // Let Angular application handle errors
     if (!video) return res.sendFile(indexPath)
@@ -115,4 +110,5 @@ function generateWatchHtmlPage (req: express.Request, res: express.Response, nex
     const htmlStringPageWithTags = addOpenGraphTags(html, video)
     res.set('Content-Type', 'text/html; charset=UTF-8').send(htmlStringPageWithTags)
   })
+  .catch(err => next(err))
 }
index 32b89b6dd583ff8175fc8d4581553341757cbbbd..1e92049f1b8e64dd40fb7e2a065842ce9adecba2 100644 (file)
@@ -4,6 +4,20 @@
 */
 
 import { join } from 'path'
+import { pseudoRandomBytes } from 'crypto'
+import {
+  readdir,
+  readFile,
+  rename,
+  unlink,
+  writeFile,
+  access
+} from 'fs'
+import * as mkdirp from 'mkdirp'
+import * as bcrypt from 'bcrypt'
+import * as createTorrent from 'create-torrent'
+import * as openssl from 'openssl-wrapper'
+import * as Promise from 'bluebird'
 
 function isTestInstance () {
   return process.env.NODE_ENV === 'test'
@@ -14,9 +28,82 @@ function root () {
   return join(__dirname, '..', '..', '..')
 }
 
+function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
+  return function promisified (): Promise<A> {
+    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+      func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
+    })
+  }
+}
+
+// Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
+function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
+  return function promisified (arg: T): Promise<A> {
+    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+      func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
+    })
+  }
+}
+
+function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
+  return function promisified (arg: T): Promise<void> {
+    return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
+      func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
+    })
+  }
+}
+
+function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
+  return function promisified (arg1: T, arg2: U): Promise<A> {
+    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
+      func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
+    })
+  }
+}
+
+function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
+  return function promisified (arg1: T, arg2: U): Promise<void> {
+    return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
+      func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
+    })
+  }
+}
+
+const readFilePromise = promisify2<string, string, string>(readFile)
+const readFileBufferPromise = promisify1<string, Buffer>(readFile)
+const unlinkPromise = promisify1WithVoid<string>(unlink)
+const renamePromise = promisify2WithVoid<string, string>(rename)
+const writeFilePromise = promisify2<string, any, void>(writeFile)
+const readdirPromise = promisify1<string, string[]>(readdir)
+const mkdirpPromise = promisify1<string, string>(mkdirp)
+const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
+const accessPromise = promisify1WithVoid<string|Buffer>(access)
+const opensslExecPromise = promisify2WithVoid<string, any>(openssl.exec)
+const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
+const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
+const bcryptHashPromise = promisify2<any, string|number, string>(bcrypt.hash)
+const createTorrentPromise = promisify2<string, any, any>(createTorrent)
+
 // ---------------------------------------------------------------------------
 
 export {
   isTestInstance,
-  root
+  root,
+
+  promisify0,
+  promisify1,
+  readdirPromise,
+  readFilePromise,
+  readFileBufferPromise,
+  unlinkPromise,
+  renamePromise,
+  writeFilePromise,
+  mkdirpPromise,
+  pseudoRandomBytesPromise,
+  accessPromise,
+  opensslExecPromise,
+  bcryptComparePromise,
+  bcryptGenSaltPromise,
+  bcryptHashPromise,
+  createTorrentPromise
 }
index f8ee9a45424e6c9e69fa70635e350cadeec1df17..f761cfb89813b928e9c671214b89530eef1e0a37 100644 (file)
@@ -1,70 +1,45 @@
-import * as Sequelize from 'sequelize'
 // TODO: import from ES6 when retry typing file will include errorFilter function
 import * as retry from 'async/retry'
+import * as Promise from 'bluebird'
 
-import { database as db } from '../initializers/database'
 import { logger } from './logger'
 
-function commitTransaction (t: Sequelize.Transaction, callback: (err: Error) => void) {
-  return t.commit().asCallback(callback)
-}
-
-function rollbackTransaction (err: Error, t: Sequelize.Transaction, callback: (err: Error) => void) {
-  // Try to rollback transaction
-  if (t) {
-    // Do not catch err, report the original one
-    t.rollback().asCallback(function () {
-      return callback(err)
-    })
-  } else {
-    return callback(err)
-  }
-}
-
 type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] }
-function retryTransactionWrapper (functionToRetry: Function, options: RetryTransactionWrapperOptions, finalCallback: Function) {
+function retryTransactionWrapper (functionToRetry: (... args) => Promise<any>, options: RetryTransactionWrapperOptions) {
   const args = options.arguments ? options.arguments : []
 
-  transactionRetryer(
+  return transactionRetryer(
     function (callback) {
-      return functionToRetry.apply(this, args.concat([ callback ]))
-    },
-    function (err) {
-      if (err) {
-        logger.error(options.errorMessage, { error: err })
-      }
-
-      // Do not return the error, continue the process
-      return finalCallback(null)
+      functionToRetry.apply(this, args)
+        .then(result => callback(null, result))
+        .catch(err => callback(err))
     }
   )
+  .catch(err => {
+    // Do not throw the error, continue the process
+    logger.error(options.errorMessage, { error: err })
+  })
 }
 
-function transactionRetryer (func: Function, callback: (err: Error) => void) {
-  retry({
-    times: 5,
-
-    errorFilter: function (err) {
-      const willRetry = (err.name === 'SequelizeDatabaseError')
-      logger.debug('Maybe retrying the transaction function.', { willRetry })
-      return willRetry
-    }
-  }, func, callback)
-}
+function transactionRetryer (func: Function) {
+  return new Promise((res, rej) => {
+    retry({
+      times: 5,
 
-function startSerializableTransaction (callback: (err: Error, t: Sequelize.Transaction) => void) {
-  db.sequelize.transaction(/* { isolationLevel: 'SERIALIZABLE' } */).asCallback(function (err, t) {
-    // We force to return only two parameters
-    return callback(err, t)
+      errorFilter: function (err) {
+        const willRetry = (err.name === 'SequelizeDatabaseError')
+        logger.debug('Maybe retrying the transaction function.', { willRetry })
+        return willRetry
+      }
+    }, func, function (err) {
+      err ? rej(err) : res()
+    })
   })
 }
 
 // ---------------------------------------------------------------------------
 
 export {
-  commitTransaction,
   retryTransactionWrapper,
-  rollbackTransaction,
-  startSerializableTransaction,
   transactionRetryer
 }
index 0ac87512704177f28fff94c7c90f38f6f883c4c4..8e8001cd69bbd3563351ad025e176852a28cac1e 100644 (file)
@@ -1,7 +1,5 @@
 import * as crypto from 'crypto'
-import * as bcrypt from 'bcrypt'
 import * as fs from 'fs'
-import * as openssl from 'openssl-wrapper'
 import { join } from 'path'
 
 import {
@@ -12,6 +10,14 @@ import {
   BCRYPT_SALT_SIZE,
   PUBLIC_CERT_NAME
 } from '../initializers'
+import {
+  readFilePromise,
+  bcryptComparePromise,
+  bcryptGenSaltPromise,
+  bcryptHashPromise,
+  accessPromise,
+  opensslExecPromise
+} from './core-utils'
 import { logger } from './logger'
 
 function checkSignature (publicKey: string, data: string, hexSignature: string) {
@@ -60,46 +66,32 @@ function sign (data: string|Object) {
   return signature
 }
 
-function comparePassword (plainPassword: string, hashPassword: string, callback: (err: Error, match?: boolean) => void) {
-  bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
-    if (err) return callback(err)
-
-    return callback(null, isPasswordMatch)
-  })
+function comparePassword (plainPassword: string, hashPassword: string) {
+  return bcryptComparePromise(plainPassword, hashPassword)
 }
 
-function createCertsIfNotExist (callback: (err: Error) => void) {
-  certsExist(function (err, exist) {
-    if (err) return callback(err)
-
+function createCertsIfNotExist () {
+  return certsExist().then(exist => {
     if (exist === true) {
-      return callback(null)
+      return undefined
     }
 
-    createCerts(function (err) {
-      return callback(err)
-    })
+    return createCerts()
   })
 }
 
-function cryptPassword (password: string, callback: (err: Error, hash?: string) => void) {
-  bcrypt.genSalt(BCRYPT_SALT_SIZE, function (err, salt) {
-    if (err) return callback(err)
-
-    bcrypt.hash(password, salt, function (err, hash) {
-      return callback(err, hash)
-    })
-  })
+function cryptPassword (password: string) {
+  return bcryptGenSaltPromise(BCRYPT_SALT_SIZE).then(salt => bcryptHashPromise(password, salt))
 }
 
-function getMyPrivateCert (callback: (err: Error, privateCert: string) => void) {
+function getMyPrivateCert () {
   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
-  fs.readFile(certPath, 'utf8', callback)
+  return readFilePromise(certPath, 'utf8')
 }
 
-function getMyPublicCert (callback: (err: Error, publicCert: string) => void) {
+function getMyPublicCert () {
   const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
-  fs.readFile(certPath, 'utf8', callback)
+  return readFilePromise(certPath, 'utf8')
 }
 
 // ---------------------------------------------------------------------------
@@ -116,23 +108,21 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function certsExist (callback: (err: Error, certsExist: boolean) => void) {
+function certsExist () {
   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
-  fs.access(certPath, function (err) {
-    // If there is an error the certificates do not exist
-    const exists = !err
-    return callback(null, exists)
-  })
-}
 
-function createCerts (callback: (err: Error) => void) {
-  certsExist(function (err, exist) {
-    if (err) return callback(err)
+  // If there is an error the certificates do not exist
+  return accessPromise(certPath)
+    .then(() => true)
+    .catch(() => false)
+}
 
+function createCerts () {
+  return certsExist().then(exist => {
     if (exist === true) {
       const errorMessage = 'Certs already exist.'
       logger.warning(errorMessage)
-      return callback(new Error(errorMessage))
+      throw new Error(errorMessage)
     }
 
     logger.info('Generating a RSA key...')
@@ -142,30 +132,27 @@ function createCerts (callback: (err: Error) => void) {
       'out': privateCertPath,
       '2048': false
     }
-    openssl.exec('genrsa', genRsaOptions, function (err) {
-      if (err) {
-        logger.error('Cannot create private key on this pod.')
-        return callback(err)
-      }
-
-      logger.info('RSA key generated.')
-      logger.info('Managing public key...')
-
-      const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
-      const rsaOptions = {
-        'in': privateCertPath,
-        'pubout': true,
-        'out': publicCertPath
-      }
-      openssl.exec('rsa', rsaOptions, function (err) {
-        if (err) {
-          logger.error('Cannot create public key on this pod.')
-          return callback(err)
+    return opensslExecPromise('genrsa', genRsaOptions)
+      .then(() => {
+        logger.info('RSA key generated.')
+        logger.info('Managing public key...')
+
+        const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
+        const rsaOptions = {
+          'in': privateCertPath,
+          'pubout': true,
+          'out': publicCertPath
         }
-
-        logger.info('Public key managed.')
-        return callback(null)
+        return opensslExecPromise('rsa', rsaOptions)
+          .then(() => logger.info('Public key managed.'))
+          .catch(err => {
+            logger.error('Cannot create public key on this pod.')
+            throw err
+          })
+      })
+      .catch(err => {
+        logger.error('Cannot create private key on this pod.')
+        throw err
       })
-    })
   })
 }
index b40fc8e39083101de377f64c70a9c60f35348e0f..b31074373e2c9c449db2a20c475ff0846a6ee455 100644 (file)
@@ -1,5 +1,6 @@
 import * as replay from 'request-replay'
 import * as request from 'request'
+import * as Promise from 'bluebird'
 
 import {
   RETRY_REQUESTS,
@@ -14,16 +15,18 @@ type MakeRetryRequestParams = {
   method: 'GET'|'POST',
   json: Object
 }
-function makeRetryRequest (params: MakeRetryRequestParams, callback: request.RequestCallback) {
-  replay(
-    request(params, callback),
-    {
-      retries: RETRY_REQUESTS,
-      factor: 3,
-      maxTimeout: Infinity,
-      errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
-    }
-  )
+function makeRetryRequest (params: MakeRetryRequestParams) {
+  return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
+    replay(
+      request(params, (err, response, body) => err ? rej(err) : res({ response, body })),
+      {
+        retries: RETRY_REQUESTS,
+        factor: 3,
+        maxTimeout: Infinity,
+        errorCodes: [ 'EADDRINFO', 'ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED' ]
+      }
+    )
+  })
 }
 
 type MakeSecureRequestParams = {
@@ -33,41 +36,43 @@ type MakeSecureRequestParams = {
   sign: boolean
   data?: Object
 }
-function makeSecureRequest (params: MakeSecureRequestParams, callback: request.RequestCallback) {
-  const requestParams = {
-    url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
-    json: {}
-  }
+function makeSecureRequest (params: MakeSecureRequestParams) {
+  return new Promise<{ response: request.RequestResponse, body: any }>((res, rej) => {
+    const requestParams = {
+      url: REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path,
+      json: {}
+    }
 
-  if (params.method !== 'POST') {
-    return callback(new Error('Cannot make a secure request with a non POST method.'), null, null)
-  }
+    if (params.method !== 'POST') {
+      return rej(new Error('Cannot make a secure request with a non POST method.'))
+    }
 
-  // Add signature if it is specified in the params
-  if (params.sign === true) {
-    const host = CONFIG.WEBSERVER.HOST
+    // Add signature if it is specified in the params
+    if (params.sign === true) {
+      const host = CONFIG.WEBSERVER.HOST
 
-    let dataToSign
-    if (params.data) {
-      dataToSign = params.data
-    } else {
-      // We do not have data to sign so we just take our host
-      // It is not ideal but the connection should be in HTTPS
-      dataToSign = host
-    }
+      let dataToSign
+      if (params.data) {
+        dataToSign = params.data
+      } else {
+        // We do not have data to sign so we just take our host
+        // It is not ideal but the connection should be in HTTPS
+        dataToSign = host
+      }
 
-    requestParams.json['signature'] = {
-      host, // Which host we pretend to be
-      signature: sign(dataToSign)
+      requestParams.json['signature'] = {
+        host, // Which host we pretend to be
+        signature: sign(dataToSign)
+      }
     }
-  }
 
-  // If there are data informations
-  if (params.data) {
-    requestParams.json['data'] = params.data
-  }
+    // If there are data informations
+    if (params.data) {
+      requestParams.json['data'] = params.data
+    }
 
-  request.post(requestParams, callback)
+    request.post(requestParams, (err, response, body) => err ? rej(err) : res({ response, body }))
+  })
 }
 
 // ---------------------------------------------------------------------------
index bbf135fa118838b0efe4ffcc99822dff380964b9..e99a48393de493e72281936b885999a3c8fefeb0 100644 (file)
@@ -1,25 +1,14 @@
 import * as express from 'express'
 
-import { pseudoRandomBytes } from 'crypto'
-
-import { logger } from './logger'
+import { pseudoRandomBytesPromise } from './core-utils'
+import { ResultList } from '../../shared'
 
 function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) {
   res.type('json').status(400).end()
 }
 
-function generateRandomString (size: number, callback: (err: Error, randomString?: string) => void) {
-  pseudoRandomBytes(size, function (err, raw) {
-    if (err) return callback(err)
-
-    callback(null, raw.toString('hex'))
-  })
-}
-
-function createEmptyCallback () {
-  return function (err) {
-    if (err) logger.error('Error in empty callback.', { error: err })
-  }
+function generateRandomString (size: number) {
+  return pseudoRandomBytesPromise(size).then(raw => raw.toString('hex'))
 }
 
 interface FormatableToJSON {
@@ -33,17 +22,18 @@ function getFormatedObjects<U, T extends FormatableToJSON> (objects: T[], object
     formatedObjects.push(object.toFormatedJSON())
   })
 
-  return {
+  const res: ResultList<U> = {
     total: objectsTotal,
     data: formatedObjects
   }
+
+  return res
 }
 
 // ---------------------------------------------------------------------------
 
 export {
   badRequest,
-  createEmptyCallback,
   generateRandomString,
   getFormatedObjects
 }
index 7007f2c0b2fa92abbfc9ff6025fa2612e574541d..fb69e05fc5c91cad5e2af0a1b5ff777ca407905b 100644 (file)
@@ -2,6 +2,7 @@ import * as config from 'config'
 
 import { database as db } from './database'
 import { CONFIG } from './constants'
+import { promisify0 } from '../helpers/core-utils'
 
 // Some checks on configuration files
 function checkConfig () {
@@ -35,41 +36,36 @@ function checkMissedConfig () {
 }
 
 // Check the available codecs
-function checkFFmpeg (callback: (err: Error) => void) {
+function checkFFmpeg () {
   const Ffmpeg = require('fluent-ffmpeg')
-
-  Ffmpeg.getAvailableCodecs(function (err, codecs) {
-    if (err) return callback(err)
-    if (CONFIG.TRANSCODING.ENABLED === false) return callback(null)
-
-    const canEncode = [ 'libx264' ]
-    canEncode.forEach(function (codec) {
-      if (codecs[codec] === undefined) {
-        return callback(new Error('Unknown codec ' + codec + ' in FFmpeg.'))
-      }
-
-      if (codecs[codec].canEncode !== true) {
-        return callback(new Error('Unavailable encode codec ' + codec + ' in FFmpeg'))
-      }
+  const getAvailableCodecsPromise = promisify0(Ffmpeg.getAvailableCodecs)
+
+  getAvailableCodecsPromise()
+    .then(codecs => {
+      if (CONFIG.TRANSCODING.ENABLED === false) return undefined
+
+      const canEncode = [ 'libx264' ]
+      canEncode.forEach(function (codec) {
+        if (codecs[codec] === undefined) {
+          throw new Error('Unknown codec ' + codec + ' in FFmpeg.')
+        }
+
+        if (codecs[codec].canEncode !== true) {
+          throw new Error('Unavailable encode codec ' + codec + ' in FFmpeg')
+        }
+      })
     })
-
-    return callback(null)
-  })
 }
 
-function clientsExist (callback: (err: Error, clientsExist?: boolean) => void) {
-  db.OAuthClient.countTotal(function (err, totalClients) {
-    if (err) return callback(err)
-
-    return callback(null, totalClients !== 0)
+function clientsExist () {
+  return db.OAuthClient.countTotal().then(totalClients => {
+    return totalClients !== 0
   })
 }
 
-function usersExist (callback: (err: Error, usersExist?: boolean) => void) {
-  db.User.countTotal(function (err, totalUsers) {
-    if (err) return callback(err)
-
-    return callback(null, totalUsers !== 0)
+function usersExist () {
+  return db.User.countTotal().then(totalUsers => {
+    return totalUsers !== 0
   })
 }
 
index 705dec6da402300c68ad30fab14c3f632a728718..6e3a8d009bba62512cdfccd5b0bc929ff1bc04e2 100644 (file)
@@ -1,12 +1,12 @@
-import * as fs from 'fs'
 import { join } from 'path'
+import { flattenDepth } from 'lodash'
 import * as Sequelize from 'sequelize'
-import { each } from 'async'
+import * as Promise from 'bluebird'
 
 import { CONFIG } from './constants'
 // Do not use barrel, we need to load database first
 import { logger } from '../helpers/logger'
-import { isTestInstance } from '../helpers/core-utils'
+import { isTestInstance, readdirPromise } from '../helpers/core-utils'
 import {
   ApplicationModel,
   AuthorModel,
@@ -33,7 +33,7 @@ const password = CONFIG.DATABASE.PASSWORD
 
 const database: {
   sequelize?: Sequelize.Sequelize,
-  init?: (silent: any, callback: any) => void,
+  init?: (silent: boolean) => Promise<void>,
 
   Application?: ApplicationModel,
   Author?: AuthorModel,
@@ -72,19 +72,17 @@ const sequelize = new Sequelize(dbname, username, password, {
 
 database.sequelize = sequelize
 
-database.init = function (silent: boolean, callback: (err: Error) => void) {
+database.init = function (silent: boolean) {
   const modelDirectory = join(__dirname, '..', 'models')
 
-  getModelFiles(modelDirectory, function (err, filePaths) {
-    if (err) throw err
-
-    filePaths.forEach(function (filePath) {
+  return getModelFiles(modelDirectory).then(filePaths => {
+    filePaths.forEach(filePath => {
       const model = sequelize.import(filePath)
 
       database[model['name']] = model
     })
 
-    Object.keys(database).forEach(function (modelName) {
+    Object.keys(database).forEach(modelName => {
       if ('associate' in database[modelName]) {
         database[modelName].associate(database)
       }
@@ -92,7 +90,7 @@ database.init = function (silent: boolean, callback: (err: Error) => void) {
 
     if (!silent) logger.info('Database %s is ready.', dbname)
 
-    return callback(null)
+    return undefined
   })
 }
 
@@ -104,49 +102,50 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function getModelFiles (modelDirectory: string, callback: (err: Error, filePaths: string[]) => void) {
-  fs.readdir(modelDirectory, function (err, files) {
-    if (err) throw err
-
-    const directories = files.filter(function (directory) {
-      // Find directories
-      if (
-        directory.endsWith('.js.map') ||
-        directory === 'index.js' || directory === 'index.ts' ||
-        directory === 'utils.js' || directory === 'utils.ts'
-      ) return false
+function getModelFiles (modelDirectory: string) {
+  return readdirPromise(modelDirectory)
+    .then(files => {
+      const directories: string[] = files.filter(function (directory) {
+        // Find directories
+        if (
+          directory.endsWith('.js.map') ||
+          directory === 'index.js' || directory === 'index.ts' ||
+          directory === 'utils.js' || directory === 'utils.ts'
+        ) return false
+
+        return true
+      })
 
-      return true
+      return directories
     })
-
-    let modelFilePaths: string[] = []
-
-    // For each directory we read it and append model in the modelFilePaths array
-    each(directories, function (directory: string, eachCallback: ErrorCallback<Error>) {
-      const modelDirectoryPath = join(modelDirectory, directory)
-
-      fs.readdir(modelDirectoryPath, function (err, files) {
-        if (err) return eachCallback(err)
-
-        const filteredFiles = files.filter(file => {
-          if (
-            file === 'index.js' || file === 'index.ts' ||
-            file === 'utils.js' || file === 'utils.ts' ||
-            file.endsWith('-interface.js') || file.endsWith('-interface.ts') ||
-            file.endsWith('.js.map')
-          ) return false
-
-          return true
-        }).map(file => {
-          return join(modelDirectoryPath, file)
+    .then(directories => {
+      const tasks = []
+
+      // For each directory we read it and append model in the modelFilePaths array
+      directories.forEach(directory => {
+        const modelDirectoryPath = join(modelDirectory, directory)
+
+        const promise = readdirPromise(modelDirectoryPath).then(files => {
+          const filteredFiles = files.filter(file => {
+            if (
+              file === 'index.js' || file === 'index.ts' ||
+              file === 'utils.js' || file === 'utils.ts' ||
+              file.endsWith('-interface.js') || file.endsWith('-interface.ts') ||
+              file.endsWith('.js.map')
+            ) return false
+
+            return true
+          }).map(file => join(modelDirectoryPath, file))
+
+          return filteredFiles
         })
 
-        modelFilePaths = modelFilePaths.concat(filteredFiles)
-
-        return eachCallback(null)
+        tasks.push(promise)
       })
-    }, function (err: Error) {
-      return callback(err, modelFilePaths)
+
+      return Promise.all(tasks)
+    })
+    .then((filteredFiles: string[][]) => {
+      return flattenDepth<string>(filteredFiles, 1)
     })
-  })
 }
index f105c82924d52e248c514802285034597cc18ded..1ec24c4ade1555aeb1bb7da536906e76656669e3 100644 (file)
@@ -1,37 +1,19 @@
 import { join } from 'path'
 import * as config from 'config'
-import { each, series } from 'async'
-import * as mkdirp from 'mkdirp'
 import * as passwordGenerator from 'password-generator'
+import * as Promise from 'bluebird'
 
 import { database as db } from './database'
 import { USER_ROLES, CONFIG, LAST_MIGRATION_VERSION } from './constants'
 import { clientsExist, usersExist } from './checker'
-import { logger, createCertsIfNotExist, root } from '../helpers'
-
-function installApplication (callback: (err: Error) => void) {
-  series([
-    function createDatabase (callbackAsync) {
-      db.sequelize.sync().asCallback(callbackAsync)
-      // db.sequelize.sync({ force: true }).asCallback(callbackAsync)
-    },
-
-    function createDirectories (callbackAsync) {
-      createDirectoriesIfNotExist(callbackAsync)
-    },
-
-    function createCertificates (callbackAsync) {
-      createCertsIfNotExist(callbackAsync)
-    },
-
-    function createOAuthClient (callbackAsync) {
-      createOAuthClientIfNotExist(callbackAsync)
-    },
-
-    function createOAuthUser (callbackAsync) {
-      createOAuthAdminIfNotExist(callbackAsync)
-    }
-  ], callback)
+import { logger, createCertsIfNotExist, root, mkdirpPromise } from '../helpers'
+
+function installApplication () {
+  return db.sequelize.sync()
+    .then(() => createDirectoriesIfNotExist())
+    .then(() => createCertsIfNotExist())
+    .then(() => createOAuthClientIfNotExist())
+    .then(() => createOAuthAdminIfNotExist())
 }
 
 // ---------------------------------------------------------------------------
@@ -42,21 +24,22 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function createDirectoriesIfNotExist (callback: (err: Error) => void) {
+function createDirectoriesIfNotExist () {
   const storages = config.get('storage')
 
-  each(Object.keys(storages), function (key, callbackEach) {
+  const tasks = []
+  Object.keys(storages).forEach(key => {
     const dir = storages[key]
-    mkdirp(join(root(), dir), callbackEach)
-  }, callback)
-}
+    tasks.push(mkdirpPromise(join(root(), dir)))
+  })
 
-function createOAuthClientIfNotExist (callback: (err: Error) => void) {
-  clientsExist(function (err, exist) {
-    if (err) return callback(err)
+  return Promise.all(tasks)
+}
 
+function createOAuthClientIfNotExist () {
+  return clientsExist().then(exist => {
     // Nothing to do, clients already exist
-    if (exist === true) return callback(null)
+    if (exist === true) return undefined
 
     logger.info('Creating a default OAuth Client.')
 
@@ -69,23 +52,19 @@ function createOAuthClientIfNotExist (callback: (err: Error) => void) {
       redirectUris: null
     })
 
-    client.save().asCallback(function (err, createdClient) {
-      if (err) return callback(err)
-
+    return client.save().then(createdClient => {
       logger.info('Client id: ' + createdClient.clientId)
       logger.info('Client secret: ' + createdClient.clientSecret)
 
-      return callback(null)
+      return undefined
     })
   })
 }
 
-function createOAuthAdminIfNotExist (callback: (err: Error) => void) {
-  usersExist(function (err, exist) {
-    if (err) return callback(err)
-
+function createOAuthAdminIfNotExist () {
+  return usersExist().then(exist => {
     // Nothing to do, users already exist
-    if (exist === true) return callback(null)
+    if (exist === true) return undefined
 
     logger.info('Creating the administrator.')
 
@@ -116,14 +95,12 @@ function createOAuthAdminIfNotExist (callback: (err: Error) => void) {
       role
     }
 
-    db.User.create(userData, createOptions).asCallback(function (err, createdUser) {
-      if (err) return callback(err)
-
+    return db.User.create(userData, createOptions).then(createdUser => {
       logger.info('Username: ' + username)
       logger.info('User password: ' + password)
 
       logger.info('Creating Application table.')
-      db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION }).asCallback(callback)
+      return db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
     })
   })
 }
index a9200c47f3b14cc30e602099e1989a625a2f7595..ceefaad4a5f1b059cb20c03ad3ae0eb1e6773c98 100644 (file)
@@ -1,9 +1,12 @@
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.STRING(400),
@@ -11,27 +14,16 @@ function up (utils, finalCallback) {
     defaultValue: ''
   }
 
-  waterfall([
-
-    function addEmailColumn (callback) {
-      q.addColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function updateWithFakeEmails (callback) {
+  return q.addColumn('Pods', 'email', data)
+    .then(() => {
       const query = 'UPDATE "Pods" SET "email" = \'dummy@example.com\''
-      utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function nullOnDefault (callback) {
+      return utils.sequelize.query(query, { transaction: utils.transaction })
+    })
+    .then(() => {
       data.defaultValue = null
 
-      q.changeColumn('Pods', 'email', data, { transaction: utils.transaction }).asCallback(callback)
-    }
-  ], finalCallback)
+      return q.changeColumn('Pods', 'email', data)
+    })
 }
 
 function down (options, callback) {
index 4b5d29394c1eee6324c49a997168a5d4c55403e5..e8865acdb9e2ffceb576ca48b8e825242db8a4a6 100644 (file)
@@ -1,37 +1,28 @@
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.STRING(400),
     allowNull: false,
     defaultValue: ''
   }
-
-  waterfall([
-
-    function addEmailColumn (callback) {
-      q.addColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function updateWithFakeEmails (callback) {
+  return q.addColumn('Users', 'email', data)
+    .then(() => {
       const query = 'UPDATE "Users" SET "email" = CONCAT("username", \'@example.com\')'
-      utils.sequelize.query(query, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function nullOnDefault (callback) {
+      return utils.sequelize.query(query, { transaction: utils.transaction })
+    })
+    .then(() => {
       data.defaultValue = null
 
-      q.changeColumn('Users', 'email', data, { transaction: utils.transaction }).asCallback(callback)
-    }
-  ], finalCallback)
+      return q.changeColumn('Users', 'email', data)
+    })
 }
 
 function down (options, callback) {
index e7086940444cfcb38fc9f65e2df446dcc58731ea..df274d81740d78b12cf419dc1e7bf738074720c6 100644 (file)
@@ -1,7 +1,12 @@
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -9,7 +14,7 @@ function up (utils, finalCallback) {
     defaultValue: 0
   }
 
-  q.addColumn('Videos', 'views', data, { transaction: utils.transaction }).asCallback(finalCallback)
+  return q.addColumn('Videos', 'views', data)
 }
 
 function down (options, callback) {
index e435d06575e930697b51b5256567ca9dd30d0c6e..3d7182d0a0d459d62b30c7ce0111b089e419832f 100644 (file)
@@ -1,7 +1,12 @@
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -9,7 +14,7 @@ function up (utils, finalCallback) {
     defaultValue: 0
   }
 
-  q.addColumn('Videos', 'likes', data, { transaction: utils.transaction }).asCallback(finalCallback)
+  return q.addColumn('Videos', 'likes', data)
 }
 
 function down (options, callback) {
index 57e54e9046a868adb4885b8ebf3e8d780f28b246..ed41095dcc7537896e7025201fdd263311312486 100644 (file)
@@ -1,7 +1,12 @@
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -9,7 +14,7 @@ function up (utils, finalCallback) {
     defaultValue: 0
   }
 
-  q.addColumn('Videos', 'dislikes', data, { transaction: utils.transaction }).asCallback(finalCallback)
+  return q.addColumn('Videos', 'dislikes', data)
 }
 
 function down (options, callback) {
index 1073f449cd554191864c2cc784de09f0dfeee00d..f5adee8f9122ace8e67614b21316fc9e492b8e6b 100644 (file)
@@ -1,9 +1,12 @@
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -11,20 +14,12 @@ function up (utils, finalCallback) {
     defaultValue: 0
   }
 
-  waterfall([
-
-    function addCategoryColumn (callback) {
-      q.addColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function nullOnDefault (callback) {
+  return q.addColumn('Videos', 'category', data)
+    .then(() => {
       data.defaultValue = null
 
-      q.changeColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(callback)
-    }
-  ], finalCallback)
+      return q.changeColumn('Videos', 'category', data)
+    })
 }
 
 function down (options, callback) {
index 9316b3c374bb22a7ebac3719f07823ae01913241..00c64d8e7a4068aa0d69ceecd770ecdff5921869 100644 (file)
@@ -1,9 +1,12 @@
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -11,20 +14,11 @@ function up (utils, finalCallback) {
     defaultValue: 0
   }
 
-  waterfall([
-
-    function addLicenceColumn (callback) {
-      q.addColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function nullOnDefault (callback) {
+  return q.addColumn('Videos', 'licence', data)
+    .then(() => {
       data.defaultValue = null
-
-      q.changeColumn('Videos', 'licence', data, { transaction: utils.transaction }).asCallback(callback)
-    }
-  ], finalCallback)
+      return q.changeColumn('Videos', 'licence', data)
+    })
 }
 
 function down (options, callback) {
index c61f496f1bd0bcd942a5307c367117a2d2638760..046876b61ebe7d2c520dcbfa789f0d0d88da15aa 100644 (file)
@@ -1,9 +1,12 @@
-import { waterfall } from 'async'
-
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.BOOLEAN,
@@ -11,20 +14,12 @@ function up (utils, finalCallback) {
     defaultValue: false
   }
 
-  waterfall([
-
-    function addNSFWColumn (callback) {
-      q.addColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(function (err) {
-        return callback(err)
-      })
-    },
-
-    function nullOnDefault (callback) {
+  return q.addColumn('Videos', 'nsfw', data)
+    .then(() => {
       data.defaultValue = null
 
-      q.changeColumn('Videos', 'nsfw', data, { transaction: utils.transaction }).asCallback(callback)
-    }
-  ], finalCallback)
+      return q.changeColumn('Videos', 'nsfw', data)
+    })
 }
 
 function down (options, callback) {
index 1ca31779514a2fe68cb5222b6ea6926acadb2817..75bd3bbeaccc01ce43739bcf72fc978bd241e4ce 100644 (file)
@@ -1,7 +1,12 @@
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.BOOLEAN,
@@ -9,7 +14,7 @@ function up (utils, finalCallback) {
     defaultValue: false
   }
 
-  q.addColumn('Users', 'displayNSFW', data, { transaction: utils.transaction }).asCallback(finalCallback)
+  return q.addColumn('Users', 'displayNSFW', data)
 }
 
 function down (options, callback) {
index 95d0a473a27f583ebffa89e371b0a8debd29f340..ed08f5866efc9431dd64cf5d4e29dabb8d5d26c5 100644 (file)
@@ -1,7 +1,12 @@
-// utils = { transaction, queryInterface, sequelize, Sequelize }
-function up (utils, finalCallback) {
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
   const q = utils.queryInterface
-  const Sequelize = utils.Sequelize
 
   const data = {
     type: Sequelize.INTEGER,
@@ -9,7 +14,7 @@ function up (utils, finalCallback) {
     defaultValue: null
   }
 
-  q.addColumn('Videos', 'language', data, { transaction: utils.transaction }).asCallback(finalCallback)
+  return q.addColumn('Videos', 'language', data)
 }
 
 function down (options, callback) {
index d8faaebc6d12d2caca680001fe1d906363b1672a..d381551b583bec39aa1b8b9b0908b7122b48c921 100644 (file)
@@ -1,70 +1,54 @@
-import { waterfall, eachSeries } from 'async'
-import * as fs from 'fs'
 import * as path from 'path'
-import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { database as db } from './database'
 import { LAST_MIGRATION_VERSION } from './constants'
-import { logger } from '../helpers'
-
-function migrate (finalCallback: (err: Error) => void) {
-  waterfall([
-
-    function checkApplicationTableExists (callback) {
-      db.sequelize.getQueryInterface().showAllTables().asCallback(function (err, tables) {
-        if (err) return callback(err)
-
-        // No tables, we don't need to migrate anything
-        // The installer will do that
-        if (tables.length === 0) return finalCallback(null)
-
-        return callback(null)
-      })
-    },
-
-    function loadMigrationVersion (callback) {
-      db.Application.loadMigrationVersion(callback)
-    },
-
-    function createMigrationRowIfNotExists (actualVersion, callback) {
+import { logger, readdirPromise } from '../helpers'
+
+function migrate () {
+  const p = db.sequelize.getQueryInterface().showAllTables()
+    .then(tables => {
+      // No tables, we don't need to migrate anything
+      // The installer will do that
+      if (tables.length === 0) throw null
+    })
+    .then(() => {
+      return db.Application.loadMigrationVersion()
+    })
+    .then(actualVersion => {
       if (actualVersion === null) {
-        db.Application.create({
-          migrationVersion: 0
-        }, function (err) {
-          return callback(err, 0)
-        })
+        return db.Application.create({ migrationVersion: 0 }).then(() => 0)
       }
 
-      return callback(null, actualVersion)
-    },
-
-    function abortMigrationIfNotNeeded (actualVersion, callback) {
-      // No need migrations
-      if (actualVersion >= LAST_MIGRATION_VERSION) return finalCallback(null)
-
-      return callback(null, actualVersion)
-    },
+      return actualVersion
+    })
+    .then(actualVersion => {
+      // No need migrations, abort
+      if (actualVersion >= LAST_MIGRATION_VERSION) throw null
 
-    function getMigrations (actualVersion, callback) {
+      return actualVersion
+    })
+    .then(actualVersion => {
       // If there are a new migration scripts
       logger.info('Begin migrations.')
 
-      getMigrationScripts(function (err, migrationScripts) {
-        return callback(err, actualVersion, migrationScripts)
+      return getMigrationScripts().then(migrationScripts => ({ actualVersion, migrationScripts }))
+    })
+    .then(({ actualVersion, migrationScripts }) => {
+      return Promise.mapSeries(migrationScripts, entity => {
+        return executeMigration(actualVersion, entity)
       })
-    },
+    })
+    .then(() => {
+      logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
+    })
+    .catch(err => {
+      if (err === null) return undefined
 
-    function doMigrations (actualVersion, migrationScripts, callback) {
-      eachSeries(migrationScripts, function (entity: any, callbackEach) {
-        executeMigration(actualVersion, entity, callbackEach)
-      }, function (err) {
-        if (err) return callback(err)
+      throw err
+    })
 
-        logger.info('Migrations finished. New migration version schema: %s', LAST_MIGRATION_VERSION)
-        return callback(null)
-      })
-    }
-  ], finalCallback)
+  return p
 }
 
 // ---------------------------------------------------------------------------
@@ -75,12 +59,12 @@ export {
 
 // ---------------------------------------------------------------------------
 
-type GetMigrationScriptsCallback = (err: Error, filesToMigrate?: { version: string, script: string }[]) => void
-function getMigrationScripts (callback: GetMigrationScriptsCallback) {
-  fs.readdir(path.join(__dirname, 'migrations'), function (err, files) {
-    if (err) return callback(err)
-
-    const filesToMigrate = []
+function getMigrationScripts () {
+  return readdirPromise(path.join(__dirname, 'migrations')).then(files => {
+    const filesToMigrate: {
+      version: string,
+      script: string
+    }[] = []
 
     files.forEach(function (file) {
       // Filename is something like 'version-blabla.js'
@@ -91,15 +75,15 @@ function getMigrationScripts (callback: GetMigrationScriptsCallback) {
       })
     })
 
-    return callback(err, filesToMigrate)
+    return filesToMigrate
   })
 }
 
-function executeMigration (actualVersion: number, entity: { version: string, script: string }, callback: (err: Error) => void) {
+function executeMigration (actualVersion: number, entity: { version: string, script: string }) {
   const versionScript = parseInt(entity.version, 10)
 
   // Do not execute old migration scripts
-  if (versionScript <= actualVersion) return callback(null)
+  if (versionScript <= actualVersion) return undefined
 
   // Load the migration module and run it
   const migrationScriptName = entity.script
@@ -107,30 +91,17 @@ function executeMigration (actualVersion: number, entity: { version: string, scr
 
   const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
 
-  db.sequelize.transaction().asCallback(function (err, t) {
-    if (err) return callback(err)
-
+  return db.sequelize.transaction(t => {
     const options = {
       transaction: t,
       queryInterface: db.sequelize.getQueryInterface(),
-      sequelize: db.sequelize,
-      Sequelize: Sequelize
+      sequelize: db.sequelize
     }
-    migrationScript.up(options, function (err) {
-      if (err) {
-        t.rollback()
-        return callback(err)
-      }
-
-      // Update the new migration version
-      db.Application.updateMigrationVersion(versionScript, t, function (err) {
-        if (err) {
-          t.rollback()
-          return callback(err)
-        }
 
-        t.commit().asCallback(callback)
+    migrationScript.up(options)
+      .then(() => {
+        // Update the new migration version
+        db.Application.updateMigrationVersion(versionScript, t)
       })
-    })
   })
 }
index 522cb82b341bae0055ae17deca15aa6b31f594a1..498144318a78819a0a59ade60b282e4013a527a3 100644 (file)
@@ -1,6 +1,6 @@
-import { each, eachLimit, eachSeries, series, waterfall } from 'async'
 import * as request from 'request'
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { database as db } from '../initializers/database'
 import {
@@ -15,8 +15,7 @@ import {
   logger,
   getMyPublicCert,
   makeSecureRequest,
-  makeRetryRequest,
-  createEmptyCallback
+  makeRetryRequest
 } from '../helpers'
 import {
   RequestScheduler,
@@ -53,24 +52,24 @@ function activateSchedulers () {
   requestVideoEventScheduler.activate()
 }
 
-function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction, callback: (err: Error) => void) {
+function addVideoToFriends (videoData: Object, transaction: Sequelize.Transaction) {
   const options = {
     type: ENDPOINT_ACTIONS.ADD,
     endpoint: REQUEST_ENDPOINTS.VIDEOS,
     data: videoData,
     transaction
   }
-  createRequest(options, callback)
+  return createRequest(options)
 }
 
-function updateVideoToFriends (videoData: Object, transaction: Sequelize.Transaction, callback: (err: Error) => void) {
+function updateVideoToFriends (videoData: Object, transaction: Sequelize.Transaction) {
   const options = {
     type: ENDPOINT_ACTIONS.UPDATE,
     endpoint: REQUEST_ENDPOINTS.VIDEOS,
     data: videoData,
     transaction
   }
-  createRequest(options, callback)
+  return createRequest(options)
 }
 
 function removeVideoToFriends (videoParams: Object) {
@@ -80,121 +79,93 @@ function removeVideoToFriends (videoParams: Object) {
     data: videoParams,
     transaction: null
   }
-  createRequest(options)
+  return createRequest(options)
 }
 
-function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance) {
+function reportAbuseVideoToFriend (reportData: Object, video: VideoInstance, transaction: Sequelize.Transaction) {
   const options = {
     type: ENDPOINT_ACTIONS.REPORT_ABUSE,
     endpoint: REQUEST_ENDPOINTS.VIDEOS,
     data: reportData,
     toIds: [ video.Author.podId ],
-    transaction: null
+    transaction
   }
-  createRequest(options)
+  return createRequest(options)
 }
 
-function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction, callback?: (err: Error) => void) {
+function quickAndDirtyUpdateVideoToFriends (qaduParam: QaduParam, transaction?: Sequelize.Transaction) {
   const options = {
     videoId: qaduParam.videoId,
     type: qaduParam.type,
     transaction
   }
-  return createVideoQaduRequest(options, callback)
+  return createVideoQaduRequest(options)
 }
 
-function quickAndDirtyUpdatesVideoToFriends (
-  qadusParams: QaduParam[],
-  transaction: Sequelize.Transaction,
-  finalCallback: (err: Error) => void
-) {
+function quickAndDirtyUpdatesVideoToFriends (qadusParams: QaduParam[], transaction: Sequelize.Transaction) {
   const tasks = []
 
   qadusParams.forEach(function (qaduParams) {
-    const fun = function (callback) {
-      quickAndDirtyUpdateVideoToFriends(qaduParams, transaction, callback)
-    }
-
-    tasks.push(fun)
+    tasks.push(quickAndDirtyUpdateVideoToFriends(qaduParams, transaction))
   })
 
-  series(tasks, finalCallback)
+  return Promise.all(tasks)
 }
 
-function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction, callback?: (err: Error) => void) {
+function addEventToRemoteVideo (eventParam: EventParam, transaction?: Sequelize.Transaction) {
   const options = {
     videoId: eventParam.videoId,
     type: eventParam.type,
     transaction
   }
-  createVideoEventRequest(options, callback)
+  return createVideoEventRequest(options)
 }
 
-function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction, finalCallback: (err: Error) => void) {
+function addEventsToRemoteVideo (eventsParams: EventParam[], transaction: Sequelize.Transaction) {
   const tasks = []
 
   eventsParams.forEach(function (eventParams) {
-    const fun = function (callback) {
-      addEventToRemoteVideo(eventParams, transaction, callback)
-    }
-
-    tasks.push(fun)
+    tasks.push(addEventToRemoteVideo(eventParams, transaction))
   })
 
-  series(tasks, finalCallback)
+  return Promise.all(tasks)
 }
 
-function hasFriends (callback: (err: Error, hasFriends?: boolean) => void) {
-  db.Pod.countAll(function (err, count) {
-    if (err) return callback(err)
-
-    const hasFriends = (count !== 0)
-    callback(null, hasFriends)
-  })
+function hasFriends () {
+  return db.Pod.countAll().then(count => count !== 0)
 }
 
-function makeFriends (hosts: string[], callback: (err: Error) => void) {
+function makeFriends (hosts: string[]) {
   const podsScore = {}
 
   logger.info('Make friends!')
-  getMyPublicCert(function (err, cert) {
-    if (err) {
-      logger.error('Cannot read public cert.')
-      return callback(err)
-    }
-
-    eachSeries(hosts, function (host, callbackEach) {
-      computeForeignPodsList(host, podsScore, callbackEach)
-    }, function (err: Error) {
-      if (err) return callback(err)
-
+  return getMyPublicCert()
+    .then(cert => {
+      return Promise.mapSeries(hosts, host => {
+        return computeForeignPodsList(host, podsScore)
+      }).then(() => cert)
+    })
+    .then(cert => {
       logger.debug('Pods scores computed.', { podsScore: podsScore })
       const podsList = computeWinningPods(hosts, podsScore)
       logger.debug('Pods that we keep.', { podsToKeep: podsList })
 
-      makeRequestsToWinningPods(cert, podsList, callback)
+      return makeRequestsToWinningPods(cert, podsList)
     })
-  })
 }
 
-function quitFriends (callback: (err: Error) => void) {
+function quitFriends () {
   // Stop pool requests
   requestScheduler.deactivate()
 
-  waterfall([
-    function flushRequests (callbackAsync) {
-      requestScheduler.flush(err => callbackAsync(err))
-    },
-
-    function flushVideoQaduRequests (callbackAsync) {
-      requestVideoQaduScheduler.flush(err => callbackAsync(err))
-    },
-
-    function getPodsList (callbackAsync) {
-      return db.Pod.list(callbackAsync)
-    },
-
-    function announceIQuitMyFriends (pods, callbackAsync) {
+  return requestScheduler.flush()
+    .then(() => {
+      return requestVideoQaduScheduler.flush()
+    })
+    .then(() => {
+      return db.Pod.list()
+    })
+    .then(pods => {
       const requestParams = {
         method: 'POST' as 'POST',
         path: '/api/' + API_VERSION + '/remote/pods/remove',
@@ -205,61 +176,57 @@ function quitFriends (callback: (err: Error) => void) {
       // Announce we quit them
       // We don't care if the request fails
       // The other pod will exclude us automatically after a while
-      eachLimit(pods, REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
+      return Promise.map(pods, pod => {
         requestParams.toPod = pod
-        makeSecureRequest(requestParams, callbackEach)
-      }, function (err) {
-        if (err) {
-          logger.error('Some errors while quitting friends.', { err: err })
-          // Don't stop the process
-        }
-
-        return callbackAsync(null, pods)
+        return makeSecureRequest(requestParams)
+      }, { concurrency: REQUESTS_IN_PARALLEL })
+      .then(() => pods)
+      .catch(err => {
+        logger.error('Some errors while quitting friends.', { err: err })
+        // Don't stop the process
       })
-    },
-
-    function removePodsFromDB (pods, callbackAsync) {
-      each(pods, function (pod: any, callbackEach) {
-        pod.destroy().asCallback(callbackEach)
-      }, callbackAsync)
-    }
-  ], function (err: Error) {
-    // Don't forget to re activate the scheduler, even if there was an error
-    requestScheduler.activate()
-
-    if (err) return callback(err)
+    })
+    .then(pods => {
+      const tasks = []
+      pods.forEach(pod => tasks.push(pod.destroy()))
 
-    logger.info('Removed all remote videos.')
-    return callback(null)
-  })
+      return Promise.all(pods)
+    })
+    .then(() => {
+      logger.info('Removed all remote videos.')
+      // Don't forget to re activate the scheduler, even if there was an error
+      return requestScheduler.activate()
+    })
+    .finally(() => requestScheduler.activate())
 }
 
 function sendOwnedVideosToPod (podId: number) {
-  db.Video.listOwnedAndPopulateAuthorAndTags(function (err, videosList) {
-    if (err) {
-      logger.error('Cannot get the list of videos we own.')
-      return
-    }
-
-    videosList.forEach(function (video) {
-      video.toAddRemoteJSON(function (err, remoteVideo) {
-        if (err) {
-          logger.error('Cannot convert video to remote.', { error: err })
-          // Don't break the process
-          return
-        }
-
-        const options = {
-          type: 'add',
-          endpoint: REQUEST_ENDPOINTS.VIDEOS,
-          data: remoteVideo,
-          toIds: [ podId ],
-          transaction: null
-        }
-        createRequest(options)
+  db.Video.listOwnedAndPopulateAuthorAndTags()
+    .then(videosList => {
+      const tasks = []
+      videosList.forEach(video => {
+        const promise = video.toAddRemoteJSON()
+          .then(remoteVideo => {
+            const options = {
+              type: 'add',
+              endpoint: REQUEST_ENDPOINTS.VIDEOS,
+              data: remoteVideo,
+              toIds: [ podId ],
+              transaction: null
+            }
+            return createRequest(options)
+          })
+          .catch(err => {
+            logger.error('Cannot convert video to remote.', { error: err })
+            // Don't break the process
+            return undefined
+          })
+
+        tasks.push(promise)
       })
+
+      return Promise.all(tasks)
     })
-  })
 }
 
 function getRequestScheduler () {
@@ -297,23 +264,22 @@ export {
 
 // ---------------------------------------------------------------------------
 
-function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }, callback: (err: Error) => void) {
-  getForeignPodsList(host, function (err, res) {
-    if (err) return callback(err)
-
+function computeForeignPodsList (host: string, podsScore: { [ host: string ]: number }) {
+  // TODO: type res
+  return getForeignPodsList(host).then((res: any) => {
     const foreignPodsList = res.data
 
     // Let's give 1 point to the pod we ask the friends list
     foreignPodsList.push({ host })
 
-    foreignPodsList.forEach(function (foreignPod) {
+    foreignPodsList.forEach(foreignPod => {
       const foreignPodHost = foreignPod.host
 
       if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
       else podsScore[foreignPodHost] = 1
     })
 
-    return callback(null)
+    return undefined
   })
 }
 
@@ -323,7 +289,7 @@ function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: num
   const podsList = []
   const baseScore = hosts.length / 2
 
-  Object.keys(podsScore).forEach(function (podHost) {
+  Object.keys(podsScore).forEach(podHost => {
     // If the pod is not me and with a good score we add it
     if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
       podsList.push({ host: podHost })
@@ -333,28 +299,30 @@ function computeWinningPods (hosts: string[], podsScore: { [ host: string ]: num
   return podsList
 }
 
-function getForeignPodsList (host: string, callback: (err: Error, foreignPodsList?: any) => void) {
-  const path = '/api/' + API_VERSION + '/pods'
+function getForeignPodsList (host: string) {
+  return new Promise((res, rej) => {
+    const path = '/api/' + API_VERSION + '/pods'
 
-  request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
-    if (err) return callback(err)
+    request.get(REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
+      if (err) return rej(err)
 
-    try {
-      const json = JSON.parse(body)
-      return callback(null, json)
-    } catch (err) {
-      return callback(err)
-    }
+      try {
+        const json = JSON.parse(body)
+        return res(json)
+      } catch (err) {
+        return rej(err)
+      }
+    })
   })
 }
 
-function makeRequestsToWinningPods (cert: string, podsList: PodInstance[], callback: (err: Error) => void) {
+function makeRequestsToWinningPods (cert: string, podsList: PodInstance[]) {
   // Stop pool requests
   requestScheduler.deactivate()
   // Flush pool requests
   requestScheduler.forceSend()
 
-  eachLimit(podsList, REQUESTS_IN_PARALLEL, function (pod: PodInstance, callbackEach) {
+  return Promise.map(podsList, pod => {
     const params = {
       url: REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + API_VERSION + '/pods/',
       method: 'POST' as 'POST',
@@ -365,38 +333,35 @@ function makeRequestsToWinningPods (cert: string, podsList: PodInstance[], callb
       }
     }
 
-    makeRetryRequest(params, function (err, res, body: { cert: string, email: string }) {
-      if (err) {
-        logger.error('Error with adding %s pod.', pod.host, { error: err })
+    return makeRetryRequest(params)
+      .then(({ response, body }) => {
+        body = body as { cert: string, email: string }
+
+        if (response.statusCode === 200) {
+          const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
+          return podObj.save()
+            .then(podCreated => {
+
+              // Add our videos to the request scheduler
+              sendOwnedVideosToPod(podCreated.id)
+            })
+            .catch(err => {
+              logger.error('Cannot add friend %s pod.', pod.host, { error: err })
+            })
+        } else {
+          logger.error('Status not 200 for %s pod.', pod.host)
+        }
+      })
+      .catch(err => {
+        logger.error('Error with adding %s pod.', pod.host, { error: err.stack })
         // Don't break the process
-        return callbackEach()
-      }
-
-      if (res.statusCode === 200) {
-        const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email })
-        podObj.save().asCallback(function (err, podCreated) {
-          if (err) {
-            logger.error('Cannot add friend %s pod.', pod.host, { error: err })
-            return callbackEach()
-          }
-
-          // Add our videos to the request scheduler
-          sendOwnedVideosToPod(podCreated.id)
-
-          return callbackEach()
-        })
-      } else {
-        logger.error('Status not 200 for %s pod.', pod.host)
-        return callbackEach()
-      }
-    })
-  }, function endRequests () {
+      })
+  }, { concurrency: REQUESTS_IN_PARALLEL })
+  .then(() => logger.debug('makeRequestsToWinningPods finished.'))
+  .finally(() => {
     // Final callback, we've ended all the requests
     // Now we made new friends, we can re activate the pool of requests
     requestScheduler.activate()
-
-    logger.debug('makeRequestsToWinningPods finished.')
-    return callback(null)
   })
 }
 
@@ -408,33 +373,22 @@ type CreateRequestOptions = {
   toIds?: number[]
   transaction: Sequelize.Transaction
 }
-function createRequest (options: CreateRequestOptions, callback?: (err: Error) => void) {
-  if (!callback) callback = function () { /* empty */ }
-
-  if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions, callback)
+function createRequest (options: CreateRequestOptions) {
+  if (options.toIds !== undefined) return requestScheduler.createRequest(options as RequestSchedulerOptions)
 
   // If the "toIds" pods is not specified, we send the request to all our friends
-  db.Pod.listAllIds(options.transaction, function (err, podIds) {
-    if (err) {
-      logger.error('Cannot get pod ids', { error: err })
-      return
-    }
-
+  return db.Pod.listAllIds(options.transaction).then(podIds => {
     const newOptions = Object.assign(options, { toIds: podIds })
-    return requestScheduler.createRequest(newOptions, callback)
+    return requestScheduler.createRequest(newOptions)
   })
 }
 
-function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions, callback: (err: Error) => void) {
-  if (!callback) callback = createEmptyCallback()
-
-  requestVideoQaduScheduler.createRequest(options, callback)
+function createVideoQaduRequest (options: RequestVideoQaduSchedulerOptions) {
+  return requestVideoQaduScheduler.createRequest(options)
 }
 
-function createVideoEventRequest (options: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
-  if (!callback) callback = createEmptyCallback()
-
-  requestVideoEventScheduler.createRequest(options, callback)
+function createVideoEventRequest (options: RequestVideoEventSchedulerOptions) {
+  return requestVideoEventScheduler.createRequest(options)
 }
 
 function isMe (host: string) {
index 7d0263b1554e9053ab6b64fcb74322f6c0ae023b..8abddae3585af8c2037828abfe99941016be1b06 100644 (file)
@@ -1,11 +1,9 @@
 import * as videoTranscoder from './video-transcoder'
 
-import { VideoInstance } from '../../../models'
-
 export interface JobHandler<T> {
-  process (data: object, callback: (err: Error, videoInstance?: T) => void)
-  onError (err: Error, jobId: number, video: T, callback: (err: Error) => void)
-  onSuccess (data: any, jobId: number, video: T, callback: (err: Error) => void)
+  process (data: object): T
+  onError (err: Error, jobId: number)
+  onSuccess (jobId: number, jobResult: T)
 }
 
 const jobHandlers: { [ handlerName: string ]: JobHandler<any> } = {
index 6f606a7d34dea8be474cb42357f7737837a0b5e3..e829ca813c5c1ff1170c07de5e674b64d456dc2d 100644 (file)
@@ -3,29 +3,23 @@ import { logger } from '../../../helpers'
 import { addVideoToFriends } from '../../../lib'
 import { VideoInstance } from '../../../models'
 
-function process (data: { id: string }, callback: (err: Error, videoInstance?: VideoInstance) => void) {
-  db.Video.loadAndPopulateAuthorAndPodAndTags(data.id, function (err, video) {
-    if (err) return callback(err)
-
-    video.transcodeVideofile(function (err) {
-      return callback(err, video)
-    })
+function process (data: { id: string }) {
+  return db.Video.loadAndPopulateAuthorAndPodAndTags(data.id).then(video => {
+    return video.transcodeVideofile().then(() => video)
   })
 }
 
-function onError (err: Error, jobId: number, video: VideoInstance, callback: (err: Error) => void) {
+function onError (err: Error, jobId: number) {
   logger.error('Error when transcoding video file in job %d.', jobId, { error: err })
-  return callback(null)
+  return Promise.resolve()
 }
 
-function onSuccess (data: any, jobId: number, video: VideoInstance, callback: (err: Error) => void) {
+function onSuccess (jobId: number, video: VideoInstance) {
   logger.info('Job %d is a success.', jobId)
 
-  video.toAddRemoteJSON(function (err, remoteVideo) {
-    if (err) return callback(err)
-
+  video.toAddRemoteJSON().then(remoteVideo => {
     // Now we'll add the video's meta data to our friends
-    addVideoToFriends(remoteVideo, null, callback)
+    return addVideoToFriends(remoteVideo, null)
   })
 }
 
index 2f01387e7258cfc2722efc47ed84f439e289a106..248dc7978e4157d0769e0fbf22009c0e074a7397 100644 (file)
@@ -32,37 +32,35 @@ class JobScheduler {
 
     // Finish processing jobs from a previous start
     const state = JOB_STATES.PROCESSING
-    db.Job.listWithLimit(limit, state, (err, jobs) => {
-      this.enqueueJobs(err, jobsQueue, jobs)
-
-      forever(
-        next => {
-          if (jobsQueue.length() !== 0) {
-            // Finish processing the queue first
-            return setTimeout(next, JOBS_FETCHING_INTERVAL)
-          }
-
-          const state = JOB_STATES.PENDING
-          db.Job.listWithLimit(limit, state, (err, jobs) => {
-            if (err) {
-              logger.error('Cannot list pending jobs.', { error: err })
-            } else {
-              jobs.forEach(job => {
-                jobsQueue.push(job)
-              })
+    db.Job.listWithLimit(limit, state)
+      .then(jobs => {
+        this.enqueueJobs(jobsQueue, jobs)
+
+        forever(
+          next => {
+            if (jobsQueue.length() !== 0) {
+              // Finish processing the queue first
+              return setTimeout(next, JOBS_FETCHING_INTERVAL)
             }
 
-            // Optimization: we could use "drain" from queue object
-            return setTimeout(next, JOBS_FETCHING_INTERVAL)
-          })
-        },
+            const state = JOB_STATES.PENDING
+            db.Job.listWithLimit(limit, state)
+              .then(jobs => {
+                this.enqueueJobs(jobsQueue, jobs)
 
-        err => { logger.error('Error in job scheduler queue.', { error: err }) }
-      )
-    })
+                // Optimization: we could use "drain" from queue object
+                return setTimeout(next, JOBS_FETCHING_INTERVAL)
+              })
+              .catch(err => logger.error('Cannot list pending jobs.', { error: err }))
+          },
+
+          err => logger.error('Error in job scheduler queue.', { error: err })
+        )
+      })
+      .catch(err => logger.error('Cannot list pending jobs.', { error: err }))
   }
 
-  createJob (transaction: Sequelize.Transaction, handlerName: string, handlerInputData: object, callback: (err: Error) => void) {
+  createJob (transaction: Sequelize.Transaction, handlerName: string, handlerInputData: object) {
     const createQuery = {
       state: JOB_STATES.PENDING,
       handlerName,
@@ -70,67 +68,62 @@ class JobScheduler {
     }
     const options = { transaction }
 
-    db.Job.create(createQuery, options).asCallback(callback)
+    return db.Job.create(createQuery, options)
   }
 
-  private enqueueJobs (err: Error, jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
-    if (err) {
-      logger.error('Cannot list pending jobs.', { error: err })
-    } else {
-      jobs.forEach(job => {
-        jobsQueue.push(job)
-      })
-    }
+  private enqueueJobs (jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
+    jobs.forEach(job => jobsQueue.push(job))
   }
 
   private processJob (job: JobInstance, callback: (err: Error) => void) {
     const jobHandler = jobHandlers[job.handlerName]
+    if (jobHandler === undefined) {
+      logger.error('Unknown job handler for job %s.', job.handlerName)
+      return callback(null)
+    }
 
     logger.info('Processing job %d with handler %s.', job.id, job.handlerName)
 
     job.state = JOB_STATES.PROCESSING
-    job.save().asCallback(err => {
-      if (err) return this.cannotSaveJobError(err, callback)
-
-      if (jobHandler === undefined) {
-        logger.error('Unknown job handler for job %s.', job.handlerName)
-        return callback(null)
-      }
+    return job.save()
+      .then(() => {
+        return jobHandler.process(job.handlerInputData)
+      })
+      .then(
+        result => {
+          return this.onJobSuccess(jobHandler, job, result)
+        },
 
-      return jobHandler.process(job.handlerInputData, (err, result) => {
-        if (err) {
+        err => {
           logger.error('Error in job handler %s.', job.handlerName, { error: err })
-          return this.onJobError(jobHandler, job, result, callback)
+          return this.onJobError(jobHandler, job, err)
         }
-
-        return this.onJobSuccess(jobHandler, job, result, callback)
+      )
+      .then(() => callback(null))
+      .catch(err => {
+        this.cannotSaveJobError(err)
+        return callback(err)
       })
-    })
   }
 
-  private onJobError (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any, callback: (err: Error) => void) {
+  private onJobError (jobHandler: JobHandler<any>, job: JobInstance, err: Error) {
     job.state = JOB_STATES.ERROR
 
-    job.save().asCallback(err => {
-      if (err) return this.cannotSaveJobError(err, callback)
-
-      return jobHandler.onError(err, job.id, jobResult, callback)
-    })
+    return job.save()
+      .then(() => jobHandler.onError(err, job.id))
+      .catch(err => this.cannotSaveJobError(err))
   }
 
-  private onJobSuccess (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any, callback: (err: Error) => void) {
+  private onJobSuccess (jobHandler: JobHandler<any>, job: JobInstance, jobResult: any) {
     job.state = JOB_STATES.SUCCESS
 
-    job.save().asCallback(err => {
-      if (err) return this.cannotSaveJobError(err, callback)
-
-      return jobHandler.onSuccess(err, job.id, jobResult, callback)
-    })
+    return job.save()
+      .then(() => jobHandler.onSuccess(job.id, jobResult))
+      .catch(err => this.cannotSaveJobError(err))
   }
 
-  private cannotSaveJobError (err: Error, callback: (err: Error) => void) {
+  private cannotSaveJobError (err: Error) {
     logger.error('Cannot save new job state.', { error: err })
-    return callback(err)
   }
 }
 
index 7cf42e94c284ed9b7a169403bdf5efcea91e291a..f34c9c6675832c9141f598719a8635b400c58f1b 100644 (file)
@@ -30,17 +30,10 @@ function getUser (username: string, password: string) {
   return db.User.getByUsername(username).then(function (user) {
     if (!user) return null
 
-    // We need to return a promise
-    return new Promise(function (resolve, reject) {
-      return user.isPasswordMatch(password, function (err, isPasswordMatch) {
-        if (err) return reject(err)
+    return user.isPasswordMatch(password).then(passwordMatch => {
+      if (passwordMatch === false) return null
 
-        if (isPasswordMatch === true) {
-          return resolve(user)
-        }
-
-        return resolve(null)
-      })
+      return user
     })
   })
 }
@@ -80,8 +73,6 @@ function saveToken (token: TokenInfo, client: OAuthClientInstance, user: UserIns
     tokenCreated.user = user
 
     return tokenCreated
-  }).catch(function (err) {
-    throw err
   })
 }
 
index e81ab9c3688ed5097fbacaf66d1129fe4dda0cb8..dd77fddb71fc2b4b94cccc13e9823b40ff280c32 100644 (file)
@@ -1,15 +1,16 @@
-import * as eachLimit from 'async/eachLimit'
+import { isEmpty } from 'lodash'
+import * as Promise from 'bluebird'
 
 import { database as db } from '../../initializers/database'
 import { logger, makeSecureRequest } from '../../helpers'
-import { PodInstance } from '../../models'
+import { AbstractRequestClass, AbstractRequestToPodClass, PodInstance } from '../../models'
 import {
   API_VERSION,
   REQUESTS_IN_PARALLEL,
   REQUESTS_INTERVAL
 } from '../../initializers'
 
-abstract class AbstractRequestScheduler {
+abstract class AbstractRequestScheduler <T> {
   requestInterval: number
   limitPods: number
   limitPerPod: number
@@ -24,9 +25,9 @@ abstract class AbstractRequestScheduler {
     this.requestInterval = REQUESTS_INTERVAL
   }
 
-  abstract getRequestModel ()
-  abstract getRequestToPodModel ()
-  abstract buildRequestObjects (requests: any)
+  abstract getRequestModel (): AbstractRequestClass<T>
+  abstract getRequestToPodModel (): AbstractRequestToPodClass
+  abstract buildRequestObjects (requestsGrouped: T): {}
 
   activate () {
     logger.info('Requests scheduler activated.')
@@ -55,20 +56,18 @@ abstract class AbstractRequestScheduler {
     return REQUESTS_INTERVAL - (Date.now() - this.lastRequestTimestamp)
   }
 
-  remainingRequestsCount (callback: (err: Error, total: number) => void) {
-    return this.getRequestModel().countTotalRequests(callback)
+  remainingRequestsCount () {
+    return this.getRequestModel().countTotalRequests()
   }
 
-  flush (callback: (err: Error) => void) {
-    this.getRequestModel().removeAll(callback)
+  flush () {
+    return this.getRequestModel().removeAll()
   }
 
   // ---------------------------------------------------------------------------
 
   // Make a requests to friends of a certain type
-  protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object, callback) {
-    if (!callback) callback = function () { /* empty */ }
-
+  protected makeRequest (toPod: PodInstance, requestEndpoint: string, requestsToMake: Object) {
     const params = {
       toPod: toPod,
       sign: true, // Prove our identity
@@ -79,65 +78,64 @@ abstract class AbstractRequestScheduler {
 
     // Make multiple retry requests to all of pods
     // The function fire some useful callbacks
-    makeSecureRequest(params, (err, res) => {
-      if (err || (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 204)) {
-        err = err ? err.message : 'Status code not 20x : ' + res.statusCode
+    return makeSecureRequest(params)
+      .then(({ response, body }) => {
+        if (response.statusCode !== 200 && response.statusCode !== 201 && response.statusCode !== 204) {
+          throw new Error('Status code not 20x : ' + response.statusCode)
+        }
+      })
+      .catch(err => {
         logger.error('Error sending secure request to %s pod.', toPod.host, { error: err })
 
-        return callback(err)
-      }
-
-      return callback(null)
-    })
+        throw err
+      })
   }
 
     // Make all the requests of the scheduler
   protected makeRequests () {
-    this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod, (err, requests) => {
-      if (err) {
-        logger.error('Cannot get the list of "%s".', this.description, { err: err })
-        return // Abort
-      }
-
-      // If there are no requests, abort
-      if (requests.length === 0) {
-        logger.info('No "%s" to make.', this.description)
-        return
-      }
-
-      // We want to group requests by destinations pod and endpoint
-      const requestsToMakeGrouped = this.buildRequestObjects(requests)
-
-      logger.info('Making "%s" to friends.', this.description)
-
-      const goodPods = []
-      const badPods = []
-
-      eachLimit(Object.keys(requestsToMakeGrouped), REQUESTS_IN_PARALLEL, (hashKey, callbackEach) => {
-        const requestToMake = requestsToMakeGrouped[hashKey]
-        const toPod = requestToMake.toPod
-
-        this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas, (err) => {
-          if (err) {
-            badPods.push(requestToMake.toPod.id)
-            return callbackEach()
-          }
-
-          logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
-          goodPods.push(requestToMake.toPod.id)
-
-          // Remove the pod id of these request ids
-          this.getRequestToPodModel().removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id, callbackEach)
+    return this.getRequestModel().listWithLimitAndRandom(this.limitPods, this.limitPerPod)
+      .then((requestsGrouped: T) => {
+        // We want to group requests by destinations pod and endpoint
+        const requestsToMake = this.buildRequestObjects(requestsGrouped)
+
+        // If there are no requests, abort
+        if (isEmpty(requestsToMake) === true) {
+          logger.info('No "%s" to make.', this.description)
+          return { goodPods: [], badPods: [] }
+        }
+
+        logger.info('Making "%s" to friends.', this.description)
+
+        const goodPods = []
+        const badPods = []
+
+        return Promise.map(Object.keys(requestsToMake), hashKey => {
+          const requestToMake = requestsToMake[hashKey]
+          const toPod: PodInstance = requestToMake.toPod
+
+          return this.makeRequest(toPod, requestToMake.endpoint, requestToMake.datas)
+            .then(() => {
+              logger.debug('Removing requests for pod %s.', requestToMake.toPod.id, { requestsIds: requestToMake.ids })
+              goodPods.push(requestToMake.toPod.id)
+
+              this.afterRequestHook()
+
+              // Remove the pod id of these request ids
+              return this.getRequestToPodModel().removeByRequestIdsAndPod(requestToMake.ids, requestToMake.toPod.id)
+            })
+            .catch(err => {
+              badPods.push(requestToMake.toPod.id)
+              logger.info('Cannot make request to %s.', toPod.host, { error: err })
+            })
+        }, { concurrency: REQUESTS_IN_PARALLEL }).then(() => ({ goodPods, badPods }))
+      })
+      .then(({ goodPods, badPods }) => {
+        this.afterRequestsHook()
 
-          this.afterRequestHook()
-        })
-      }, () => {
         // All the requests were made, we update the pods score
-        db.Pod.updatePodsScore(goodPods, badPods)
-
-        this.afterRequestsHook()
+        return db.Pod.updatePodsScore(goodPods, badPods)
       })
-    })
+      .catch(err => logger.error('Cannot get the list of "%s".', this.description, { error: err.stack }))
   }
 
   protected afterRequestHook () {
index 575e0227c8e397cb60c31703c159d660bb7a008d..0dd796fb070d4c0149232e7771cec83d01299fab 100644 (file)
@@ -3,10 +3,8 @@ import * as Sequelize from 'sequelize'
 import { database as db } from '../../initializers/database'
 import { AbstractRequestScheduler } from './abstract-request-scheduler'
 import { logger } from '../../helpers'
-import {
-  REQUESTS_LIMIT_PODS,
-  REQUESTS_LIMIT_PER_POD
-} from '../../initializers'
+import { REQUESTS_LIMIT_PODS, REQUESTS_LIMIT_PER_POD } from '../../initializers'
+import { RequestsGrouped } from '../../models'
 import { RequestEndpoint } from '../../../shared'
 
 export type RequestSchedulerOptions = {
@@ -17,7 +15,7 @@ export type RequestSchedulerOptions = {
   transaction: Sequelize.Transaction
 }
 
-class RequestScheduler extends AbstractRequestScheduler {
+class RequestScheduler extends AbstractRequestScheduler<RequestsGrouped> {
   constructor () {
     super()
 
@@ -36,11 +34,11 @@ class RequestScheduler extends AbstractRequestScheduler {
     return db.RequestToPod
   }
 
-  buildRequestObjects (requests: { [ toPodId: number ]: any }) {
+  buildRequestObjects (requestsGrouped: RequestsGrouped) {
     const requestsToMakeGrouped = {}
 
-    Object.keys(requests).forEach(toPodId => {
-      requests[toPodId].forEach(data => {
+    Object.keys(requestsGrouped).forEach(toPodId => {
+      requestsGrouped[toPodId].forEach(data => {
         const request = data.request
         const pod = data.pod
         const hashKey = toPodId + request.endpoint
@@ -62,12 +60,12 @@ class RequestScheduler extends AbstractRequestScheduler {
     return requestsToMakeGrouped
   }
 
-  createRequest ({ type, endpoint, data, toIds, transaction }: RequestSchedulerOptions, callback: (err: Error) => void) {
+  createRequest ({ type, endpoint, data, toIds, transaction }: RequestSchedulerOptions) {
     // TODO: check the setPods works
     const podIds = []
 
     // If there are no destination pods abort
-    if (toIds.length === 0) return callback(null)
+    if (toIds.length === 0) return undefined
 
     toIds.forEach(toPod => {
       podIds.push(toPod)
@@ -85,20 +83,18 @@ class RequestScheduler extends AbstractRequestScheduler {
       transaction
     }
 
-    return db.Request.create(createQuery, dbRequestOptions).asCallback((err, request) => {
-      if (err) return callback(err)
-
-      return request.setPods(podIds, dbRequestOptions).asCallback(callback)
-    })
+    return db.Request.create(createQuery, dbRequestOptions)
+      .then(request => {
+        return request.setPods(podIds, dbRequestOptions)
+      })
   }
 
   // ---------------------------------------------------------------------------
 
   afterRequestsHook () {
     // Flush requests with no pod
-    this.getRequestModel().removeWithEmptyTo(err => {
-      if (err) logger.error('Error when removing requests with no pods.', { error: err })
-    })
+    this.getRequestModel().removeWithEmptyTo()
+      .catch(err => logger.error('Error when removing requests with no pods.', { error: err }))
   }
 }
 
index 4bb76f4c93537141280f8c263c9ad1ff25ee917c..d4d714c02facd5271c460190e51886d11e26f04e 100644 (file)
@@ -7,6 +7,7 @@ import {
   REQUESTS_VIDEO_EVENT_LIMIT_PER_POD,
   REQUEST_VIDEO_EVENT_ENDPOINT
 } from '../../initializers'
+import { RequestsVideoEventGrouped } from '../../models'
 import { RequestVideoEventType } from '../../../shared'
 
 export type RequestVideoEventSchedulerOptions = {
@@ -16,7 +17,7 @@ export type RequestVideoEventSchedulerOptions = {
   transaction?: Sequelize.Transaction
 }
 
-class RequestVideoEventScheduler extends AbstractRequestScheduler {
+class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoEventGrouped> {
   constructor () {
     super()
 
@@ -35,7 +36,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
     return db.RequestVideoEvent
   }
 
-  buildRequestObjects (eventsToProcess: { [ toPodId: number ]: any }[]) {
+  buildRequestObjects (eventRequests: RequestsVideoEventGrouped) {
     const requestsToMakeGrouped = {}
 
     /* Example:
@@ -50,8 +51,8 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
 
     // We group video events per video and per pod
     // We add the counts of the same event types
-    Object.keys(eventsToProcess).forEach(toPodId => {
-      eventsToProcess[toPodId].forEach(eventToProcess => {
+    Object.keys(eventRequests).forEach(toPodId => {
+      eventRequests[toPodId].forEach(eventToProcess => {
         if (!eventsPerVideoPerPod[toPodId]) eventsPerVideoPerPod[toPodId] = {}
 
         if (!requestsToMakeGrouped[toPodId]) {
@@ -97,7 +98,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
     return requestsToMakeGrouped
   }
 
-  createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions, callback: (err: Error) => void) {
+  createRequest ({ type, videoId, count, transaction }: RequestVideoEventSchedulerOptions) {
     if (count === undefined) count = 1
 
     const dbRequestOptions: Sequelize.CreateOptions = {}
@@ -109,7 +110,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler {
       videoId
     }
 
-    return db.RequestVideoEvent.create(createQuery, dbRequestOptions).asCallback(callback)
+    return db.RequestVideoEvent.create(createQuery, dbRequestOptions)
   }
 }
 
index d7169cc815f46c73b3e69fb8018ef824b447d3c9..5ec7de9c206e1291aa2a6bb4bd9f13d0473f3991 100644 (file)
@@ -9,6 +9,7 @@ import {
   REQUEST_VIDEO_QADU_ENDPOINT,
   REQUEST_VIDEO_QADU_TYPES
 } from '../../initializers'
+import { RequestsVideoQaduGrouped } from '../../models'
 import { RequestVideoQaduType } from '../../../shared'
 
 export type RequestVideoQaduSchedulerOptions = {
@@ -17,7 +18,7 @@ export type RequestVideoQaduSchedulerOptions = {
   transaction?: Sequelize.Transaction
 }
 
-class RequestVideoQaduScheduler extends AbstractRequestScheduler {
+class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQaduGrouped> {
   constructor () {
     super()
 
@@ -36,7 +37,7 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler {
     return db.RequestVideoQadu
   }
 
-  buildRequestObjects (requests: { [ toPodId: number ]: any }[]) {
+  buildRequestObjects (requests: RequestsVideoQaduGrouped) {
     const requestsToMakeGrouped = {}
 
     Object.keys(requests).forEach(toPodId => {
@@ -105,20 +106,18 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler {
     return requestsToMakeGrouped
   }
 
-  createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions, callback: (err: Error) => void) {
+  createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions) {
     const dbRequestOptions: Sequelize.BulkCreateOptions = {}
     if (transaction) dbRequestOptions.transaction = transaction
 
     // Send the update to all our friends
-    db.Pod.listAllIds(transaction, function (err, podIds) {
-      if (err) return callback(err)
-
+    return db.Pod.listAllIds(transaction).then(podIds => {
       const queries = []
       podIds.forEach(podId => {
         queries.push({ type, videoId, podId })
       })
 
-      return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions).asCallback(callback)
+      return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions)
     })
   }
 }
index fbfd08c7b4cb7c44a410dd923b4164a3a948477d..0fa9ee9d2822932e50561f5a9eb130d9212fd61e 100644 (file)
@@ -9,41 +9,41 @@ import {
 
 function checkSignature (req: express.Request, res: express.Response, next: express.NextFunction) {
   const host = req.body.signature.host
-  db.Pod.loadByHost(host, function (err, pod) {
-    if (err) {
-      logger.error('Cannot get signed host in body.', { error: err })
-      return res.sendStatus(500)
-    }
+  db.Pod.loadByHost(host)
+    .then(pod => {
+      if (pod === null) {
+        logger.error('Unknown pod %s.', host)
+        return res.sendStatus(403)
+      }
 
-    if (pod === null) {
-      logger.error('Unknown pod %s.', host)
-      return res.sendStatus(403)
-    }
+      logger.debug('Checking signature from %s.', host)
 
-    logger.debug('Checking signature from %s.', host)
+      let signatureShouldBe
+      // If there is data in the body the sender used it for its signature
+      // If there is no data we just use its host as signature
+      if (req.body.data) {
+        signatureShouldBe = req.body.data
+      } else {
+        signatureShouldBe = host
+      }
 
-    let signatureShouldBe
-    // If there is data in the body the sender used it for its signature
-    // If there is no data we just use its host as signature
-    if (req.body.data) {
-      signatureShouldBe = req.body.data
-    } else {
-      signatureShouldBe = host
-    }
+      const signatureOk = peertubeCryptoCheckSignature(pod.publicKey, signatureShouldBe, req.body.signature.signature)
 
-    const signatureOk = peertubeCryptoCheckSignature(pod.publicKey, signatureShouldBe, req.body.signature.signature)
+      if (signatureOk === true) {
+        res.locals.secure = {
+          pod
+        }
 
-    if (signatureOk === true) {
-      res.locals.secure = {
-        pod
+        return next()
       }
 
-      return next()
-    }
-
-    logger.error('Signature is not okay in body for %s.', req.body.signature.host)
-    return res.sendStatus(403)
-  })
+      logger.error('Signature is not okay in body for %s.', req.body.signature.host)
+      return res.sendStatus(403)
+    })
+    .catch(err => {
+      logger.error('Cannot get signed host in body.', { error: err })
+      return res.sendStatus(500)
+    })
 }
 
 // ---------------------------------------------------------------------------
index d8eb90168a1c1014453461fc1d0d25fe80683432..da7fc2bd6eb423e5a2e9c5ab2418009fc3b64513 100644 (file)
@@ -19,19 +19,19 @@ function makeFriendsValidator (req: express.Request, res: express.Response, next
   logger.debug('Checking makeFriends parameters', { parameters: req.body })
 
   checkErrors(req, res, function () {
-    hasFriends(function (err, heHasFriends) {
-      if (err) {
+    hasFriends()
+      .then(heHasFriends => {
+        if (heHasFriends === true) {
+          // We need to quit our friends before make new ones
+          return res.sendStatus(409)
+        }
+
+        return next()
+      })
+      .catch(err => {
         logger.error('Cannot know if we have friends.', { error: err })
         res.sendStatus(500)
-      }
-
-      if (heHasFriends === true) {
-        // We need to quit our friends before make new ones
-        return res.sendStatus(409)
-      }
-
-      return next()
-    })
+      })
   })
 }
 
@@ -42,19 +42,19 @@ function podsAddValidator (req: express.Request, res: express.Response, next: ex
   logger.debug('Checking podsAdd parameters', { parameters: req.body })
 
   checkErrors(req, res, function () {
-    db.Pod.loadByHost(req.body.host, function (err, pod) {
-      if (err) {
+    db.Pod.loadByHost(req.body.host)
+      .then(pod => {
+        // Pod with this host already exists
+        if (pod) {
+          return res.sendStatus(409)
+        }
+
+        return next()
+      })
+      .catch(err => {
         logger.error('Cannot load pod by host.', { error: err })
         res.sendStatus(500)
-      }
-
-      // Pod with this host already exists
-      if (pod) {
-        return res.sendStatus(409)
-      }
-
-      return next()
-    })
+      })
   })
 }
 
index b7b9ef370079197fc390a78e1cf78bf4db013b7f..c06735047eca51b70898d40f0bf0ff4cf87b4db8 100644 (file)
@@ -13,16 +13,16 @@ function usersAddValidator (req: express.Request, res: express.Response, next: e
   logger.debug('Checking usersAdd parameters', { parameters: req.body })
 
   checkErrors(req, res, function () {
-    db.User.loadByUsernameOrEmail(req.body.username, req.body.email, function (err, user) {
-      if (err) {
+    db.User.loadByUsernameOrEmail(req.body.username, req.body.email)
+      .then(user => {
+        if (user) return res.status(409).send('User already exists.')
+
+        next()
+      })
+      .catch(err => {
         logger.error('Error in usersAdd request validator.', { error: err })
         return res.sendStatus(500)
-      }
-
-      if (user) return res.status(409).send('User already exists.')
-
-      next()
-    })
+      })
   })
 }
 
@@ -32,18 +32,18 @@ function usersRemoveValidator (req: express.Request, res: express.Response, next
   logger.debug('Checking usersRemove parameters', { parameters: req.params })
 
   checkErrors(req, res, function () {
-    db.User.loadById(req.params.id, function (err, user) {
-      if (err) {
-        logger.error('Error in usersRemove request validator.', { error: err })
-        return res.sendStatus(500)
-      }
-
-      if (!user) return res.status(404).send('User not found')
+    db.User.loadById(req.params.id)
+      .then(user => {
+        if (!user) return res.status(404).send('User not found')
 
-      if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
+        if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
 
-      next()
-    })
+        next()
+      })
+      .catch(err => {
+        logger.error('Error in usersRemove request validator.', { error: err })
+        return res.sendStatus(500)
+      })
   })
 }
 
@@ -64,16 +64,16 @@ function usersVideoRatingValidator (req: express.Request, res: express.Response,
   logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
 
   checkErrors(req, res, function () {
-    db.Video.load(req.params.videoId, function (err, video) {
-      if (err) {
+    db.Video.load(req.params.videoId)
+      .then(video => {
+        if (!video) return res.status(404).send('Video not found')
+
+        next()
+      })
+      .catch(err => {
         logger.error('Error in user request validator.', { error: err })
         return res.sendStatus(500)
-      }
-
-      if (!video) return res.status(404).send('Video not found')
-
-      next()
-    })
+      })
   })
 }
 
index 03742a522bd8e43be01319216dfe9f1e9a283696..ec452cade5482abcbfb073b6b56c99ef45f5d976 100644 (file)
@@ -1,5 +1,4 @@
 import 'express-validator'
-import * as multer from 'multer'
 import * as express from 'express'
 
 import { database as db } from '../../initializers/database'
@@ -24,18 +23,19 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
   checkErrors(req, res, function () {
     const videoFile = req.files.videofile[0]
 
-    db.Video.getDurationFromFile(videoFile.path, function (err, duration) {
-      if (err) {
-        return res.status(400).send('Cannot retrieve metadata of the file.')
-      }
-
-      if (!isVideoDurationValid(duration)) {
-        return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
-      }
+    db.Video.getDurationFromFile(videoFile.path)
+      .then(duration => {
+        if (!isVideoDurationValid('' + duration)) {
+          return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
+        }
 
-      videoFile['duration'] = duration
-      next()
-    })
+        videoFile['duration'] = duration
+        next()
+      })
+      .catch(err => {
+        logger.error('Error in getting duration from file.', { error: err })
+        res.status(400).send('Cannot retrieve metadata of the file.')
+      })
   })
 }
 
@@ -157,43 +157,42 @@ export {
 // ---------------------------------------------------------------------------
 
 function checkVideoExists (id: string, res: express.Response, callback: () => void) {
-  db.Video.loadAndPopulateAuthorAndPodAndTags(id, function (err, video) {
-    if (err) {
-      logger.error('Error in video request validator.', { error: err })
-      return res.sendStatus(500)
-    }
-
+  db.Video.loadAndPopulateAuthorAndPodAndTags(id).then(video => {
     if (!video) return res.status(404).send('Video not found')
 
     res.locals.video = video
     callback()
   })
+  .catch(err => {
+    logger.error('Error in video request validator.', { error: err })
+    return res.sendStatus(500)
+  })
 }
 
 function checkUserCanDeleteVideo (userId: number, res: express.Response, callback: () => void) {
   // Retrieve the user who did the request
-  db.User.loadById(userId, function (err, user) {
-    if (err) {
-      logger.error('Error in video request validator.', { error: err })
-      return res.sendStatus(500)
-    }
-
-    // Check if the user can delete the video
-    // The user can delete it if s/he is an admin
-    // Or if s/he is the video's author
-    if (user.isAdmin() === false) {
-      if (res.locals.video.isOwned() === false) {
-        return res.status(403).send('Cannot remove video of another pod')
-      }
-
-      if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
-        return res.status(403).send('Cannot remove video of another user')
+  db.User.loadById(userId)
+    .then(user => {
+      // Check if the user can delete the video
+      // The user can delete it if s/he is an admin
+      // Or if s/he is the video's author
+      if (user.isAdmin() === false) {
+        if (res.locals.video.isOwned() === false) {
+          return res.status(403).send('Cannot remove video of another pod')
+        }
+
+        if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
+          return res.status(403).send('Cannot remove video of another user')
+        }
       }
-    }
 
-    // If we reach this comment, we can delete the video
-    callback()
-  })
+      // If we reach this comment, we can delete the video
+      callback()
+    })
+    .catch(err => {
+      logger.error('Error in video request validator.', { error: err })
+      return res.sendStatus(500)
+    })
 }
 
 function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) {
index c03513db17bd2bbf65b7894fe9f44070deebb84a..33254ba2db6f182e523f0f5205f33319d084019d 100644 (file)
@@ -1,11 +1,13 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 export namespace ApplicationMethods {
-  export type LoadMigrationVersionCallback = (err: Error, version: number) => void
-  export type LoadMigrationVersion = (callback: LoadMigrationVersionCallback) => void
+  export type LoadMigrationVersion = () => Promise<number>
 
-  export type UpdateMigrationVersionCallback = (err: Error, applicationInstance: ApplicationAttributes) => void
-  export type UpdateMigrationVersion = (newVersion: number, transaction: Sequelize.Transaction, callback: UpdateMigrationVersionCallback) => void
+  export type UpdateMigrationVersion = (
+    newVersion: number,
+    transaction: Sequelize.Transaction
+  ) => Promise<[ number, ApplicationInstance[] ]>
 }
 
 export interface ApplicationClass {
index 0e9a1ebb3045c331b01eea8aef76af95b7100ef7..507b7a84351919a73cc46ceb29c5c1efd1977254 100644 (file)
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
 
 import { addMethodsToModel } from '../utils'
 import {
-  ApplicationClass,
   ApplicationAttributes,
   ApplicationInstance,
 
@@ -35,23 +34,19 @@ export default function defineApplication (sequelize: Sequelize.Sequelize, DataT
 
 // ---------------------------------------------------------------------------
 
-loadMigrationVersion = function (callback: ApplicationMethods.LoadMigrationVersionCallback) {
+loadMigrationVersion = function () {
   const query = {
     attributes: [ 'migrationVersion' ]
   }
 
-  return Application.findOne(query).asCallback(function (err, data) {
-    const version = data ? data.migrationVersion : null
-
-    return callback(err, version)
-  })
+  return Application.findOne(query).then(data => data ? data.migrationVersion : null)
 }
 
-updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction, callback: ApplicationMethods.UpdateMigrationVersionCallback) {
+updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction) {
   const options: Sequelize.UpdateOptions = {
     where: {},
     transaction: transaction
   }
 
-  return Application.update({ migrationVersion: newVersion }, options).asCallback(callback)
+  return Application.update({ migrationVersion: newVersion }, options)
 }
index 31b3773670d0eab841efee7c2c4c30d13202f105..ba5622977e0b317c01e65734dc31f96893891c27 100644 (file)
@@ -1,10 +1,10 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { JobState } from '../../../shared/models/job.model'
 
 export namespace JobMethods {
-  export type ListWithLimitCallback = (err: Error, jobInstances: JobInstance[]) => void
-  export type ListWithLimit = (limit: number, state: JobState, callback: ListWithLimitCallback) => void
+  export type ListWithLimit = (limit: number, state: JobState) => Promise<JobInstance[]>
 }
 
 export interface JobClass {
index 38e4e8f30aac5b71acd2b5071dfd821153073738..968f9d71ddcba2b85bcc818d8d9bc5befcca5cb2 100644 (file)
@@ -5,7 +5,6 @@ import { JOB_STATES } from '../../initializers'
 
 import { addMethodsToModel } from '../utils'
 import {
-  JobClass,
   JobInstance,
   JobAttributes,
 
@@ -49,7 +48,7 @@ export default function defineJob (sequelize: Sequelize.Sequelize, DataTypes: Se
 
 // ---------------------------------------------------------------------------
 
-listWithLimit = function (limit: number, state: JobState, callback: JobMethods.ListWithLimitCallback) {
+listWithLimit = function (limit: number, state: JobState) {
   const query = {
     order: [
       [ 'id', 'ASC' ]
@@ -60,5 +59,5 @@ listWithLimit = function (limit: number, state: JobState, callback: JobMethods.L
     }
   }
 
-  return Job.findAll(query).asCallback(callback)
+  return Job.findAll(query)
 }
index 3b4325740bca26675dd8d3b478dd9e35770fc866..3526e41595932d21a02a708d9f6bca666474e03a 100644 (file)
@@ -1,13 +1,12 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 export namespace OAuthClientMethods {
-  export type CountTotalCallback = (err: Error, total: number) => void
-  export type CountTotal = (callback: CountTotalCallback) => void
+  export type CountTotal = () => Promise<number>
 
-  export type LoadFirstClientCallback = (err: Error, client: OAuthClientInstance) => void
-  export type LoadFirstClient = (callback: LoadFirstClientCallback) => void
+  export type LoadFirstClient = () => Promise<OAuthClientInstance>
 
-  export type GetByIdAndSecret = (clientId, clientSecret) => void
+  export type GetByIdAndSecret = (clientId: string, clientSecret: string) => Promise<OAuthClientInstance>
 }
 
 export interface OAuthClientClass {
index fbc2a339317de610d1f666dee950d3d34b204104..9cc68771dad5983d959f93ea6de09094574676a5 100644 (file)
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
 
 import { addMethodsToModel } from '../utils'
 import {
-  OAuthClientClass,
   OAuthClientInstance,
   OAuthClientAttributes,
 
@@ -67,12 +66,12 @@ function associate (models) {
   })
 }
 
-countTotal = function (callback: OAuthClientMethods.CountTotalCallback) {
-  return OAuthClient.count().asCallback(callback)
+countTotal = function () {
+  return OAuthClient.count()
 }
 
-loadFirstClient = function (callback: OAuthClientMethods.LoadFirstClientCallback) {
-  return OAuthClient.findOne().asCallback(callback)
+loadFirstClient = function () {
+  return OAuthClient.findOne()
 }
 
 getByIdAndSecret = function (clientId: string, clientSecret: string) {
index 815ad5eef3c6e805a8421ce6e6851ff5d611f2b0..f2ddafa5475368503a3f41b9fec43e04089dc30a 100644 (file)
@@ -1,5 +1,5 @@
 import * as Sequelize from 'sequelize'
-import * as Bluebird from 'bluebird'
+import * as Promise from 'bluebird'
 
 import { UserModel } from '../user'
 
@@ -15,12 +15,11 @@ export type OAuthTokenInfo = {
 }
 
 export namespace OAuthTokenMethods {
-  export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Bluebird<OAuthTokenInfo>
-  export type GetByTokenAndPopulateUser = (bearerToken: string) => Bluebird<OAuthTokenInstance>
-  export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Bluebird<OAuthTokenInstance>
+  export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Promise<OAuthTokenInfo>
+  export type GetByTokenAndPopulateUser = (bearerToken: string) => Promise<OAuthTokenInstance>
+  export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Promise<OAuthTokenInstance>
 
-  export type RemoveByUserIdCallback = (err: Error) => void
-  export type RemoveByUserId = (userId, callback) => void
+  export type RemoveByUserId = (userId) => Promise<number>
 }
 
 export interface OAuthTokenClass {
index eab9cf8580a205ae4388d110c0b84904e1f918fc..8c68834651f5603ee85bcc8257d32e73c61bb67a 100644 (file)
@@ -4,7 +4,6 @@ import { logger } from '../../helpers'
 
 import { addMethodsToModel } from '../utils'
 import {
-  OAuthTokenClass,
   OAuthTokenInstance,
   OAuthTokenAttributes,
 
@@ -149,12 +148,12 @@ getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
   })
 }
 
-removeByUserId = function (userId, callback) {
+removeByUserId = function (userId: number) {
   const query = {
     where: {
       userId: userId
     }
   }
 
-  return OAuthToken.destroy(query).asCallback(callback)
+  return OAuthToken.destroy(query)
 }
index d88847c459ce7565e409c44c43ab741eba7740bd..f6963d47eecd27bba44beb8731b7e1ced9fd8929 100644 (file)
@@ -1,4 +1,5 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 // Don't use barrel, import just what we need
 import { Pod as FormatedPod } from '../../../shared/models/pod.model'
@@ -6,32 +7,23 @@ import { Pod as FormatedPod } from '../../../shared/models/pod.model'
 export namespace PodMethods {
   export type ToFormatedJSON = (this: PodInstance) => FormatedPod
 
-  export type CountAllCallback = (err: Error, total: number) => void
-  export type CountAll = (callback) => void
+  export type CountAll = () => Promise<number>
 
-  export type IncrementScoresCallback = (err: Error) => void
-  export type IncrementScores = (ids: number[], value: number, callback?: IncrementScoresCallback) => void
+  export type IncrementScores = (ids: number[], value: number) => Promise<[ number, PodInstance[] ]>
 
-  export type ListCallback = (err: Error, podInstances?: PodInstance[]) => void
-  export type List = (callback: ListCallback) => void
+  export type List = () => Promise<PodInstance[]>
 
-  export type ListAllIdsCallback = (err: Error, ids?: number[]) => void
-  export type ListAllIds = (transaction: Sequelize.Transaction, callback: ListAllIdsCallback) => void
+  export type ListAllIds = (transaction: Sequelize.Transaction) => Promise<number[]>
 
-  export type ListRandomPodIdsWithRequestCallback = (err: Error, podInstanceIds?: number[]) => void
-  export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: ListRandomPodIdsWithRequestCallback) => void
+  export type ListRandomPodIdsWithRequest = (limit: number, tableWithPods: string, tableWithPodsJoins: string) => Promise<number[]>
 
-  export type ListBadPodsCallback = (err: Error, podInstances?: PodInstance[]) => void
-  export type ListBadPods = (callback: ListBadPodsCallback) => void
+  export type ListBadPods = () => Promise<PodInstance[]>
 
-  export type LoadCallback = (err: Error, podInstance: PodInstance) => void
-  export type Load = (id: number, callback: LoadCallback) => void
+  export type Load = (id: number) => Promise<PodInstance>
 
-  export type LoadByHostCallback = (err: Error, podInstance: PodInstance) => void
-  export type LoadByHost = (host: string, callback: LoadByHostCallback) => void
+  export type LoadByHost = (host: string) => Promise<PodInstance>
 
-  export type RemoveAllCallback = (err: Error) => void
-  export type RemoveAll = (callback: RemoveAllCallback) => void
+  export type RemoveAll = () => Promise<number>
 
   export type UpdatePodsScore = (goodPods: number[], badPods: number[]) => void
 }
index 4fe7fda1c2352c1f45f2ec16251ca1f7efa9f712..9209380fc058e6d6583c02a941dec6fe791badaa 100644 (file)
@@ -1,4 +1,3 @@
-import { each, waterfall } from 'async'
 import { map } from 'lodash'
 import * as Sequelize from 'sequelize'
 
@@ -7,7 +6,6 @@ import { logger, isHostValid } from '../../helpers'
 
 import { addMethodsToModel } from '../utils'
 import {
-  PodClass,
   PodInstance,
   PodAttributes,
 
@@ -118,13 +116,11 @@ function associate (models) {
   })
 }
 
-countAll = function (callback: PodMethods.CountAllCallback) {
-  return Pod.count().asCallback(callback)
+countAll = function () {
+  return Pod.count()
 }
 
-incrementScores = function (ids: number[], value: number, callback?: PodMethods.IncrementScoresCallback) {
-  if (!callback) callback = function () { /* empty */ }
-
+incrementScores = function (ids: number[], value: number) {
   const update = {
     score: Sequelize.literal('score +' + value)
   }
@@ -139,33 +135,28 @@ incrementScores = function (ids: number[], value: number, callback?: PodMethods.
     validate: false
   }
 
-  return Pod.update(update, options).asCallback(callback)
+  return Pod.update(update, options)
 }
 
-list = function (callback: PodMethods.ListCallback) {
-  return Pod.findAll().asCallback(callback)
+list = function () {
+  return Pod.findAll()
 }
 
-listAllIds = function (transaction: Sequelize.Transaction, callback: PodMethods.ListAllIdsCallback) {
-  const query: any = {
-    attributes: [ 'id' ]
+listAllIds = function (transaction: Sequelize.Transaction) {
+  const query: Sequelize.FindOptions = {
+    attributes: [ 'id' ],
+    transaction
   }
 
-  if (transaction !== null) query.transaction = transaction
-
-  return Pod.findAll(query).asCallback(function (err: Error, pods) {
-    if (err) return callback(err)
-
-    return callback(null, map(pods, 'id'))
+  return Pod.findAll(query).then(pods => {
+    return map(pods, 'id')
   })
 }
 
-listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string, callback: PodMethods.ListRandomPodIdsWithRequestCallback) {
-  Pod.count().asCallback(function (err, count) {
-    if (err) return callback(err)
-
+listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
+  return Pod.count().then(count => {
     // Optimization...
-    if (count === 0) return callback(null, [])
+    if (count === 0) return []
 
     let start = Math.floor(Math.random() * count) - limit
     if (start < 0) start = 0
@@ -186,56 +177,55 @@ listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, ta
       }
     }
 
-    return Pod.findAll(query).asCallback(function (err, pods) {
-      if (err) return callback(err)
-
-      return callback(null, map(pods, 'id'))
+    return Pod.findAll(query).then(pods => {
+      return map(pods, 'id')
     })
   })
 }
 
-listBadPods = function (callback: PodMethods.ListBadPodsCallback) {
+listBadPods = function () {
   const query = {
     where: {
       score: { $lte: 0 }
     }
   }
 
-  return Pod.findAll(query).asCallback(callback)
+  return Pod.findAll(query)
 }
 
-load = function (id: number, callback: PodMethods.LoadCallback) {
-  return Pod.findById(id).asCallback(callback)
+load = function (id: number) {
+  return Pod.findById(id)
 }
 
-loadByHost = function (host: string, callback: PodMethods.LoadByHostCallback) {
+loadByHost = function (host: string) {
   const query = {
     where: {
       host: host
     }
   }
 
-  return Pod.findOne(query).asCallback(callback)
+  return Pod.findOne(query)
 }
 
-removeAll = function (callback: PodMethods.RemoveAllCallback) {
-  return Pod.destroy().asCallback(callback)
+removeAll = function () {
+  return Pod.destroy()
 }
 
 updatePodsScore = function (goodPods: number[], badPods: number[]) {
   logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
 
   if (goodPods.length !== 0) {
-    incrementScores(goodPods, PODS_SCORE.BONUS, function (err) {
-      if (err) logger.error('Cannot increment scores of good pods.', { error: err })
+    incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
+      logger.error('Cannot increment scores of good pods.', { error: err })
     })
   }
 
   if (badPods.length !== 0) {
-    incrementScores(badPods, PODS_SCORE.MALUS, function (err) {
-      if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
-      removeBadPods()
-    })
+    incrementScores(badPods, PODS_SCORE.MALUS)
+      .then(() => removeBadPods())
+      .catch(err => {
+        if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
+      })
   }
 }
 
@@ -243,32 +233,19 @@ updatePodsScore = function (goodPods: number[], badPods: number[]) {
 
 // Remove pods with a score of 0 (too many requests where they were unreachable)
 function removeBadPods () {
-  waterfall([
-    function findBadPods (callback) {
-      listBadPods(function (err, pods) {
-        if (err) {
-          logger.error('Cannot find bad pods.', { error: err })
-          return callback(err)
-        }
-
-        return callback(null, pods)
-      })
-    },
-
-    function removeTheseBadPods (pods, callback) {
-      each(pods, function (pod: any, callbackEach) {
-        pod.destroy().asCallback(callbackEach)
-      }, function (err) {
-        return callback(err, pods.length)
-      })
-    }
-  ], function (err, numberOfPodsRemoved) {
-    if (err) {
+  return listBadPods()
+    .then(pods => {
+      const podsRemovePromises = pods.map(pod => pod.destroy())
+      return Promise.all(podsRemovePromises).then(() => pods.length)
+    })
+    .then(numberOfPodsRemoved => {
+      if (numberOfPodsRemoved) {
+        logger.info('Removed %d pods.', numberOfPodsRemoved)
+      } else {
+        logger.info('No need to remove bad pods.')
+      }
+    })
+    .catch(err => {
       logger.error('Cannot remove bad pods.', { error: err })
-    } else if (numberOfPodsRemoved) {
-      logger.info('Removed %d pods.', numberOfPodsRemoved)
-    } else {
-      logger.info('No need to remove bad pods.')
-    }
-  })
+    })
 }
diff --git a/server/models/request/abstract-request-interface.ts b/server/models/request/abstract-request-interface.ts
new file mode 100644 (file)
index 0000000..a384f4d
--- /dev/null
@@ -0,0 +1,12 @@
+import * as Promise from 'bluebird'
+
+export interface AbstractRequestClass <T> {
+  countTotalRequests: () => Promise<number>
+  listWithLimitAndRandom: (limitPods: number, limitRequestsPerPod: number) => Promise<T>
+  removeWithEmptyTo: () => Promise<number>
+  removeAll: () => Promise<void>
+}
+
+export interface AbstractRequestToPodClass {
+  removeByRequestIdsAndPod: (ids: number[], podId: number) => Promise<number>
+}
index 824c140f5c04983c46d4780cc5b96e21492b6e03..3dd6aedc20a23299018878e40df9435e5f956cf1 100644 (file)
@@ -1,3 +1,4 @@
+export * from './abstract-request-interface'
 export * from './request-interface'
 export * from './request-to-pod-interface'
 export * from './request-video-event-interface'
index 48385063389a0819f7549058580520635fe38408..7b0ee4df9af0092430902c97a189558a596f2fe0 100644 (file)
@@ -1,5 +1,7 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
+import { AbstractRequestClass } from './abstract-request-interface'
 import { PodInstance, PodAttributes } from '../pod'
 import { RequestEndpoint } from '../../../shared/models/request-scheduler.model'
 
@@ -11,20 +13,16 @@ export type RequestsGrouped = {
 }
 
 export namespace RequestMethods {
-  export type CountTotalRequestsCallback = (err: Error, total: number) => void
-  export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+  export type CountTotalRequests = () => Promise<number>
 
-  export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsGrouped) => void
-  export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback: ListWithLimitAndRandomCallback) => void
+  export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsGrouped>
 
-  export type RemoveWithEmptyToCallback = (err: Error) => void
-  export type RemoveWithEmptyTo = (callback: RemoveWithEmptyToCallback) => void
+  export type RemoveWithEmptyTo = () => Promise<number>
 
-  export type RemoveAllCallback = (err: Error) => void
-  export type RemoveAll = (callback: RemoveAllCallback) => void
+  export type RemoveAll = () => Promise<void>
 }
 
-export interface RequestClass {
+export interface RequestClass extends AbstractRequestClass<RequestsGrouped> {
   countTotalRequests: RequestMethods.CountTotalRequests
   listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom
   removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo
index 6d75ca6e51f6986e7ce4fc540cbb684e7939c836..7ca99f4d4955a9340230c476544354ce52986fed 100644 (file)
@@ -1,11 +1,13 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+import { AbstractRequestToPodClass } from './abstract-request-interface'
 
 export namespace RequestToPodMethods {
-  export type RemoveByRequestIdsAndPodCallback = (err: Error) => void
-  export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number, callback?: RemoveByRequestIdsAndPodCallback) => void
+  export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number) => Promise<number>
 }
 
-export interface RequestToPodClass {
+export interface RequestToPodClass extends AbstractRequestToPodClass {
   removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod
 }
 
index 67331be1d2321c8f373339ed5849496b36542e62..6678ed290f4fc9ed8a5f999b0efc21bf0bb521b6 100644 (file)
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
 
 import { addMethodsToModel } from '../utils'
 import {
-  RequestToPodClass,
   RequestToPodInstance,
   RequestToPodAttributes,
 
@@ -38,9 +37,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
 
 // ---------------------------------------------------------------------------
 
-removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callback?: RequestToPodMethods.RemoveByRequestIdsAndPodCallback) {
-  if (!callback) callback = function () { /* empty */ }
-
+removeByRequestIdsAndPod = function (requestsIds: number[], podId: number) {
   const query = {
     where: {
       requestId: {
@@ -50,5 +47,5 @@ removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callb
     }
   }
 
-  RequestToPod.destroy(query).asCallback(callback)
+  return RequestToPod.destroy(query)
 }
index 3ed03339a4ff192997e40ba076ba021831c05e38..a5032e1b13222679b7e82adb247f656c8949a1a9 100644 (file)
@@ -1,5 +1,7 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
+import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
 import { VideoInstance } from '../video'
 import { PodInstance } from '../pod'
 
@@ -16,20 +18,16 @@ export type RequestsVideoEventGrouped = {
 }
 
 export namespace RequestVideoEventMethods {
-  export type CountTotalRequestsCallback = (err: Error, total: number) => void
-  export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+  export type CountTotalRequests = () => Promise<number>
 
-  export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoEventGrouped) => void
-  export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
+  export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoEventGrouped>
 
-  export type RemoveByRequestIdsAndPodCallback = () => void
-  export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
+  export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
 
-  export type RemoveAllCallback = () => void
-  export type RemoveAll = (callback: RemoveAllCallback) => void
+  export type RemoveAll = () => Promise<void>
 }
 
-export interface RequestVideoEventClass {
+export interface RequestVideoEventClass extends AbstractRequestClass<RequestsVideoEventGrouped>, AbstractRequestToPodClass {
   countTotalRequests: RequestVideoEventMethods.CountTotalRequests
   listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom
   removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod
@@ -41,10 +39,12 @@ export interface RequestVideoEventAttributes {
   count: number
 }
 
-export interface RequestVideoEventInstance extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
+export interface RequestVideoEventInstance
+  extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance<RequestVideoEventAttributes> {
   id: number
 
   Video: VideoInstance
 }
 
-export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
+export interface RequestVideoEventModel
+  extends RequestVideoEventClass, Sequelize.Model<RequestVideoEventInstance, RequestVideoEventAttributes> {}
index f552ef50bbf9e6572e8d5d58d36061ebe48faf19..90ea15702b859ccb98d2ccd4bb5cef4cc779ee77 100644 (file)
@@ -10,7 +10,6 @@ import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers'
 import { isVideoEventCountValid } from '../../helpers'
 import { addMethodsToModel } from '../utils'
 import {
-  RequestVideoEventClass,
   RequestVideoEventInstance,
   RequestVideoEventAttributes,
 
@@ -77,23 +76,21 @@ function associate (models) {
   })
 }
 
-countTotalRequests = function (callback: RequestVideoEventMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
   const query = {}
-  return RequestVideoEvent.count(query).asCallback(callback)
+  return RequestVideoEvent.count(query)
 }
 
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoEventMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
   const Pod = db.Pod
 
   // We make a join between videos and authors to find the podId of our video event requests
   const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' +
                    'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"'
 
-  Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins, function (err, podIds) {
-    if (err) return callback(err)
-
+  return Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins).then(podIds => {
     // We don't have friends that have requests
-    if (podIds.length === 0) return callback(null, [])
+    if (podIds.length === 0) return []
 
     const query = {
       order: [
@@ -121,16 +118,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
       ]
     }
 
-    RequestVideoEvent.findAll(query).asCallback(function (err, requests) {
-      if (err) return callback(err)
-
+    return RequestVideoEvent.findAll(query).then(requests => {
       const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
-      return callback(err, requestsGrouped)
+      return requestsGrouped
     })
   })
 }
 
-removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoEventMethods.RemoveByRequestIdsAndPodCallback) {
+removeByRequestIdsAndPod = function (ids: number[], podId: number) {
   const query = {
     where: {
       id: {
@@ -152,12 +147,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
     ]
   }
 
-  RequestVideoEvent.destroy(query).asCallback(callback)
+  return RequestVideoEvent.destroy(query)
 }
 
-removeAll = function (callback: RequestVideoEventMethods.RemoveAllCallback) {
+removeAll = function () {
   // Delete all requests
-  RequestVideoEvent.truncate({ cascade: true }).asCallback(callback)
+  return RequestVideoEvent.truncate({ cascade: true })
 }
 
 // ---------------------------------------------------------------------------
index 805771cdadcfb071e7278c870ca9f24f47cc9630..9a172a4d4cd07b8b351b11bda386d6b510ac5d24 100644 (file)
@@ -1,5 +1,7 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
+import { AbstractRequestClass, AbstractRequestToPodClass } from './abstract-request-interface'
 import { VideoInstance } from '../video'
 import { PodInstance } from '../pod'
 
@@ -14,20 +16,16 @@ export type RequestsVideoQaduGrouped = {
 }
 
 export namespace RequestVideoQaduMethods {
-  export type CountTotalRequestsCallback = (err: Error, total: number) => void
-  export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void
+  export type CountTotalRequests = () => Promise<number>
 
-  export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoQaduGrouped) => void
-  export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void
+  export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number) => Promise<RequestsVideoQaduGrouped>
 
-  export type RemoveByRequestIdsAndPodCallback = () => void
-  export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void
+  export type RemoveByRequestIdsAndPod = (ids: number[], podId: number) => Promise<number>
 
-  export type RemoveAllCallback = () => void
-  export type RemoveAll = (callback: RemoveAllCallback) => void
+  export type RemoveAll = () => Promise<void>
 }
 
-export interface RequestVideoQaduClass {
+export interface RequestVideoQaduClass extends AbstractRequestClass<RequestsVideoQaduGrouped>, AbstractRequestToPodClass {
   countTotalRequests: RequestVideoQaduMethods.CountTotalRequests
   listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom
   removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod
@@ -38,11 +36,13 @@ export interface RequestVideoQaduAttributes {
   type: RequestVideoQaduType
 }
 
-export interface RequestVideoQaduInstance extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
+export interface RequestVideoQaduInstance
+  extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance<RequestVideoQaduAttributes> {
   id: number
 
   Pod: PodInstance
   Video: VideoInstance
 }
 
-export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
+export interface RequestVideoQaduModel
+  extends RequestVideoQaduClass, Sequelize.Model<RequestVideoQaduInstance, RequestVideoQaduAttributes> {}
index da62239f56ba005b1caf552dd7cdbf282162ef00..74e28f12947900ecf12624184a20d9bc2cf97235 100644 (file)
@@ -16,7 +16,6 @@ import { database as db } from '../../initializers/database'
 import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers'
 import { addMethodsToModel } from '../utils'
 import {
-  RequestVideoQaduClass,
   RequestVideoQaduInstance,
   RequestVideoQaduAttributes,
 
@@ -83,20 +82,18 @@ function associate (models) {
   })
 }
 
-countTotalRequests = function (callback: RequestVideoQaduMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
   const query = {}
-  return RequestVideoQadu.count(query).asCallback(callback)
+  return RequestVideoQadu.count(query)
 }
 
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoQaduMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
   const Pod = db.Pod
   const tableJoin = ''
 
-  Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin, function (err, podIds) {
-    if (err) return callback(err)
-
+  return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin).then(podIds => {
     // We don't have friends that have requests
-    if (podIds.length === 0) return callback(null, [])
+    if (podIds.length === 0) return []
 
     const query = {
       include: [
@@ -114,16 +111,14 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
       ]
     }
 
-    RequestVideoQadu.findAll(query).asCallback(function (err, requests) {
-      if (err) return callback(err)
-
+    return RequestVideoQadu.findAll(query).then(requests => {
       const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
-      return callback(err, requestsGrouped)
+      return requestsGrouped
     })
   })
 }
 
-removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoQaduMethods.RemoveByRequestIdsAndPodCallback) {
+removeByRequestIdsAndPod = function (ids: number[], podId: number) {
   const query = {
     where: {
       id: {
@@ -133,12 +128,12 @@ removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: Req
     }
   }
 
-  RequestVideoQadu.destroy(query).asCallback(callback)
+  return RequestVideoQadu.destroy(query)
 }
 
-removeAll = function (callback: RequestVideoQaduMethods.RemoveAllCallback) {
+removeAll = function () {
   // Delete all requests
-  RequestVideoQadu.truncate({ cascade: true }).asCallback(callback)
+  return RequestVideoQadu.truncate({ cascade: true })
 }
 
 // ---------------------------------------------------------------------------
index 66e7da845c0a90efa69695e437aecbabb3511342..c3ce2cd4eb780ba11010c73a2f4f5b8880025055 100644 (file)
@@ -5,7 +5,6 @@ import { database as db } from '../../initializers/database'
 import { REQUEST_ENDPOINTS } from '../../initializers'
 import { addMethodsToModel } from '../utils'
 import {
-  RequestClass,
   RequestInstance,
   RequestAttributes,
 
@@ -60,25 +59,23 @@ function associate (models) {
   })
 }
 
-countTotalRequests = function (callback: RequestMethods.CountTotalRequestsCallback) {
+countTotalRequests = function () {
   // We need to include Pod because there are no cascade delete when a pod is removed
   // So we could count requests that do not have existing pod anymore
   const query = {
     include: [ Request['sequelize'].models.Pod ]
   }
 
-  return Request.count(query).asCallback(callback)
+  return Request.count(query)
 }
 
-listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestMethods.ListWithLimitAndRandomCallback) {
+listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number) {
   const Pod = db.Pod
   const tableJoin = ''
 
-  Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', '', function (err, podIds) {
-    if (err) return callback(err)
-
+  return Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', tableJoin).then(podIds => {
     // We don't have friends that have requests
-    if (podIds.length === 0) return callback(null, [])
+    if (podIds.length === 0) return []
 
     // The first x requests of these pods
     // It is very important to sort by id ASC to keep the requests order!
@@ -98,23 +95,20 @@ listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: numbe
       ]
     }
 
-    Request.findAll(query).asCallback(function (err, requests) {
-      if (err) return callback(err)
+    return Request.findAll(query).then(requests => {
 
       const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
-      return callback(err, requestsGrouped)
+      return requestsGrouped
     })
   })
 }
 
-removeAll = function (callback: RequestMethods.RemoveAllCallback) {
+removeAll = function () {
   // Delete all requests
-  Request.truncate({ cascade: true }).asCallback(callback)
+  return Request.truncate({ cascade: true })
 }
 
-removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallback) {
-  if (!callback) callback = function () { /* empty */ }
-
+removeWithEmptyTo = function () {
   const query = {
     where: {
       id: {
@@ -125,7 +119,7 @@ removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallbac
     }
   }
 
-  Request.destroy(query).asCallback(callback)
+  return Request.destroy(query)
 }
 
 // ---------------------------------------------------------------------------
index 48c67678ba7c6414c3efa14866e7ae1194f52a46..f743945f83e798fee385dd4f2690c11d4c3c68cf 100644 (file)
@@ -1,35 +1,29 @@
 import * as Sequelize from 'sequelize'
-import * as Bluebird from 'bluebird'
+import * as Promise from 'bluebird'
 
 // Don't use barrel, import just what we need
 import { UserRole, User as FormatedUser } from '../../../shared/models/user.model'
+import { ResultList } from '../../../shared/models/result-list.model'
 
 export namespace UserMethods {
-  export type IsPasswordMatchCallback = (err: Error, same: boolean) => void
-  export type IsPasswordMatch = (this: UserInstance, password: string, callback: IsPasswordMatchCallback) => void
+  export type IsPasswordMatch = (this: UserInstance, password: string) => Promise<boolean>
 
   export type ToFormatedJSON = (this: UserInstance) => FormatedUser
   export type IsAdmin = (this: UserInstance) => boolean
 
-  export type CountTotalCallback = (err: Error, total: number) => void
-  export type CountTotal = (callback: CountTotalCallback) => void
+  export type CountTotal = () => Promise<number>
 
-  export type GetByUsername = (username: string) => Bluebird<UserInstance>
+  export type GetByUsername = (username: string) => Promise<UserInstance>
 
-  export type ListCallback = (err: Error, userInstances: UserInstance[]) => void
-  export type List = (callback: ListCallback) => void
+  export type List = () => Promise<UserInstance[]>
 
-  export type ListForApiCallback = (err: Error, userInstances?: UserInstance[], total?: number) => void
-  export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+  export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<UserInstance> >
 
-  export type LoadByIdCallback = (err: Error, userInstance: UserInstance) => void
-  export type LoadById = (id: number, callback: LoadByIdCallback) => void
+  export type LoadById = (id: number) => Promise<UserInstance>
 
-  export type LoadByUsernameCallback = (err: Error, userInstance: UserInstance) => void
-  export type LoadByUsername = (username: string, callback: LoadByUsernameCallback) => void
+  export type LoadByUsername = (username: string) => Promise<UserInstance>
 
-  export type LoadByUsernameOrEmailCallback = (err: Error, userInstance: UserInstance) => void
-  export type LoadByUsernameOrEmail = (username: string, email: string, callback: LoadByUsernameOrEmailCallback) => void
+  export type LoadByUsernameOrEmail = (username: string, email: string) => Promise<UserInstance>
 }
 
 export interface UserClass {
index a726639b1e7578b347cb13c68bcd2db6cc1f4d90..e0b65a13d5c714f5bc3796bc1ab43d18a80c0dc0 100644 (file)
@@ -1,10 +1,10 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { VideoRateType } from '../../../shared/models/user-video-rate.model'
 
 export namespace UserVideoRateMethods {
-  export type LoadCallback = (err: Error, userVideoRateInstance: UserVideoRateInstance) => void
-  export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: LoadCallback) => void
+  export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
 }
 
 export interface UserVideoRateClass {
index 4bdd35bc93eff627064f8127f727215de0083dc0..37d0222cfa121ebae34b287bc2e420131f62f914 100644 (file)
@@ -8,7 +8,6 @@ import { VIDEO_RATE_TYPES } from '../../initializers'
 
 import { addMethodsToModel } from '../utils'
 import {
-  UserVideoRateClass,
   UserVideoRateInstance,
   UserVideoRateAttributes,
 
@@ -66,7 +65,7 @@ function associate (models) {
   })
 }
 
-load = function (userId: number, videoId: string, transaction: Sequelize.Transaction, callback: UserVideoRateMethods.LoadCallback) {
+load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) {
   const options: Sequelize.FindOptions = {
     where: {
       userId,
@@ -75,5 +74,5 @@ load = function (userId: number, videoId: string, transaction: Sequelize.Transac
   }
   if (transaction) options.transaction = transaction
 
-  return UserVideoRate.findOne(options).asCallback(callback)
+  return UserVideoRate.findOne(options)
 }
index 6b2410259cdcfd0bac3cf74c5341b3b78f5989ef..5ff81e741dd8ee4c9bb9475fc16c0e1ff8f2d2fa 100644 (file)
@@ -13,7 +13,6 @@ import {
 
 import { addMethodsToModel } from '../utils'
 import {
-  UserClass,
   UserInstance,
   UserAttributes,
 
@@ -118,21 +117,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
 }
 
 function beforeCreateOrUpdate (user: UserInstance) {
-  return new Promise(function (resolve, reject) {
-    cryptPassword(user.password, function (err, hash) {
-      if (err) return reject(err)
-
-      user.password = hash
-
-      return resolve()
-    })
+  return cryptPassword(user.password).then(hash => {
+    user.password = hash
+    return undefined
   })
 }
 
 // ------------------------------ METHODS ------------------------------
 
-isPasswordMatch = function (this: UserInstance, password: string, callback: UserMethods.IsPasswordMatchCallback) {
-  return comparePassword(password, this.password, callback)
+isPasswordMatch = function (this: UserInstance, password: string) {
+  return comparePassword(password, this.password)
 }
 
 toFormatedJSON = function (this: UserInstance) {
@@ -164,8 +158,8 @@ function associate (models) {
   })
 }
 
-countTotal = function (callback: UserMethods.CountTotalCallback) {
-  return this.count().asCallback(callback)
+countTotal = function () {
+  return this.count()
 }
 
 getByUsername = function (username: string) {
@@ -178,44 +172,45 @@ getByUsername = function (username: string) {
   return User.findOne(query)
 }
 
-list = function (callback: UserMethods.ListCallback) {
-  return User.find().asCallback(callback)
+list = function () {
+  return User.findAll()
 }
 
-listForApi = function (start: number, count: number, sort: string, callback: UserMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
   const query = {
     offset: start,
     limit: count,
     order: [ getSort(sort) ]
   }
 
-  return User.findAndCountAll(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    return callback(null, result.rows, result.count)
+  return User.findAndCountAll(query).then(({ rows, count }) => {
+    return {
+      data: rows,
+      total: count
+    }
   })
 }
 
-loadById = function (id: number, callback: UserMethods.LoadByIdCallback) {
-  return User.findById(id).asCallback(callback)
+loadById = function (id: number) {
+  return User.findById(id)
 }
 
-loadByUsername = function (username: string, callback: UserMethods.LoadByUsernameCallback) {
+loadByUsername = function (username: string) {
   const query = {
     where: {
       username: username
     }
   }
 
-  return User.findOne(query).asCallback(callback)
+  return User.findOne(query)
 }
 
-loadByUsernameOrEmail = function (username: string, email: string, callback: UserMethods.LoadByUsernameOrEmailCallback) {
+loadByUsernameOrEmail = function (username: string, email: string) {
   const query = {
     where: {
       $or: [ { username }, { email } ]
     }
   }
 
-  return User.findOne(query).asCallback(callback)
+  return User.findOne(query)
 }
index c1b30848ca9c44111f957249470a944b3cd3d787..dbcb85b17d2ce45e78833f3621a2e580c9a8f8bc 100644 (file)
@@ -1,10 +1,15 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { PodInstance } from '../pod'
 
 export namespace AuthorMethods {
-  export type FindOrCreateAuthorCallback = (err: Error, authorInstance?: AuthorInstance) => void
-  export type FindOrCreateAuthor = (name: string, podId: number, userId: number, transaction: Sequelize.Transaction, callback: FindOrCreateAuthorCallback) => void
+  export type FindOrCreateAuthor = (
+    name: string,
+    podId: number,
+    userId: number,
+    transaction: Sequelize.Transaction
+  ) => Promise<AuthorInstance>
 }
 
 export interface AuthorClass {
index 4a115e328fe55836b39719d1eb90ccc08c102253..3222c483494f82774b82128a41b5dd578ddc64d9 100644 (file)
@@ -4,7 +4,6 @@ import { isUserUsernameValid } from '../../helpers'
 
 import { addMethodsToModel } from '../utils'
 import {
-  AuthorClass,
   AuthorInstance,
   AuthorAttributes,
 
@@ -74,30 +73,18 @@ function associate (models) {
   })
 }
 
-findOrCreateAuthor = function (
-  name: string,
-  podId: number,
-  userId: number,
-  transaction: Sequelize.Transaction,
-  callback: AuthorMethods.FindOrCreateAuthorCallback
-) {
+findOrCreateAuthor = function (name: string, podId: number, userId: number, transaction: Sequelize.Transaction) {
   const author = {
     name,
     podId,
     userId
   }
 
-  const query: any = {
+  const query: Sequelize.FindOrInitializeOptions<AuthorAttributes> = {
     where: author,
-    defaults: author
+    defaults: author,
+    transaction
   }
 
-  if (transaction !== null) query.transaction = transaction
-
-  Author.findOrCreate(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    // [ instance, wasCreated ]
-    return callback(null, result[0])
-  })
+  return Author.findOrCreate(query).then(([ authorInstance ]) => authorInstance)
 }
index e045e7ca56411ac094a9a96e8e44b113ff9bf325..08e5c32463d12415d71a5e70b1c15f1d0aa704cd 100644 (file)
@@ -1,8 +1,8 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 export namespace TagMethods {
-  export type FindOrCreateTagsCallback = (err: Error, tagInstances: TagInstance[]) => void
-  export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction, callback: FindOrCreateTagsCallback) => void
+  export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction) => Promise<TagInstance[]>
 }
 
 export interface TagClass {
index 3c657d7517961d71a0aec962c96f4e96668385b8..d0d8353d7999607482d2a522009a0590e5703ac8 100644 (file)
@@ -1,9 +1,8 @@
-import { each } from 'async'
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { addMethodsToModel } from '../utils'
 import {
-  TagClass,
   TagInstance,
   TagAttributes,
 
@@ -52,10 +51,9 @@ function associate (models) {
   })
 }
 
-findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction, callback: TagMethods.FindOrCreateTagsCallback) {
-  const tagInstances = []
-
-  each<string, Error>(tags, function (tag, callbackEach) {
+findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction) {
+  const tasks: Promise<TagInstance>[] = []
+  tags.forEach(tag => {
     const query: any = {
       where: {
         name: tag
@@ -67,15 +65,9 @@ findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction,
 
     if (transaction) query.transaction = transaction
 
-    Tag.findOrCreate(query).asCallback(function (err, res) {
-      if (err) return callbackEach(err)
-
-      // res = [ tag, isCreated ]
-      const tag = res[0]
-      tagInstances.push(tag)
-      return callbackEach()
-    })
-  }, function (err) {
-    return callback(err, tagInstances)
+    const promise = Tag.findOrCreate(query).then(([ tagInstance ]) => tagInstance)
+    tasks.push(promise)
   })
+
+  return Promise.all(tasks)
 }
index c85d09091ea732a0e460ecabc191655056ed7d14..75647fe0eac8deb58c75c066c10df2081ce361c5 100644 (file)
@@ -1,6 +1,8 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { PodInstance } from '../pod'
+import { ResultList } from '../../../shared'
 
 // Don't use barrel, import just what we need
 import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-abuse.model'
@@ -8,8 +10,7 @@ import { VideoAbuse as FormatedVideoAbuse } from '../../../shared/models/video-a
 export namespace VideoAbuseMethods {
   export type ToFormatedJSON = (this: VideoAbuseInstance) => FormatedVideoAbuse
 
-  export type ListForApiCallback = (err: Error, videoAbuseInstances?: VideoAbuseInstance[], total?: number) => void
-  export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+  export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoAbuseInstance> >
 }
 
 export interface VideoAbuseClass {
index bc5f01e2101f84f24e3f0c573ad08ba555a7680d..ab1a3ea7d393d6b3970e7e1b8a9ffdbee70209a4 100644 (file)
@@ -5,7 +5,6 @@ import { isVideoAbuseReporterUsernameValid, isVideoAbuseReasonValid } from '../.
 
 import { addMethodsToModel, getSort } from '../utils'
 import {
-  VideoAbuseClass,
   VideoAbuseInstance,
   VideoAbuseAttributes,
 
@@ -109,7 +108,7 @@ function associate (models) {
   })
 }
 
-listForApi = function (start: number, count: number, sort: string, callback: VideoAbuseMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
   const query = {
     offset: start,
     limit: count,
@@ -122,11 +121,7 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
     ]
   }
 
-  return VideoAbuse.findAndCountAll(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    return callback(null, result.rows, result.count)
+  return VideoAbuse.findAndCountAll(query).then(({ rows, count }) => {
+    return { total: count, data: rows }
   })
 }
-
-
index d4d6528d18f922573c4ce6a5f9d197c10123d05d..5ca4238016cda88b7fe4e1e4970c7989093ecd17 100644 (file)
@@ -1,4 +1,7 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+import { ResultList } from '../../../shared'
 
 // Don't use barrel, import just what we need
 import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/models/video-blacklist.model'
@@ -6,20 +9,15 @@ import { BlacklistedVideo as FormatedBlacklistedVideo } from '../../../shared/mo
 export namespace BlacklistedVideoMethods {
   export type ToFormatedJSON = (this: BlacklistedVideoInstance) => FormatedBlacklistedVideo
 
-  export type CountTotalCallback = (err: Error, total: number) => void
-  export type CountTotal = (callback: CountTotalCallback) => void
+  export type CountTotal = () => Promise<number>
 
-  export type ListCallback = (err: Error, backlistedVideoInstances: BlacklistedVideoInstance[]) => void
-  export type List = (callback: ListCallback) => void
+  export type List = () => Promise<BlacklistedVideoInstance[]>
 
-  export type ListForApiCallback = (err: Error, blacklistedVIdeoInstances?: BlacklistedVideoInstance[], total?: number) => void
-  export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
+  export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<BlacklistedVideoInstance> >
 
-  export type LoadByIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
-  export type LoadById = (id: number, callback: LoadByIdCallback) => void
+  export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
 
-  export type LoadByVideoIdCallback = (err: Error, blacklistedVideoInstance: BlacklistedVideoInstance) => void
-  export type LoadByVideoId = (id: string, callback: LoadByVideoIdCallback) => void
+  export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance>
 }
 
 export interface BlacklistedVideoClass {
@@ -35,7 +33,8 @@ export interface BlacklistedVideoAttributes {
   videoId: string
 }
 
-export interface BlacklistedVideoInstance extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
+export interface BlacklistedVideoInstance
+  extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
   id: number
   createdAt: Date
   updatedAt: Date
@@ -43,4 +42,5 @@ export interface BlacklistedVideoInstance extends BlacklistedVideoClass, Blackli
   toFormatedJSON: BlacklistedVideoMethods.ToFormatedJSON
 }
 
-export interface BlacklistedVideoModel extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
+export interface BlacklistedVideoModel
+  extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
index 3576c96f6c108e2123ef9fc1f9900c508a483540..8c42dbc21fbb2dc74681e5bb7c9ea7e9cf17cd72 100644 (file)
@@ -2,7 +2,6 @@ import * as Sequelize from 'sequelize'
 
 import { addMethodsToModel, getSort } from '../utils'
 import {
-  BlacklistedVideoClass,
   BlacklistedVideoInstance,
   BlacklistedVideoAttributes,
 
@@ -66,38 +65,39 @@ function associate (models) {
   })
 }
 
-countTotal = function (callback: BlacklistedVideoMethods.CountTotalCallback) {
-  return BlacklistedVideo.count().asCallback(callback)
+countTotal = function () {
+  return BlacklistedVideo.count()
 }
 
-list = function (callback: BlacklistedVideoMethods.ListCallback) {
-  return BlacklistedVideo.findAll().asCallback(callback)
+list = function () {
+  return BlacklistedVideo.findAll()
 }
 
-listForApi = function (start: number, count: number, sort: string, callback: BlacklistedVideoMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
   const query = {
     offset: start,
     limit: count,
     order: [ getSort(sort) ]
   }
 
-  return BlacklistedVideo.findAndCountAll(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    return callback(null, result.rows, result.count)
+  return BlacklistedVideo.findAndCountAll(query).then(({ rows, count }) => {
+    return {
+      data: rows,
+      total: count
+    }
   })
 }
 
-loadById = function (id: number, callback: BlacklistedVideoMethods.LoadByIdCallback) {
-  return BlacklistedVideo.findById(id).asCallback(callback)
+loadById = function (id: number) {
+  return BlacklistedVideo.findById(id)
 }
 
-loadByVideoId = function (id: string, callback: BlacklistedVideoMethods.LoadByIdCallback) {
+loadByVideoId = function (id: string) {
   const query = {
     where: {
       videoId: id
     }
   }
 
-  return BlacklistedVideo.find(query).asCallback(callback)
+  return BlacklistedVideo.findOne(query)
 }
index 4b591b9e73c1fe681721afea9660121135b20252..c3e3365d595f280bbfdd893fafcf34e91ee0d97e 100644 (file)
@@ -1,10 +1,12 @@
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { AuthorInstance } from './author-interface'
-import { VideoTagInstance } from './video-tag-interface'
+import { TagAttributes, TagInstance } from './tag-interface'
 
 // Don't use barrel, import just what we need
 import { Video as FormatedVideo } from '../../../shared/models/video.model'
+import { ResultList } from '../../../shared/models/result-list.model'
 
 export type FormatedAddRemoteVideo = {
   name: string
@@ -56,46 +58,32 @@ export namespace VideoMethods {
   export type IsOwned = (this: VideoInstance) => boolean
   export type ToFormatedJSON = (this: VideoInstance) => FormatedVideo
 
-  export type ToAddRemoteJSONCallback = (err: Error, videoFormated?: FormatedAddRemoteVideo) => void
-  export type ToAddRemoteJSON = (this: VideoInstance, callback: ToAddRemoteJSONCallback) => void
-
+  export type ToAddRemoteJSON = (this: VideoInstance) => Promise<FormatedAddRemoteVideo>
   export type ToUpdateRemoteJSON = (this: VideoInstance) => FormatedUpdateRemoteVideo
 
-  export type TranscodeVideofileCallback = (err: Error) => void
-  export type TranscodeVideofile = (this: VideoInstance, callback: TranscodeVideofileCallback) => void
-
-  export type GenerateThumbnailFromDataCallback = (err: Error, thumbnailName?: string) => void
-  export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string, callback: GenerateThumbnailFromDataCallback) => void
-
-  export type GetDurationFromFileCallback = (err: Error, duration?: number) => void
-  export type GetDurationFromFile = (videoPath, callback) => void
-
-  export type ListCallback = (err: Error, videoInstances: VideoInstance[]) => void
-  export type List = (callback: ListCallback) => void
-
-  export type ListForApiCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
-  export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void
-
-  export type LoadByHostAndRemoteIdCallback = (err: Error, videoInstance: VideoInstance) => void
-  export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string, callback: LoadByHostAndRemoteIdCallback) => void
-
-  export type ListOwnedAndPopulateAuthorAndTagsCallback = (err: Error, videoInstances: VideoInstance[]) => void
-  export type ListOwnedAndPopulateAuthorAndTags = (callback: ListOwnedAndPopulateAuthorAndTagsCallback) => void
-
-  export type ListOwnedByAuthorCallback = (err: Error, videoInstances: VideoInstance[]) => void
-  export type ListOwnedByAuthor = (author: string, callback: ListOwnedByAuthorCallback) => void
-
-  export type LoadCallback = (err: Error, videoInstance: VideoInstance) => void
-  export type Load = (id: string, callback: LoadCallback) => void
-
-  export type LoadAndPopulateAuthorCallback = (err: Error, videoInstance: VideoInstance) => void
-  export type LoadAndPopulateAuthor = (id: string, callback: LoadAndPopulateAuthorCallback) => void
-
-  export type LoadAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstance: VideoInstance) => void
-  export type LoadAndPopulateAuthorAndPodAndTags = (id: string, callback: LoadAndPopulateAuthorAndPodAndTagsCallback) => void
-
-  export type SearchAndPopulateAuthorAndPodAndTagsCallback = (err: Error, videoInstances?: VideoInstance[], total?: number) => void
-  export type SearchAndPopulateAuthorAndPodAndTags = (value: string, field: string, start: number, count: number, sort: string, callback: SearchAndPopulateAuthorAndPodAndTagsCallback) => void
+  export type TranscodeVideofile = (this: VideoInstance) => Promise<void>
+
+  // Return thumbnail name
+  export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string>
+  export type GetDurationFromFile = (videoPath: string) => Promise<number>
+
+  export type List = () => Promise<VideoInstance[]>
+  export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]>
+  export type ListOwnedByAuthor = (author: string) => Promise<VideoInstance[]>
+
+  export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoInstance> >
+  export type SearchAndPopulateAuthorAndPodAndTags = (
+    value: string,
+    field: string,
+    start: number,
+    count: number,
+    sort: string
+  ) => Promise< ResultList<VideoInstance> >
+
+  export type Load = (id: string) => Promise<VideoInstance>
+  export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance>
+  export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance>
+  export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance>
 }
 
 export interface VideoClass {
@@ -139,7 +127,7 @@ export interface VideoAttributes {
   dislikes?: number
 
   Author?: AuthorInstance
-  Tags?: VideoTagInstance[]
+  Tags?: TagInstance[]
 }
 
 export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
@@ -157,6 +145,8 @@ export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.In
   toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
   toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
   transcodeVideofile: VideoMethods.TranscodeVideofile
+
+  setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string>
 }
 
 export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
index 71ca85332a6f32c7715bb40f43599331dfd98794..ac45374f8fd3dd68323682c3a44225a3de005098 100644 (file)
@@ -1,12 +1,8 @@
 import * as Sequelize from 'sequelize'
 
-import { addMethodsToModel } from '../utils'
 import {
-  VideoTagClass,
   VideoTagInstance,
-  VideoTagAttributes,
-
-  VideoTagMethods
+  VideoTagAttributes
 } from './video-tag-interface'
 
 let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
index e66ebee2ddf2b1605bd9617d6754d1deb96daf6e..629051ae46c24af0b3747a897e6014b848f10667 100644 (file)
@@ -1,17 +1,15 @@
 import * as safeBuffer from 'safe-buffer'
 const Buffer = safeBuffer.Buffer
-import * as createTorrent from 'create-torrent'
 import * as ffmpeg from 'fluent-ffmpeg'
-import * as fs from 'fs'
 import * as magnetUtil from 'magnet-uri'
 import { map, values } from 'lodash'
-import { parallel, series } from 'async'
 import * as parseTorrent from 'parse-torrent'
 import { join } from 'path'
 import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
 
 import { database as db } from '../../initializers/database'
-import { VideoTagInstance } from './video-tag-interface'
+import { TagInstance } from './tag-interface'
 import {
   logger,
   isVideoNameValid,
@@ -21,7 +19,12 @@ import {
   isVideoNSFWValid,
   isVideoDescriptionValid,
   isVideoInfoHashValid,
-  isVideoDurationValid
+  isVideoDurationValid,
+  readFileBufferPromise,
+  unlinkPromise,
+  renamePromise,
+  writeFilePromise,
+  createTorrentPromise
 } from '../../helpers'
 import {
   CONSTRAINTS_FIELDS,
@@ -37,7 +40,6 @@ import { JobScheduler, removeVideoToFriends } from '../../lib'
 
 import { addMethodsToModel, getSort } from '../utils'
 import {
-  VideoClass,
   VideoInstance,
   VideoAttributes,
 
@@ -260,7 +262,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
     toFormatedJSON,
     toAddRemoteJSON,
     toUpdateRemoteJSON,
-    transcodeVideofile,
+    transcodeVideofile
   ]
   addMethodsToModel(Video, classMethods, instanceMethods)
 
@@ -276,91 +278,53 @@ function beforeValidate (video: VideoInstance) {
 }
 
 function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.Transaction }) {
-  return new Promise(function (resolve, reject) {
+  if (video.isOwned()) {
+    const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
     const tasks = []
 
-    if (video.isOwned()) {
-      const videoPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
-
-      tasks.push(
-        function createVideoTorrent (callback) {
-          createTorrentFromVideo(video, videoPath, callback)
-        },
-
-        function createVideoThumbnail (callback) {
-          createThumbnail(video, videoPath, callback)
-        },
-
-        function createVideoPreview (callback) {
-          createPreview(video, videoPath, callback)
-        }
-      )
-
-      if (CONFIG.TRANSCODING.ENABLED === true) {
-        tasks.push(
-          function createVideoTranscoderJob (callback) {
-            const dataInput = {
-              id: video.id
-            }
+    tasks.push(
+      createTorrentFromVideo(video, videoPath),
+      createThumbnail(video, videoPath),
+      createPreview(video, videoPath)
+    )
 
-            JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput, callback)
-          }
-        )
+    if (CONFIG.TRANSCODING.ENABLED === true) {
+      const dataInput = {
+        id: video.id
       }
 
-      return parallel(tasks, function (err) {
-        if (err) return reject(err)
-
-        return resolve()
-      })
+      tasks.push(
+        JobScheduler.Instance.createJob(options.transaction, 'videoTranscoder', dataInput)
+      )
     }
 
-    return resolve()
-  })
+    return Promise.all(tasks)
+  }
+
+  return Promise.resolve()
 }
 
 function afterDestroy (video: VideoInstance) {
-  return new Promise(function (resolve, reject) {
-    const tasks = []
-
-    tasks.push(
-      function (callback) {
-        removeThumbnail(video, callback)
-      }
-    )
-
-    if (video.isOwned()) {
-      tasks.push(
-        function removeVideoFile (callback) {
-          removeFile(video, callback)
-        },
+  const tasks = []
 
-        function removeVideoTorrent (callback) {
-          removeTorrent(video, callback)
-        },
-
-        function removeVideoPreview (callback) {
-          removePreview(video, callback)
-        },
-
-        function notifyFriends (callback) {
-          const params = {
-            remoteId: video.id
-          }
-
-          removeVideoToFriends(params)
+  tasks.push(
+    removeThumbnail(video)
+  )
 
-          return callback()
-        }
-      )
+  if (video.isOwned()) {
+    const removeVideoToFriendsParams = {
+      remoteId: video.id
     }
 
-    parallel(tasks, function (err) {
-      if (err) return reject(err)
+    tasks.push(
+      removeFile(video),
+      removeTorrent(video),
+      removePreview(video),
+      removeVideoToFriends(removeVideoToFriendsParams)
+    )
+  }
 
-      return resolve()
-    })
-  })
+  return Promise.all(tasks)
 }
 
 // ------------------------------ METHODS ------------------------------
@@ -488,7 +452,7 @@ toFormatedJSON = function (this: VideoInstance) {
     views: this.views,
     likes: this.likes,
     dislikes: this.dislikes,
-    tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+    tags: map<TagInstance, string>(this.Tags, 'name'),
     thumbnailPath: join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
     createdAt: this.createdAt,
     updatedAt: this.updatedAt
@@ -497,15 +461,11 @@ toFormatedJSON = function (this: VideoInstance) {
   return json
 }
 
-toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRemoteJSONCallback) {
+toAddRemoteJSON = function (this: VideoInstance) {
   // Get thumbnail data to send to the other pod
   const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
-  fs.readFile(thumbnailPath, (err, thumbnailData) => {
-    if (err) {
-      logger.error('Cannot read the thumbnail of the video')
-      return callback(err)
-    }
 
+  return readFileBufferPromise(thumbnailPath).then(thumbnailData => {
     const remoteVideo = {
       name: this.name,
       category: this.category,
@@ -518,7 +478,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
       author: this.Author.name,
       duration: this.duration,
       thumbnailData: thumbnailData.toString('binary'),
-      tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+      tags: map<TagInstance, string>(this.Tags, 'name'),
       createdAt: this.createdAt,
       updatedAt: this.updatedAt,
       extname: this.extname,
@@ -527,7 +487,7 @@ toAddRemoteJSON = function (this: VideoInstance, callback: VideoMethods.ToAddRem
       dislikes: this.dislikes
     }
 
-    return callback(null, remoteVideo)
+    return remoteVideo
   })
 }
 
@@ -543,7 +503,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
     remoteId: this.id,
     author: this.Author.name,
     duration: this.duration,
-    tags: map<VideoTagInstance, string>(this.Tags, 'name'),
+    tags: map<TagInstance, string>(this.Tags, 'name'),
     createdAt: this.createdAt,
     updatedAt: this.updatedAt,
     extname: this.extname,
@@ -555,7 +515,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
   return json
 }
 
-transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.TranscodeVideofileCallback) {
+transcodeVideofile = function (this: VideoInstance) {
   const video = this
 
   const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
@@ -563,78 +523,73 @@ transcodeVideofile = function (this: VideoInstance, finalCallback: VideoMethods.
   const videoInputPath = join(videosDirectory, video.getVideoFilename())
   const videoOutputPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
 
-  ffmpeg(videoInputPath)
-    .output(videoOutputPath)
-    .videoCodec('libx264')
-    .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
-    .outputOption('-movflags faststart')
-    .on('error', finalCallback)
-    .on('end', function () {
-      series([
-        function removeOldFile (callback) {
-          fs.unlink(videoInputPath, callback)
-        },
-
-        function moveNewFile (callback) {
-          // Important to do this before getVideoFilename() to take in account the new file extension
-          video.set('extname', newExtname)
-
-          const newVideoPath = join(videosDirectory, video.getVideoFilename())
-          fs.rename(videoOutputPath, newVideoPath, callback)
-        },
-
-        function torrent (callback) {
-          const newVideoPath = join(videosDirectory, video.getVideoFilename())
-          createTorrentFromVideo(video, newVideoPath, callback)
-        },
-
-        function videoExtension (callback) {
-          video.save().asCallback(callback)
-        }
-
-      ], function (err: Error) {
-        if (err) {
-          // Autodesctruction...
-          video.destroy().asCallback(function (err) {
-            if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
+  return new Promise<void>((res, rej) => {
+    ffmpeg(videoInputPath)
+      .output(videoOutputPath)
+      .videoCodec('libx264')
+      .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
+      .outputOption('-movflags faststart')
+      .on('error', rej)
+      .on('end', () => {
+
+        return unlinkPromise(videoInputPath)
+          .then(() => {
+            // Important to do this before getVideoFilename() to take in account the new file extension
+            video.set('extname', newExtname)
+
+            const newVideoPath = join(videosDirectory, video.getVideoFilename())
+            return renamePromise(videoOutputPath, newVideoPath)
           })
+          .then(() => {
+            const newVideoPath = join(videosDirectory, video.getVideoFilename())
+            return createTorrentFromVideo(video, newVideoPath)
+          })
+          .then(() => {
+            return video.save()
+          })
+          .then(() => {
+            return res()
+          })
+          .catch(err => {
+            // Autodesctruction...
+            video.destroy().asCallback(function (err) {
+              if (err) logger.error('Cannot destruct video after transcoding failure.', { error: err })
+            })
 
-          return finalCallback(err)
-        }
-
-        return finalCallback(null)
+            return rej(err)
+          })
       })
-    })
-    .run()
+      .run()
+  })
 }
 
 // ------------------------------ STATICS ------------------------------
 
-generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string, callback: VideoMethods.GenerateThumbnailFromDataCallback) {
+generateThumbnailFromData = function (video: VideoInstance, thumbnailData: string) {
   // Creating the thumbnail for a remote video
 
   const thumbnailName = video.getThumbnailName()
   const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
-  fs.writeFile(thumbnailPath, Buffer.from(thumbnailData, 'binary'), function (err) {
-    if (err) return callback(err)
-
-    return callback(null, thumbnailName)
+  return writeFilePromise(thumbnailPath, Buffer.from(thumbnailData, 'binary')).then(() => {
+    return thumbnailName
   })
 }
 
-getDurationFromFile = function (videoPath: string, callback: VideoMethods.GetDurationFromFileCallback) {
-  ffmpeg.ffprobe(videoPath, function (err, metadata) {
-    if (err) return callback(err)
+getDurationFromFile = function (videoPath: string) {
+  return new Promise<number>((res, rej) => {
+    ffmpeg.ffprobe(videoPath, function (err, metadata) {
+      if (err) return rej(err)
 
-    return callback(null, Math.floor(metadata.format.duration))
+      return res(Math.floor(metadata.format.duration))
+    })
   })
 }
 
-list = function (callback: VideoMethods.ListCallback) {
-  return Video.findAll().asCallback(callback)
+list = function () {
+  return Video.findAll()
 }
 
-listForApi = function (start: number, count: number, sort: string, callback: VideoMethods.ListForApiCallback) {
+listForApi = function (start: number, count: number, sort: string) {
   // Exclude Blakclisted videos from the list
   const query = {
     distinct: true,
@@ -652,14 +607,15 @@ listForApi = function (start: number, count: number, sort: string, callback: Vid
     where: createBaseVideosWhere()
   }
 
-  return Video.findAndCountAll(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    return callback(null, result.rows, result.count)
+  return Video.findAndCountAll(query).then(({ rows, count }) => {
+    return {
+      data: rows,
+      total: count
+    }
   })
 }
 
-loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback: VideoMethods.LoadByHostAndRemoteIdCallback) {
+loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
   const query = {
     where: {
       remoteId: remoteId
@@ -680,10 +636,10 @@ loadByHostAndRemoteId = function (fromHost: string, remoteId: string, callback:
     ]
   }
 
-  return Video.findOne(query).asCallback(callback)
+  return Video.findOne(query)
 }
 
-listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAndPopulateAuthorAndTagsCallback) {
+listOwnedAndPopulateAuthorAndTags = function () {
   // If remoteId is null this is *our* video
   const query = {
     where: {
@@ -692,10 +648,10 @@ listOwnedAndPopulateAuthorAndTags = function (callback: VideoMethods.ListOwnedAn
     include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
   }
 
-  return Video.findAll(query).asCallback(callback)
+  return Video.findAll(query)
 }
 
-listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedByAuthorCallback) {
+listOwnedByAuthor = function (author: string) {
   const query = {
     where: {
       remoteId: null
@@ -710,22 +666,22 @@ listOwnedByAuthor = function (author: string, callback: VideoMethods.ListOwnedBy
     ]
   }
 
-  return Video.findAll(query).asCallback(callback)
+  return Video.findAll(query)
 }
 
-load = function (id: string, callback: VideoMethods.LoadCallback) {
-  return Video.findById(id).asCallback(callback)
+load = function (id: string) {
+  return Video.findById(id)
 }
 
-loadAndPopulateAuthor = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorCallback) {
+loadAndPopulateAuthor = function (id: string) {
   const options = {
     include: [ Video['sequelize'].models.Author ]
   }
 
-  return Video.findById(id, options).asCallback(callback)
+  return Video.findById(id, options)
 }
 
-loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethods.LoadAndPopulateAuthorAndPodAndTagsCallback) {
+loadAndPopulateAuthorAndPodAndTags = function (id: string) {
   const options = {
     include: [
       {
@@ -736,17 +692,10 @@ loadAndPopulateAuthorAndPodAndTags = function (id: string, callback: VideoMethod
     ]
   }
 
-  return Video.findById(id, options).asCallback(callback)
+  return Video.findById(id, options)
 }
 
-searchAndPopulateAuthorAndPodAndTags = function (
-  value: string,
-  field: string,
-  start: number,
-  count: number,
-  sort: string,
-  callback: VideoMethods.SearchAndPopulateAuthorAndPodAndTagsCallback
-) {
+searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
   const podInclude: any = {
     model: Video['sequelize'].models.Pod,
     required: false
@@ -778,7 +727,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
   } else if (field === 'tags') {
     const escapedValue = Video['sequelize'].escape('%' + value + '%')
     query.where.id.$in = Video['sequelize'].literal(
-      '(SELECT "VideoTags"."videoId" FROM "Tags" INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" WHERE name LIKE ' + escapedValue + ')'
+      `(SELECT "VideoTags"."videoId"
+        FROM "Tags"
+        INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
+        WHERE name LIKE ${escapedValue}
+       )`
     )
   } else if (field === 'host') {
     // FIXME: Include our pod? (not stored in the database)
@@ -810,10 +763,11 @@ searchAndPopulateAuthorAndPodAndTags = function (
     // query.include.push([ Video['sequelize'].models.Tag ])
   }
 
-  return Video.findAndCountAll(query).asCallback(function (err, result) {
-    if (err) return callback(err)
-
-    return callback(null, result.rows, result.count)
+  return Video.findAndCountAll(query).then(({ rows, count }) => {
+    return {
+      data: rows,
+      total: count
+    }
   })
 }
 
@@ -829,27 +783,27 @@ function createBaseVideosWhere () {
   }
 }
 
-function removeThumbnail (video: VideoInstance, callback: (err: Error) => void) {
+function removeThumbnail (video: VideoInstance) {
   const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName())
-  fs.unlink(thumbnailPath, callback)
+  return unlinkPromise(thumbnailPath)
 }
 
-function removeFile (video: VideoInstance, callback: (err: Error) => void) {
+function removeFile (video: VideoInstance) {
   const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
-  fs.unlink(filePath, callback)
+  return unlinkPromise(filePath)
 }
 
-function removeTorrent (video: VideoInstance, callback: (err: Error) => void) {
+function removeTorrent (video: VideoInstance) {
   const torrenPath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
-  fs.unlink(torrenPath, callback)
+  return unlinkPromise(torrenPath)
 }
 
-function removePreview (video: VideoInstance, callback: (err: Error) => void) {
+function removePreview (video: VideoInstance) {
   // Same name than video thumnail
-  fs.unlink(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName(), callback)
+  return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + video.getPreviewName())
 }
 
-function createTorrentFromVideo (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
+function createTorrentFromVideo (video: VideoInstance, videoPath: string) {
   const options = {
     announceList: [
       [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
@@ -859,30 +813,27 @@ function createTorrentFromVideo (video: VideoInstance, videoPath: string, callba
     ]
   }
 
-  createTorrent(videoPath, options, function (err, torrent) {
-    if (err) return callback(err)
-
-    const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
-    fs.writeFile(filePath, torrent, function (err) {
-      if (err) return callback(err)
-
+  return createTorrentPromise(videoPath, options)
+    .then(torrent => {
+      const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, video.getTorrentName())
+      return writeFilePromise(filePath, torrent).then(() => torrent)
+    })
+    .then(torrent => {
       const parsedTorrent = parseTorrent(torrent)
       video.set('infoHash', parsedTorrent.infoHash)
-      video.validate().asCallback(callback)
+      return video.validate()
     })
-  })
 }
 
-function createPreview (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
-  generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null, callback)
+function createPreview (video: VideoInstance, videoPath: string) {
+  return generateImage(video, videoPath, CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName(), null)
 }
 
-function createThumbnail (video: VideoInstance, videoPath: string, callback: (err: Error) => void) {
-  generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE, callback)
+function createThumbnail (video: VideoInstance, videoPath: string) {
+  return generateImage(video, videoPath, CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName(), THUMBNAILS_SIZE)
 }
 
-type GenerateImageCallback = (err: Error, imageName: string) => void
-function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string, callback?: GenerateImageCallback) {
+function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string) {
   const options: any = {
     filename: imageName,
     count: 1,
@@ -893,29 +844,25 @@ function generateImage (video: VideoInstance, videoPath: string, folder: string,
     options.size = size
   }
 
-  ffmpeg(videoPath)
-    .on('error', callback)
-    .on('end', function () {
-      callback(null, imageName)
-    })
-    .thumbnail(options)
+  return new Promise<string>((res, rej) => {
+    ffmpeg(videoPath)
+      .on('error', rej)
+      .on('end', function () {
+        return res(imageName)
+      })
+      .thumbnail(options)
+  })
 }
 
-function removeFromBlacklist (video: VideoInstance, callback: (err: Error) => void) {
+function removeFromBlacklist (video: VideoInstance) {
   // Find the blacklisted video
-  db.BlacklistedVideo.loadByVideoId(video.id, function (err, video) {
-    // If an error occured, stop here
-    if (err) {
-      logger.error('Error when fetching video from blacklist.', { error: err })
-      return callback(err)
+  return db.BlacklistedVideo.loadByVideoId(video.id).then(video => {
+    // Not found the video, skip
+    if (!video) {
+      return null
     }
 
     // If we found the video, remove it from the blacklist
-    if (video) {
-      video.destroy().asCallback(callback)
-    } else {
-      // If haven't found it, simply ignore it and do nothing
-      return callback(null)
-    }
+    return video.destroy()
   })
 }
index 237cb533d53c60542ae808059ee85f04456ea27d..917583a429f187aad9bd2c04706b277aad6207d5 100644 (file)
@@ -171,6 +171,23 @@ describe('Test advanced friends', function () {
       function (next) {
         setTimeout(next, 22000)
       },
+      // Check the pods 1, 2 expulsed pod 4
+      function (next) {
+        each([ 1, 2 ], function (i, callback) {
+          getFriendsList(i, function (err, res) {
+            if (err) throw err
+
+            // Pod 4 should not be our friend
+            const result = res.body.data
+            expect(result.length).to.equal(2)
+            for (const pod of result) {
+              expect(pod.host).not.equal(servers[3].host)
+            }
+
+            callback()
+          })
+        }, next)
+      },
       // Rerun server 4
       function (next) {
         serversUtils.runServer(4, function (server) {
@@ -187,7 +204,7 @@ describe('Test advanced friends', function () {
           next()
         })
       },
-      // Pod 6 ask pod 1, 2 and 3
+      // Pod 6 asks pod 1, 2 and 3
       function (next) {
         makeFriends(6, next)
       },
index 95d95ab4790f00f28cd3b9e9a0868ce2ba3a1693..cd885af89fb316ff041f89377d6e0d022430053b 100644 (file)
@@ -1,6 +1,7 @@
 export * from './job.model'
 export * from './oauth-client-local.model'
 export * from './pod.model'
+export * from './result-list.model'
 export * from './request-scheduler.model'
 export * from './user-video-rate.model'
 export * from './user.model'
diff --git a/shared/models/result-list.model.ts b/shared/models/result-list.model.ts
new file mode 100644 (file)
index 0000000..2d5147a
--- /dev/null
@@ -0,0 +1,4 @@
+export interface ResultList<T> {
+  total: number
+  data: T[]
+}
index f62012e91472e96f336d4f2344e9eb08b90c22d0..5636db49479645b6ff4b1db7afc05d9c5eab192c 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -84,9 +84,9 @@
   dependencies:
     "@types/express" "*"
 
-"@types/node@*", "@types/node@^7.0.18":
-  version "7.0.32"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.32.tgz#6afe6c66520a4c316623a14aef123908d01b4bba"
+"@types/node@*", "@types/node@^8.0.3":
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.3.tgz#fca61c26f83e5f453166114f57d53a47feb36d45"
 
 "@types/request@^0.0.44":
   version "0.0.44"
@@ -453,7 +453,7 @@ bluebird@^2.10.0, bluebird@^2.9.13:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
 
-bluebird@^3.0.5, bluebird@^3.4.0, bluebird@^3.4.6:
+bluebird@^3.0.5, bluebird@^3.4.0, bluebird@^3.4.6, bluebird@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
 
@@ -3909,9 +3909,9 @@ typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 
-typescript@^2.3.4:
-  version "2.4.0"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd"
+typescript@^2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc"
 
 uid-number@^0.0.6, uid-number@~0.0.6:
   version "0.0.6"