diff --git a/client/src/app/+account/account-about/account-about.component.ts b/client/src/app/+account/account-about/account-about.component.ts index 0772b844e..8746875cb 100644 --- a/client/src/app/+account/account-about/account-about.component.ts +++ b/client/src/app/+account/account-about/account-about.component.ts @@ -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, diff --git a/client/src/app/+account/account.component.ts b/client/src/app/+account/account.component.ts index 1c3e528a7..ae5354ed9 100644 --- a/client/src/app/+account/account.component.ts +++ b/client/src/app/+account/account.component.ts @@ -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, diff --git a/client/src/app/shared/account/account.model.ts b/client/src/app/shared/account/account.model.ts index 3d5176bdd..10a70ac31 100644 --- a/client/src/app/shared/account/account.model.ts +++ b/client/src/app/shared/account/account.model.ts @@ -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 + } } diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 04c5897c5..85987c912 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -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 diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts index 474329b58..dcc4ef196 100644 --- a/server/controllers/api/users.ts +++ b/server/controllers/api/users.ts @@ -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() } diff --git a/server/lib/user.ts b/server/lib/user.ts index 2ea7481e8..d019c4e71 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -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 ( diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index e3a11a41b..9e6f459cf 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts @@ -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 +} diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 40f3be7fe..4a50af265 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts @@ -108,7 +108,7 @@ export class VideoChannelModel extends Model { foreignKey: { allowNull: false }, - onDelete: 'CASCADE' + hooks: true }) Account: AccountModel @@ -234,17 +234,26 @@ export class VideoChannelModel extends Model { 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 } - return Object.assign(actor, account) + if (this.Account) { + videoChannel.ownerAccount = { + id: this.Account.id, + uuid: this.Account.Actor.uuid + } + } + + return Object.assign(actor, videoChannel) } toActivityPubObject (): ActivityPubActor { diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 7ababbf23..f23fac2ab 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -430,7 +430,7 @@ export class VideoModel extends Model { foreignKey: { allowNull: true }, - onDelete: 'cascade' + hooks: true }) VideoChannel: VideoChannelModel @@ -510,10 +510,12 @@ export class VideoModel extends Model { return undefined } - @AfterDestroy + @BeforeDestroy static async removeFilesAndSendDelete (instance: VideoModel) { const tasks: Promise[] = [] + 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 { }) } - 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 () { diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts index acb6bdd57..25b2dc9b9 100644 --- a/server/tests/api/check-params/video-channels.ts +++ b/server/tests/api/check-params/video-channels.ts @@ -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) }) }) diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index da41f515b..850ad12e0 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts @@ -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 } }) diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts index bb458f7a5..a7f3aa8d3 100644 --- a/server/tests/api/users/users-multiple-servers.ts +++ b/server/tests/api/users/users-multiple-servers.ts @@ -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 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 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 - const resVideo = await uploadVideo(servers[0].url, userAccessToken, {}) - videoUUID = resVideo.body.uuid + 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 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) } }) diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index a7552a83a..04e7b8c6a 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts @@ -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 () { diff --git a/server/tests/api/videos/video-nsfw.ts b/server/tests/api/videos/video-nsfw.ts index 4e5ab11ce..8901f38f9 100644 --- a/server/tests/api/videos/video-nsfw.ts +++ b/server/tests/api/videos/video-nsfw.ts @@ -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 diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts index cfc541431..0b4fa89b7 100644 --- a/server/tests/utils/videos/video-channels.ts +++ b/server/tests/utils/videos/video-channels.ts @@ -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) diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts index 943c85cc7..2ba3a860b 100644 --- a/server/tests/utils/videos/videos.ts +++ b/server/tests/utils/videos/videos.ts @@ -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, diff --git a/shared/models/videos/video-channel.model.ts b/shared/models/videos/video-channel.model.ts index 470295a81..02fbcc315 100644 --- a/shared/models/videos/video-channel.model.ts +++ b/shared/models/videos/video-channel.model.ts @@ -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[] } diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 4a1f06d00..56941031b 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -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: