Video channel API routes refractor
authorChocobozzz <me@florianbigard.com>
Wed, 25 Apr 2018 08:21:38 +0000 (10:21 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 25 Apr 2018 11:50:48 +0000 (13:50 +0200)
18 files changed:
client/src/app/+account/account-about/account-about.component.ts
client/src/app/+account/account.component.ts
client/src/app/shared/account/account.model.ts
server/controllers/api/accounts.ts
server/controllers/api/users.ts
server/lib/user.ts
server/middlewares/validators/video-channels.ts
server/models/video/video-channel.ts
server/models/video/video.ts
server/tests/api/check-params/video-channels.ts
server/tests/api/check-params/videos.ts
server/tests/api/users/users-multiple-servers.ts
server/tests/api/videos/video-channels.ts
server/tests/api/videos/video-nsfw.ts
server/tests/utils/videos/video-channels.ts
server/tests/utils/videos/videos.ts
shared/models/videos/video-channel.model.ts
support/doc/api/openapi.yaml

index 0772b844ed5eaaed1777abe92dacea2c6c3a2087..8746875cb6eae1c401a4b02feec084d5e932bfe9 100644 (file)
@@ -18,7 +18,7 @@ import { AccountService } from '@app/shared/account/account.service'
   styleUrls: [ './account-about.component.scss' ]
 })
 export class AccountAboutComponent implements OnInit {
-  private account: Account
+  account: Account
 
   constructor (
     protected route: ActivatedRoute,
index 1c3e528a70371c81972831bd60d26f199b6539f4..ae5354ed9be710947f083d583a27deb090ffce62 100644 (file)
@@ -9,7 +9,7 @@ import { Account } from '@app/shared/account/account.model'
   styleUrls: [ './account.component.scss' ]
 })
 export class AccountComponent implements OnInit {
-  private account: Account
+  account: Account
 
   constructor (
     private route: ActivatedRoute,
index 3d5176bdd691288957072c8b4aaf25540db19101..10a70ac31abf46d0e3400c79b696f9ccb487efda 100644 (file)
@@ -16,21 +16,6 @@ export class Account implements ServerAccount {
   updatedAt: Date
   avatar: Avatar
 
-  constructor (hash: ServerAccount) {
-    this.id = hash.id
-    this.uuid = hash.uuid
-    this.url = hash.url
-    this.name = hash.name
-    this.displayName = hash.displayName
-    this.description = hash.description
-    this.host = hash.host
-    this.followingCount = hash.followingCount
-    this.followersCount = hash.followersCount
-    this.createdAt = new Date(hash.createdAt.toString())
-    this.updatedAt = new Date(hash.updatedAt.toString())
-    this.avatar = hash.avatar
-  }
-
   static GET_ACCOUNT_AVATAR_URL (account: Account) {
     const absoluteAPIUrl = getAbsoluteAPIUrl()
 
@@ -47,4 +32,19 @@ export class Account implements ServerAccount {
 
     return accountName + '@' + host
   }
+
+  constructor (hash: ServerAccount) {
+    this.id = hash.id
+    this.uuid = hash.uuid
+    this.url = hash.url
+    this.name = hash.name
+    this.displayName = hash.displayName
+    this.description = hash.description
+    this.host = hash.host
+    this.followingCount = hash.followingCount
+    this.followersCount = hash.followersCount
+    this.createdAt = new Date(hash.createdAt.toString())
+    this.updatedAt = new Date(hash.updatedAt.toString())
+    this.avatar = hash.avatar
+  }
 }
index 04c5897c5310e83b70339ac13720704cbac1c67f..85987c912faca8eabd05139412b548233b5407f0 100644 (file)
@@ -126,7 +126,8 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
   const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
   return res.json({
     videoChannel: {
-      id: videoChannel.id
+      id: videoChannel.id,
+      uuid: videoChannel.Actor.uuid
     }
   }).end()
 }
@@ -233,7 +234,6 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
   return res.json(getFormattedObjects(resultList.data, resultList.total))
 }
 
-
 async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
   const account: AccountModel = res.locals.account
 
index 474329b5853d00623e49808868a164a3448a888f..dcc4ef196b7444cef4f322deb7c8a8ef286e79f4 100644 (file)
@@ -185,7 +185,10 @@ async function createUserRetryWrapper (req: express.Request, res: express.Respon
   return res.json({
     user: {
       id: user.id,
-      uuid: account.uuid
+      account: {
+        id: account.id,
+        uuid: account.Actor.uuid
+      }
     }
   }).end()
 }
index 2ea7481e8152b09ce9fc11ff75bd178951ed9e89..d019c4e719b44f7c51fc27f91711fe8e284d60b6 100644 (file)
@@ -5,6 +5,7 @@ import { AccountModel } from '../models/account/account'
 import { UserModel } from '../models/account/user'
 import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
 import { createVideoChannel } from './video-channel'
+import { VideoChannelModel } from '../models/video/video-channel'
 
 async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) {
   const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@@ -28,7 +29,7 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
   account.Actor = await setAsyncActorKeys(account.Actor)
   videoChannel.Actor = await setAsyncActorKeys(videoChannel.Actor)
 
-  return { user, account, videoChannel }
+  return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel }
 }
 
 async function createLocalAccountWithoutKeys (
index e3a11a41b58aff00f221f571b93e8a8340367a87..9e6f459cfa196f6e8515129c9cc48e29aa552774 100644 (file)
@@ -11,6 +11,7 @@ import { logger } from '../../helpers/logger'
 import { UserModel } from '../../models/account/user'
 import { VideoChannelModel } from '../../models/video/video-channel'
 import { areValidationErrors } from './utils'
+import { AccountModel } from '../../models/account/account'
 
 const listVideoAccountChannelsValidator = [
   param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
@@ -53,6 +54,7 @@ const videoChannelsUpdateValidator = [
     if (areValidationErrors(req, res)) return
     if (!await isAccountIdExist(req.params.accountId, res)) return
     if (!await isVideoChannelExist(req.params.id, res)) return
+    if (!checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
 
     // We need to make additional checks
     if (res.locals.videoChannel.Actor.isOwned() === false) {
@@ -82,6 +84,7 @@ const videoChannelsRemoveValidator = [
     if (!await isAccountIdExist(req.params.accountId, res)) return
     if (!await isVideoChannelExist(req.params.id, res)) return
 
+    if (!checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
     // Check if the user who did the request is able to delete the video
     if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
     if (!await checkVideoChannelIsNotTheLastOne(res)) return
@@ -98,10 +101,13 @@ const videoChannelsGetValidator = [
     logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
 
     if (areValidationErrors(req, res)) return
+
     // On some routes, accountId is optional (for example in the ActivityPub route)
     if (req.params.accountId && !await isAccountIdExist(req.params.accountId, res)) return
     if (!await isVideoChannelExist(req.params.id, res)) return
 
+    if (res.locals.account && !checkAccountOwnsVideoChannel(res.locals.account, res.locals.videoChannel, res)) return
+
     return next()
   }
 ]
@@ -154,3 +160,15 @@ async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
 
   return true
 }
+
+function checkAccountOwnsVideoChannel (account: AccountModel, videoChannel: VideoChannelModel, res: express.Response) {
+  if (videoChannel.Account.id !== account.id) {
+    res.status(400)
+              .json({ error: 'This account does not own this video channel' })
+              .end()
+
+    return false
+  }
+
+  return true
+}
index 40f3be7fe562ff8826ab5411fb7ec233c8938700..4a50af265fae906a65f0e060688ef7f1b70ffb7c 100644 (file)
@@ -108,7 +108,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
     foreignKey: {
       allowNull: false
     },
-    onDelete: 'CASCADE'
+    hooks: true
   })
   Account: AccountModel
 
@@ -234,17 +234,26 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
 
   toFormattedJSON (): VideoChannel {
     const actor = this.Actor.toFormattedJSON()
-    const account = {
+    const videoChannel = {
       id: this.id,
       displayName: this.name,
       description: this.description,
       support: this.support,
       isLocal: this.Actor.isOwned(),
       createdAt: this.createdAt,
-      updatedAt: this.updatedAt
+      updatedAt: this.updatedAt,
+      ownerAccount: undefined,
+      videos: undefined
+    }
+
+    if (this.Account) {
+      videoChannel.ownerAccount = {
+        id: this.Account.id,
+        uuid: this.Account.Actor.uuid
+      }
     }
 
-    return Object.assign(actor, account)
+    return Object.assign(actor, videoChannel)
   }
 
   toActivityPubObject (): ActivityPubActor {
index 7ababbf232e4c0e3c5c8a1e77e51a5c1d5509d45..f23fac2ab1b3da2d2027b6c98b0479bc4d979638 100644 (file)
@@ -430,7 +430,7 @@ export class VideoModel extends Model<VideoModel> {
     foreignKey: {
       allowNull: true
     },
-    onDelete: 'cascade'
+    hooks: true
   })
   VideoChannel: VideoChannelModel
 
@@ -510,10 +510,12 @@ export class VideoModel extends Model<VideoModel> {
     return undefined
   }
 
-  @AfterDestroy
+  @BeforeDestroy
   static async removeFilesAndSendDelete (instance: VideoModel) {
     const tasks: Promise<any>[] = []
 
+    logger.debug('Removing files of video %s.', instance.url)
+
     tasks.push(instance.removeThumbnail())
 
     if (instance.isOwned()) {
@@ -530,10 +532,13 @@ export class VideoModel extends Model<VideoModel> {
       })
     }
 
-    return Promise.all(tasks)
+    // Do not wait video deletion because we could be in a transaction
+    Promise.all(tasks)
       .catch(err => {
         logger.error('Some errors when removing files of video %s in after destroy hook.', instance.uuid, { err })
       })
+
+    return undefined
   }
 
   static list () {
index acb6bdd57ead1bd48dfbeae9f0c3abb20ab2c7db..25b2dc9b950c29db840ab4cb16e97f947c62e222 100644 (file)
@@ -7,7 +7,7 @@ import {
   createUser,
   deleteVideoChannel,
   flushTests,
-  getAccountVideoChannelsList,
+  getAccountVideoChannelsList, getMyUserInformation,
   getVideoChannelsList,
   immutableAssign,
   killallServers,
@@ -21,6 +21,7 @@ import {
 } from '../../utils'
 import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
 import { getAccountsList } from '../../utils/users/accounts'
+import { User } from '../../../../shared/models/users'
 
 const expect = chai.expect
 
@@ -29,6 +30,8 @@ describe('Test videos API validator', function () {
   const accountPath = '/api/v1/accounts/'
   let server: ServerInfo
   let accessTokenUser: string
+  let accountUUID: string
+  let videoChannelUUID: string
 
   // ---------------------------------------------------------------
 
@@ -45,8 +48,18 @@ describe('Test videos API validator', function () {
       username: 'fake',
       password: 'fake_password'
     }
-    await createUser(server.url, server.accessToken, user.username, user.password)
-    accessTokenUser = await userLogin(server, user)
+
+    {
+      await createUser(server.url, server.accessToken, user.username, user.password)
+      accessTokenUser = await userLogin(server, user)
+    }
+
+    {
+      const res = await getMyUserInformation(server.url, server.accessToken)
+      const user: User = res.body
+      accountUUID = user.account.uuid
+      videoChannelUUID = user.videoChannels[0].uuid
+    }
   })
 
   describe('When listing a video channels', function () {
@@ -74,18 +87,15 @@ describe('Test videos API validator', function () {
   })
 
   describe('When adding a video channel', function () {
-    let path: string
-
     const baseCorrectParams = {
       name: 'hello',
       description: 'super description',
       support: 'super support text'
     }
+    let path: string
 
     before(async function () {
-      const res = await getAccountsList(server.url)
-      const accountId = res.body.data[0].id
-      path = accountPath + accountId + '/video-channels'
+      path = accountPath + accountUUID + '/video-channels'
     })
 
     it('Should fail with a non authenticated user', async function () {
@@ -129,21 +139,14 @@ describe('Test videos API validator', function () {
   })
 
   describe('When updating a video channel', function () {
-    let path: string
-
     const baseCorrectParams = {
       name: 'hello',
       description: 'super description'
     }
+    let path: string
 
     before(async function () {
-      const res1 = await getVideoChannelsList(server.url, 0, 1)
-      const videoChannelId = res1.body.data[0].id
-
-      const res2 = await getAccountsList(server.url)
-      const accountId = res2.body.data[0].id
-
-      path = accountPath + accountId + '/video-channels/' + videoChannelId
+      path = accountPath + accountUUID + '/video-channels/' + videoChannelUUID
     })
 
     it('Should fail with a non authenticated user', async function () {
@@ -194,16 +197,9 @@ describe('Test videos API validator', function () {
 
   describe('When getting a video channel', function () {
     let basePath: string
-    let videoChannelId: number
 
     before(async function () {
-      const res1 = await getVideoChannelsList(server.url, 0, 1)
-      videoChannelId = res1.body.data[0].id
-
-      const res2 = await getAccountsList(server.url)
-      const accountId = res2.body.data[0].id
-
-      basePath = accountPath + accountId + '/video-channels'
+      basePath = accountPath + accountUUID + '/video-channels'
     })
 
     it('Should return the list of the video channels with nothing', async function () {
@@ -235,49 +231,38 @@ describe('Test videos API validator', function () {
     it('Should succeed with the correct parameters', async function () {
       await makeGetRequest({
         url: server.url,
-        path: basePath + '/' + videoChannelId,
+        path: basePath + '/' + videoChannelUUID,
         statusCodeExpected: 200
       })
     })
   })
 
   describe('When deleting a video channel', function () {
-    let videoChannelId: number
-    let accountId: number
-
-    before(async function () {
-      const res1 = await getVideoChannelsList(server.url, 0, 1)
-      videoChannelId = res1.body.data[0].id
-
-      const res2 = await getAccountsList(server.url)
-      accountId = res2.body.data[0].id
-    })
-
     it('Should fail with a non authenticated user', async function () {
-      await deleteVideoChannel(server.url, 'coucou', accountId, videoChannelId, 401)
+      await deleteVideoChannel(server.url, 'coucou', accountUUID, videoChannelUUID, 401)
     })
 
     it('Should fail with another authenticated user', async function () {
-      await deleteVideoChannel(server.url, accessTokenUser, accountId, videoChannelId, 403)
+      await deleteVideoChannel(server.url, accessTokenUser, accountUUID, videoChannelUUID, 403)
     })
 
     it('Should fail with an unknown account id', async function () {
-      await deleteVideoChannel(server.url, server.accessToken, 454554,videoChannelId, 404)
+      await deleteVideoChannel(server.url, server.accessToken, 454554,videoChannelUUID, 404)
     })
 
     it('Should fail with an unknown video channel id', async function () {
-      await deleteVideoChannel(server.url, server.accessToken, accountId,454554, 404)
+      await deleteVideoChannel(server.url, server.accessToken, accountUUID,454554, 404)
     })
 
     it('Should succeed with the correct parameters', async function () {
-      await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId)
+      await deleteVideoChannel(server.url, server.accessToken, accountUUID, videoChannelUUID)
     })
 
     it('Should fail to delete the last user video channel', async function () {
       const res = await getVideoChannelsList(server.url, 0, 1)
-      videoChannelId = res.body.data[0].id
+      const lastVideoChannelUUID = res.body.data[0].uuid
 
-      await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId, 409)
+      await deleteVideoChannel(server.url, server.accessToken, accountUUID, lastVideoChannelUUID, 409)
     })
   })
 
index da41f515b7f6401f9e01c5cc58654185c4d2da48..850ad12e0188c25791892b24c17c9c7117a9f7d0 100644 (file)
@@ -10,6 +10,7 @@ import {
   makeGetRequest, makeUploadRequest, makePutBodyRequest, removeVideo, runServer, ServerInfo, setAccessTokensToServers, userLogin
 } from '../../utils'
 import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
+import { getAccountsList } from '../../utils/users/accounts'
 
 const expect = chai.expect
 
@@ -17,7 +18,9 @@ describe('Test videos API validator', function () {
   const path = '/api/v1/videos/'
   let server: ServerInfo
   let userAccessToken = ''
+  let accountUUID: string
   let channelId: number
+  let channelUUID: string
   let videoId
 
   // ---------------------------------------------------------------
@@ -36,8 +39,12 @@ describe('Test videos API validator', function () {
     await createUser(server.url, server.accessToken, username, password)
     userAccessToken = await userLogin(server, { username, password })
 
-    const res = await getMyUserInformation(server.url, server.accessToken)
-    channelId = res.body.videoChannels[0].id
+    {
+      const res = await getMyUserInformation(server.url, server.accessToken)
+      channelId = res.body.videoChannels[ 0 ].id
+      channelUUID = res.body.videoChannels[ 0 ].uuid
+      accountUUID = res.body.account.uuid
+    }
   })
 
   describe('When listing a video', function () {
@@ -52,6 +59,10 @@ describe('Test videos API validator', function () {
     it('Should fail with an incorrect sort', async function () {
       await checkBadSortPagination(server.url, path)
     })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, path, statusCodeExpected: 200 })
+    })
   })
 
   describe('When searching a video', function () {
@@ -75,6 +86,10 @@ describe('Test videos API validator', function () {
     it('Should fail with an incorrect sort', async function () {
       await checkBadSortPagination(server.url, join(path, 'search', 'test'))
     })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, path, statusCodeExpected: 200 })
+    })
   })
 
   describe('When listing my videos', function () {
@@ -91,6 +106,58 @@ describe('Test videos API validator', function () {
     it('Should fail with an incorrect sort', async function () {
       await checkBadSortPagination(server.url, path, server.accessToken)
     })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, token: server.accessToken, path, statusCodeExpected: 200 })
+    })
+  })
+
+  describe('When listing account videos', function () {
+    let path: string
+
+    before(async function () {
+      path = '/api/v1/accounts/' + accountUUID + '/videos'
+    })
+
+    it('Should fail with a bad start pagination', async function () {
+      await checkBadStartPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with a bad count pagination', async function () {
+      await checkBadCountPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with an incorrect sort', async function () {
+      await checkBadSortPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, path, statusCodeExpected: 200 })
+    })
+  })
+
+  describe('When listing video channel videos', function () {
+    let path: string
+
+    before(async function () {
+      path = '/api/v1/accounts/' + accountUUID + '/video-channels/' + channelUUID + '/videos'
+    })
+
+    it('Should fail with a bad start pagination', async function () {
+      await checkBadStartPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with a bad count pagination', async function () {
+      await checkBadCountPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with an incorrect sort', async function () {
+      await checkBadSortPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, path, statusCodeExpected: 200 })
+    })
   })
 
   describe('When adding a video', function () {
@@ -112,7 +179,7 @@ describe('Test videos API validator', function () {
         support: 'my super support text',
         tags: [ 'tag1', 'tag2' ],
         privacy: VideoPrivacy.PUBLIC,
-        channelId
+        channelId: channelId
       }
     })
 
index bb458f7a501322ac7f05a924f7d39eba16a5b9a2..a7f3aa8d35624e6cc062a640e8434a4f97985020 100644 (file)
@@ -4,22 +4,33 @@ import * as chai from 'chai'
 import 'mocha'
 import { Account } from '../../../../shared/models/actors'
 import {
-  checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, updateMyUser, userLogin,
+  checkVideoFilesWereRemoved,
+  createUser,
+  doubleFollow,
+  flushAndRunMultipleServers,
+  getAccountVideos,
+  getVideoChannelsList,
+  removeUser,
+  updateMyUser,
+  userLogin,
   wait
 } from '../../utils'
 import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index'
 import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts'
 import { setAccessTokensToServers } from '../../utils/users/login'
+import { User } from '../../../../shared/models/users'
+import { VideoChannel } from '../../../../shared/models/videos'
 
 const expect = chai.expect
 
 describe('Test users with multiple servers', function () {
   let servers: ServerInfo[] = []
-  let user
-  let userUUID
-  let userId
-  let videoUUID
-  let userAccessToken
+  let user: User
+  let userAccountUUID: string
+  let userVideoChannelUUID: string
+  let userId: number
+  let videoUUID: string
+  let userAccessToken: string
 
   before(async function () {
     this.timeout(120000)
@@ -39,17 +50,28 @@ describe('Test users with multiple servers', function () {
     // The root user of server 1 is propagated to servers 2 and 3
     await uploadVideo(servers[0].url, servers[0].accessToken, {})
 
-    const user = {
-      username: 'user1',
-      password: 'password'
+    {
+      const user = {
+        username: 'user1',
+        password: 'password'
+      }
+      const res = await createUser(servers[ 0 ].url, servers[ 0 ].accessToken, user.username, user.password)
+      userAccountUUID = res.body.user.account.uuid
+      userId = res.body.user.id
+
+      userAccessToken = await userLogin(servers[ 0 ], user)
+    }
+
+    {
+      const res = await getMyUserInformation(servers[ 0 ].url, servers[ 0 ].accessToken)
+      const user: User = res.body
+      userVideoChannelUUID = user.videoChannels[0].uuid
     }
-    const resUser = await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
-    userUUID = resUser.body.user.uuid
-    userId = resUser.body.user.id
-    userAccessToken = await userLogin(servers[0], user)
 
-    const resVideo = await uploadVideo(servers[0].url, userAccessToken, {})
-    videoUUID = resVideo.body.uuid
+    {
+      const resVideo = await uploadVideo(servers[ 0 ].url, userAccessToken, {})
+      videoUUID = resVideo.body.video.uuid
+    }
 
     await wait(5000)
   })
@@ -106,14 +128,31 @@ describe('Test users with multiple servers', function () {
     }
   })
 
+  it('Should list account videos', async function () {
+    for (const server of servers) {
+      const res = await getAccountVideos(server.url, server.accessToken, userAccountUUID, 0, 5)
+
+      expect(res.body.total).to.equal(1)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(1)
+      expect(res.body.data[0].uuid).to.equal(videoUUID)
+    }
+  })
+
   it('Should remove the user', async function () {
     this.timeout(10000)
 
     for (const server of servers) {
       const resAccounts = await getAccountsList(server.url, '-createdAt')
 
-      const userServer1List = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
-      expect(userServer1List).not.to.be.undefined
+      const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
+      expect(accountDeleted).not.to.be.undefined
+
+      const resVideoChannels = await getVideoChannelsList(server.url, 0, 10)
+      const videoChannelDeleted = resVideoChannels.body.data.find(a => {
+        return a.displayName === 'Default user1 channel' && a.host === 'localhost:9001'
+      }) as VideoChannel
+      expect(videoChannelDeleted).not.to.be.undefined
     }
 
     await removeUser(servers[0].url, userId, servers[0].accessToken)
@@ -123,14 +162,21 @@ describe('Test users with multiple servers', function () {
     for (const server of servers) {
       const resAccounts = await getAccountsList(server.url, '-createdAt')
 
-      const userServer1List = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
-      expect(userServer1List).to.be.undefined
+      const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
+      expect(accountDeleted).to.be.undefined
+
+      const resVideoChannels = await getVideoChannelsList(server.url, 0, 10)
+      const videoChannelDeleted = resVideoChannels.body.data.find(a => {
+        return a.name === 'Default user1 channel' && a.host === 'localhost:9001'
+      }) as VideoChannel
+      expect(videoChannelDeleted).to.be.undefined
     }
   })
 
   it('Should not have actor files', async () => {
     for (const server of servers) {
-      await checkActorFilesWereRemoved(userUUID, server.serverNumber)
+      await checkActorFilesWereRemoved(userAccountUUID, server.serverNumber)
+      await checkActorFilesWereRemoved(userVideoChannelUUID, server.serverNumber)
     }
   })
 
index a7552a83a7f7cbee253d280e4f917c7e4ec68d81..04e7b8c6ac28f225f19a40e264501cf2b0d49746 100644 (file)
@@ -3,7 +3,7 @@
 import * as chai from 'chai'
 import 'mocha'
 import { User } from '../../../../shared/index'
-import { doubleFollow, flushAndRunMultipleServers, uploadVideo, wait } from '../../utils'
+import { doubleFollow, flushAndRunMultipleServers, getVideoChannelVideos, uploadVideo, wait } from '../../utils'
 import {
   addVideoChannel,
   deleteVideoChannel,
@@ -24,8 +24,9 @@ const expect = chai.expect
 describe('Test video channels', function () {
   let servers: ServerInfo[]
   let userInfo: User
-  let accountId: number
+  let accountUUID: string
   let videoChannelId: number
+  let videoChannelUUID: string
 
   before(async function () {
     this.timeout(30000)
@@ -38,8 +39,9 @@ describe('Test video channels', function () {
     await doubleFollow(servers[0], servers[1])
 
     {
-      const res = await getAccountsList(servers[0].url)
-      accountId = res.body.data[0].id
+      const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
+      const user: User = res.body
+      accountUUID = user.account.uuid
     }
 
     await wait(5000)
@@ -61,11 +63,12 @@ describe('Test video channels', function () {
       description: 'super video channel description',
       support: 'super video channel support text'
     }
-    const res = await addVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannel)
+    const res = await addVideoChannel(servers[0].url, servers[0].accessToken, accountUUID, videoChannel)
     videoChannelId = res.body.videoChannel.id
+    videoChannelUUID = res.body.videoChannel.uuid
 
     // The channel is 1 is propagated to servers 2
-    await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: videoChannelId })
+    await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'my video name', channelId: videoChannelId })
 
     await wait(3000)
   })
@@ -127,7 +130,7 @@ describe('Test video channels', function () {
       support: 'video channel support text updated'
     }
 
-    await updateVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId, videoChannelAttributes)
+    await updateVideoChannel(servers[0].url, servers[0].accessToken, accountUUID, videoChannelId, videoChannelAttributes)
 
     await wait(3000)
   })
@@ -146,7 +149,7 @@ describe('Test video channels', function () {
   })
 
   it('Should get video channel', async function () {
-    const res = await getVideoChannel(servers[0].url, accountId, videoChannelId)
+    const res = await getVideoChannel(servers[0].url, accountUUID, videoChannelId)
 
     const videoChannel = res.body
     expect(videoChannel.displayName).to.equal('video channel updated')
@@ -154,8 +157,20 @@ describe('Test video channels', function () {
     expect(videoChannel.support).to.equal('video channel support text updated')
   })
 
+  it('Should list the video channel videos', async function () {
+    this.timeout(10000)
+
+    for (const server of servers) {
+      const res = await getVideoChannelVideos(server.url, server.accessToken, accountUUID, videoChannelUUID, 0, 5)
+      expect(res.body.total).to.equal(1)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(1)
+      expect(res.body.data[0].name).to.equal('my video name')
+    }
+  })
+
   it('Should delete video channel', async function () {
-    await deleteVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId)
+    await deleteVideoChannel(servers[0].url, servers[0].accessToken, accountUUID, videoChannelId)
   })
 
   it('Should have video channel deleted', async function () {
index 4e5ab11ce94b5e0b9313ee1af9823b37d65efe21..8901f38f97a9ad1631f40a22c85965c67107d893 100644 (file)
@@ -7,8 +7,9 @@ import { userLogin } from '../../utils/users/login'
 import { createUser } from '../../utils/users/users'
 import { getMyVideos } from '../../utils/videos/videos'
 import {
+  getAccountVideos,
   getConfig, getCustomConfig,
-  getMyUserInformation,
+  getMyUserInformation, getVideoChannelVideos,
   getVideosListWithToken,
   runServer,
   searchVideo,
@@ -17,6 +18,7 @@ import {
 } from '../../utils'
 import { ServerConfig } from '../../../../shared/models'
 import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
+import { User } from '../../../../shared/models/users'
 
 const expect = chai.expect
 
@@ -25,6 +27,31 @@ describe('Test video NSFW policy', function () {
   let userAccessToken: string
   let customConfig: CustomConfig
 
+  function getVideosFunctions (token?: string) {
+    return getMyUserInformation(server.url, server.accessToken)
+      .then(res => {
+        const user: User = res.body
+        const videoChannelUUID = user.videoChannels[0].uuid
+        const accountUUID = user.account.uuid
+
+        if (token) {
+          return Promise.all([
+            getVideosListWithToken(server.url, token),
+            searchVideoWithToken(server.url, 'n', token),
+            getAccountVideos(server.url, token, accountUUID, 0, 5),
+            getVideoChannelVideos(server.url, token, accountUUID, videoChannelUUID, 0, 5)
+          ])
+        }
+
+        return Promise.all([
+          getVideosList(server.url),
+          searchVideo(server.url, 'n'),
+          getAccountVideos(server.url, undefined, accountUUID, 0, 5),
+          getVideoChannelVideos(server.url, undefined, accountUUID, videoChannelUUID, 0, 5)
+        ])
+      })
+  }
+
   before(async function () {
     this.timeout(50000)
 
@@ -56,7 +83,7 @@ describe('Test video NSFW policy', function () {
       const serverConfig: ServerConfig = resConfig.body
       expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display')
 
-      for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
+      for (const res of await getVideosFunctions()) {
         expect(res.body.total).to.equal(2)
 
         const videos = res.body.data
@@ -74,7 +101,7 @@ describe('Test video NSFW policy', function () {
       const serverConfig: ServerConfig = resConfig.body
       expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list')
 
-      for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
+      for (const res of await getVideosFunctions()) {
         expect(res.body.total).to.equal(1)
 
         const videos = res.body.data
@@ -91,7 +118,7 @@ describe('Test video NSFW policy', function () {
       const serverConfig: ServerConfig = resConfig.body
       expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur')
 
-      for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
+      for (const res of await getVideosFunctions()) {
         expect(res.body.total).to.equal(2)
 
         const videos = res.body.data
@@ -118,12 +145,7 @@ describe('Test video NSFW policy', function () {
     })
 
     it('Should display NSFW videos with blur user NSFW policy', async function () {
-      const results = [
-        await getVideosListWithToken(server.url, userAccessToken),
-        await searchVideoWithToken(server.url, 'n', userAccessToken)
-      ]
-
-      for (const res of results) {
+      for (const res of await getVideosFunctions(userAccessToken)) {
         expect(res.body.total).to.equal(2)
 
         const videos = res.body.data
@@ -140,12 +162,7 @@ describe('Test video NSFW policy', function () {
         nsfwPolicy: 'display'
       })
 
-      const results = [
-        await getVideosListWithToken(server.url, server.accessToken),
-        await searchVideoWithToken(server.url, 'n', server.accessToken)
-      ]
-
-      for (const res of results) {
+      for (const res of await getVideosFunctions(server.accessToken)) {
         expect(res.body.total).to.equal(2)
 
         const videos = res.body.data
@@ -162,11 +179,7 @@ describe('Test video NSFW policy', function () {
         nsfwPolicy: 'do_not_list'
       })
 
-      const results = [
-        await getVideosListWithToken(server.url, server.accessToken),
-        await searchVideoWithToken(server.url, 'n', server.accessToken)
-      ]
-      for (const res of results) {
+      for (const res of await getVideosFunctions(server.accessToken)) {
         expect(res.body.total).to.equal(1)
 
         const videos = res.body.data
index cfc541431d52f788884794de5c977f0d3ec2b4b4..0b4fa89b7eac6a2bf87cf70ed59c0628ad711d06 100644 (file)
@@ -34,7 +34,7 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
 function addVideoChannel (
   url: string,
   token: string,
-  accountId: number,
+  accountId: number | string,
   videoChannelAttributesArg: VideoChannelAttributes,
   expectedStatus = 200
 ) {
@@ -59,8 +59,8 @@ function addVideoChannel (
 function updateVideoChannel (
   url: string,
   token: string,
-  accountId: number,
-  channelId: number,
+  accountId: number | string,
+  channelId: number | string,
   attributes: VideoChannelAttributes,
   expectedStatus = 204
 ) {
@@ -79,7 +79,7 @@ function updateVideoChannel (
     .expect(expectedStatus)
 }
 
-function deleteVideoChannel (url: string, token: string, accountId: number, channelId: number, expectedStatus = 204) {
+function deleteVideoChannel (url: string, token: string, accountId: number | string, channelId: number | string, expectedStatus = 204) {
   const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
 
   return request(url)
@@ -89,7 +89,7 @@ function deleteVideoChannel (url: string, token: string, accountId: number, chan
     .expect(expectedStatus)
 }
 
-function getVideoChannel (url: string, accountId: number, channelId: number) {
+function getVideoChannel (url: string, accountId: number | string, channelId: number | string) {
   const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
 
   return request(url)
index 943c85cc73b2ac721adb46bcf04e304b138cdbd2..2ba3a860b8eba13f9dab7145029ce59e522ceb81 100644 (file)
@@ -167,6 +167,46 @@ function getMyVideos (url: string, accessToken: string, start: number, count: nu
     .expect('Content-Type', /json/)
 }
 
+function getAccountVideos (url: string, accessToken: string, accountId: number | string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/accounts/' + accountId + '/videos'
+
+  return makeGetRequest({
+    url,
+    path,
+    query: {
+      start,
+      count,
+      sort
+    },
+    token: accessToken,
+    statusCodeExpected: 200
+  })
+}
+
+function getVideoChannelVideos (
+  url: string,
+  accessToken: string,
+  accountId: number | string,
+  videoChannelId: number | string,
+  start: number,
+  count: number,
+  sort?: string
+) {
+  const path = '/api/v1/accounts/' + accountId + '/video-channels/' + videoChannelId + '/videos'
+
+  return makeGetRequest({
+    url,
+    path,
+    query: {
+      start,
+      count,
+      sort
+    },
+    token: accessToken,
+    statusCodeExpected: 200
+  })
+}
+
 function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
   const path = '/api/v1/videos'
 
@@ -514,6 +554,8 @@ export {
   getVideoPrivacies,
   getVideoLanguages,
   getMyVideos,
+  getAccountVideos,
+  getVideoChannelVideos,
   searchVideoWithToken,
   getVideo,
   getVideoWithToken,
index 470295a817e5912bf525fd0d6acefa693bcc24bf..02fbcc315a1ab09e7a51a76f4dee5a482282de14 100644 (file)
@@ -6,9 +6,8 @@ export interface VideoChannel extends Actor {
   description: string
   support: string
   isLocal: boolean
-  owner?: {
-    name: string
+  ownerAccount?: {
+    id: number
     uuid: string
   }
-  videos?: Video[]
 }
index 4a1f06d005fe206cd88c84b498f3b6cd5a5edebf..56941031bc2a8c35da5b2361fb9032857f85060a 100644 (file)
@@ -50,6 +50,25 @@ paths:
           description: successful operation
           schema:
             $ref: '#/definitions/Account'
+  '/accounts/{id}/videos':
+    get:
+      tags:
+        - Accounts
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      parameters:
+        - name: id
+          in: path
+          required: true
+          type: string
+          description: 'The id of the account'
+      responses:
+        '200':
+          description: successful operation
+          schema:
+            $ref: '#/definitions/Video'
   /accounts:
     get:
       tags:
@@ -1115,6 +1134,30 @@ paths:
       responses:
         '204':
           description: successful operation
+  "/account/{accountId}/video-channels/{id}/videos":
+    get:
+      tags:
+        - VideoChannel
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      parameters:
+        - name: accountId
+          in: path
+          required: true
+          type: string
+          description: 'The account id '
+        - name: id
+          in: path
+          required: true
+          type: string
+          description: 'The video channel id '
+      responses:
+        '200':
+          description: successful operation
+          schema:
+            $ref: '#/definitions/Video'
   "/videos/{videoId}/comment-threads":
     get:
       tags:
@@ -1387,17 +1430,13 @@ definitions:
         type: string
       isLocal:
         type: boolean
-      owner:
+      ownerAccount:
         type: object
         properties:
-          name:
-            type: string
+          id:
+            type: number
           uuid:
             type: string
-      videos:
-        type: array
-        items:
-          $ref: "#/definitions/Video"
   VideoComment:
     properties:
       id: