aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-09-04 10:22:10 +0200
committerChocobozzz <me@florianbigard.com>2018-09-04 10:49:53 +0200
commit5cf84858d49f4231cc4efec5e3132f17f65f6cf6 (patch)
treef1ff23476ff54c32c3fa34db79c11f242855d3b4
parent0b74c74abe5a44e9f564ab6adb5177ab17d28e91 (diff)
downloadPeerTube-5cf84858d49f4231cc4efec5e3132f17f65f6cf6.tar.gz
PeerTube-5cf84858d49f4231cc4efec5e3132f17f65f6cf6.tar.zst
PeerTube-5cf84858d49f4231cc4efec5e3132f17f65f6cf6.zip
Add federation to ownership change
-rw-r--r--client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts4
-rw-r--r--server/controllers/api/users/index.ts2
-rw-r--r--server/controllers/api/videos/ownership.ts33
-rw-r--r--server/helpers/custom-validators/activitypub/undo.ts4
-rw-r--r--server/lib/activitypub/process/process-undo.ts10
-rw-r--r--server/lib/activitypub/send/send-announce.ts5
-rw-r--r--server/lib/activitypub/send/send-update.ts10
-rw-r--r--server/lib/activitypub/share.ts2
-rw-r--r--server/models/account/user.ts32
-rw-r--r--server/models/video/video-change-ownership.ts12
-rw-r--r--server/models/video/video-channel.ts16
-rw-r--r--server/tests/api/videos/index.ts1
-rw-r--r--server/tests/api/videos/video-change-ownership.ts85
13 files changed, 138 insertions, 78 deletions
diff --git a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
index 0aa4c32ee..7437b939a 100644
--- a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
+++ b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts
@@ -53,7 +53,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
53 const query = event.query 53 const query = event.query
54 this.userService.autocomplete(query) 54 this.userService.autocomplete(query)
55 .subscribe( 55 .subscribe(
56 (usernames) => { 56 usernames => {
57 this.usernamePropositions = usernames 57 this.usernamePropositions = usernames
58 }, 58 },
59 59
@@ -67,7 +67,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
67 this.videoOwnershipService 67 this.videoOwnershipService
68 .changeOwnership(this.video.id, username) 68 .changeOwnership(this.video.id, username)
69 .subscribe( 69 .subscribe(
70 () => this.notificationsService.success(this.i18n('Success'), this.i18n('Ownership changed.')), 70 () => this.notificationsService.success(this.i18n('Success'), this.i18n('Ownership change request sent.')),
71 71
72 err => this.notificationsService.error(this.i18n('Error'), err.message) 72 err => this.notificationsService.error(this.i18n('Error'), err.message)
73 ) 73 )
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index faba7e208..07edf3727 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -229,7 +229,7 @@ function getUser (req: express.Request, res: express.Response, next: express.Nex
229} 229}
230 230
231async function autocompleteUsers (req: express.Request, res: express.Response, next: express.NextFunction) { 231async function autocompleteUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
232 const resultList = await UserModel.autocomplete(req.query.search as string) 232 const resultList = await UserModel.autoComplete(req.query.search as string)
233 233
234 return res.json(resultList) 234 return res.json(resultList)
235} 235}
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index fc42f5fff..d26ed6cfc 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -14,9 +14,11 @@ import {
14import { AccountModel } from '../../../models/account/account' 14import { AccountModel } from '../../../models/account/account'
15import { VideoModel } from '../../../models/video/video' 15import { VideoModel } from '../../../models/video/video'
16import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership' 16import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
17import { VideoChangeOwnershipStatus } from '../../../../shared/models/videos' 17import { VideoChangeOwnershipStatus, VideoPrivacy, VideoState } from '../../../../shared/models/videos'
18import { VideoChannelModel } from '../../../models/video/video-channel' 18import { VideoChannelModel } from '../../../models/video/video-channel'
19import { getFormattedObjects } from '../../../helpers/utils' 19import { getFormattedObjects } from '../../../helpers/utils'
20import { changeVideoChannelShare } from '../../../lib/activitypub'
21import { sendUpdateVideo } from '../../../lib/activitypub/send'
20 22
21const ownershipVideoRouter = express.Router() 23const ownershipVideoRouter = express.Router()
22 24
@@ -59,8 +61,8 @@ async function giveVideoOwnership (req: express.Request, res: express.Response)
59 const initiatorAccount = res.locals.oauth.token.User.Account as AccountModel 61 const initiatorAccount = res.locals.oauth.token.User.Account as AccountModel
60 const nextOwner = res.locals.nextOwner as AccountModel 62 const nextOwner = res.locals.nextOwner as AccountModel
61 63
62 await sequelizeTypescript.transaction(async t => { 64 await sequelizeTypescript.transaction(t => {
63 await VideoChangeOwnershipModel.findOrCreate({ 65 return VideoChangeOwnershipModel.findOrCreate({
64 where: { 66 where: {
65 initiatorAccountId: initiatorAccount.id, 67 initiatorAccountId: initiatorAccount.id,
66 nextOwnerAccountId: nextOwner.id, 68 nextOwnerAccountId: nextOwner.id,
@@ -72,11 +74,14 @@ async function giveVideoOwnership (req: express.Request, res: express.Response)
72 nextOwnerAccountId: nextOwner.id, 74 nextOwnerAccountId: nextOwner.id,
73 videoId: videoInstance.id, 75 videoId: videoInstance.id,
74 status: VideoChangeOwnershipStatus.WAITING 76 status: VideoChangeOwnershipStatus.WAITING
75 } 77 },
78 transaction: t
76 }) 79 })
77 logger.info('Ownership change for video %s created.', videoInstance.name) 80
78 return res.type('json').status(204).end()
79 }) 81 })
82
83 logger.info('Ownership change for video %s created.', videoInstance.name)
84 return res.type('json').status(204).end()
80} 85}
81 86
82async function listVideoOwnership (req: express.Request, res: express.Response) { 87async function listVideoOwnership (req: express.Request, res: express.Response) {
@@ -97,11 +102,19 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
97 const targetVideo = videoChangeOwnership.Video 102 const targetVideo = videoChangeOwnership.Video
98 const channel = res.locals.videoChannel as VideoChannelModel 103 const channel = res.locals.videoChannel as VideoChannelModel
99 104
105 const oldVideoChannel = await VideoChannelModel.loadByIdAndPopulateAccount(targetVideo.channelId)
106
100 targetVideo.set('channelId', channel.id) 107 targetVideo.set('channelId', channel.id)
108 const targetVideoUpdated = await targetVideo.save({ transaction: t })
109 targetVideoUpdated.VideoChannel = channel
110
111 if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
112 await changeVideoChannelShare(targetVideoUpdated, oldVideoChannel, t)
113 await sendUpdateVideo(targetVideoUpdated, t, oldVideoChannel.Account.Actor)
114 }
101 115
102 await targetVideo.save()
103 videoChangeOwnership.set('status', VideoChangeOwnershipStatus.ACCEPTED) 116 videoChangeOwnership.set('status', VideoChangeOwnershipStatus.ACCEPTED)
104 await videoChangeOwnership.save() 117 await videoChangeOwnership.save({ transaction: t })
105 118
106 return res.sendStatus(204) 119 return res.sendStatus(204)
107 }) 120 })
@@ -110,8 +123,10 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
110async function refuseOwnership (req: express.Request, res: express.Response) { 123async function refuseOwnership (req: express.Request, res: express.Response) {
111 return sequelizeTypescript.transaction(async t => { 124 return sequelizeTypescript.transaction(async t => {
112 const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel 125 const videoChangeOwnership = res.locals.videoChangeOwnership as VideoChangeOwnershipModel
126
113 videoChangeOwnership.set('status', VideoChangeOwnershipStatus.REFUSED) 127 videoChangeOwnership.set('status', VideoChangeOwnershipStatus.REFUSED)
114 await videoChangeOwnership.save() 128 await videoChangeOwnership.save({ transaction: t })
129
115 return res.sendStatus(204) 130 return res.sendStatus(204)
116 }) 131 })
117} 132}
diff --git a/server/helpers/custom-validators/activitypub/undo.ts b/server/helpers/custom-validators/activitypub/undo.ts
index a2831b0bf..f50f224fa 100644
--- a/server/helpers/custom-validators/activitypub/undo.ts
+++ b/server/helpers/custom-validators/activitypub/undo.ts
@@ -1,13 +1,15 @@
1import { isActorFollowActivityValid } from './actor' 1import { isActorFollowActivityValid } from './actor'
2import { isBaseActivityValid } from './misc' 2import { isBaseActivityValid } from './misc'
3import { isDislikeActivityValid, isLikeActivityValid } from './rate' 3import { isDislikeActivityValid, isLikeActivityValid } from './rate'
4import { isAnnounceActivityValid } from './announce'
4 5
5function isUndoActivityValid (activity: any) { 6function isUndoActivityValid (activity: any) {
6 return isBaseActivityValid(activity, 'Undo') && 7 return isBaseActivityValid(activity, 'Undo') &&
7 ( 8 (
8 isActorFollowActivityValid(activity.object) || 9 isActorFollowActivityValid(activity.object) ||
9 isLikeActivityValid(activity.object) || 10 isLikeActivityValid(activity.object) ||
10 isDislikeActivityValid(activity.object) 11 isDislikeActivityValid(activity.object) ||
12 isAnnounceActivityValid(activity.object)
11 ) 13 )
12} 14}
13 15
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index eab9e3d61..1c1de8827 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -104,17 +104,19 @@ function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) {
104 104
105function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) { 105function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) {
106 return sequelizeTypescript.transaction(async t => { 106 return sequelizeTypescript.transaction(async t => {
107 const byAccount = await AccountModel.loadByUrl(actorUrl, t) 107 const byActor = await ActorModel.loadByUrl(actorUrl, t)
108 if (!byAccount) throw new Error('Unknown account ' + actorUrl) 108 if (!byActor) throw new Error('Unknown actor ' + actorUrl)
109 109
110 const share = await VideoShareModel.loadByUrl(announceActivity.id, t) 110 const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
111 if (!share) throw new Error(`'Unknown video share ${announceActivity.id}.`) 111 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
112
113 if (share.actorId !== byActor.id) throw new Error(`${share.url} is not shared by ${byActor.url}.`)
112 114
113 await share.destroy({ transaction: t }) 115 await share.destroy({ transaction: t })
114 116
115 if (share.Video.isOwned()) { 117 if (share.Video.isOwned()) {
116 // Don't resend the activity to the sender 118 // Don't resend the activity to the sender
117 const exceptions = [ byAccount.Actor ] 119 const exceptions = [ byActor ]
118 120
119 await forwardVideoRelatedActivity(announceActivity, t, exceptions, share.Video) 121 await forwardVideoRelatedActivity(announceActivity, t, exceptions, share.Video)
120 } 122 }
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
index 1ab05ca3c..352813d73 100644
--- a/server/lib/activitypub/send/send-announce.ts
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -20,7 +20,10 @@ async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareMod
20 20
21 logger.info('Creating job to send announce %s.', videoShare.url) 21 logger.info('Creating job to send announce %s.', videoShare.url)
22 22
23 return broadcastToFollowers(data, byActor, [ byActor ], t) 23 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
24 const followersException = [ byActor ]
25
26 return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException)
24} 27}
25 28
26function announceActivityData (url: string, byActor: ActorModel, object: string, audience?: ActivityAudience): ActivityAnnounce { 29function announceActivityData (url: string, byActor: ActorModel, object: string, audience?: ActivityAudience): ActivityAnnounce {
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 17d4f185c..6f1d80898 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -10,13 +10,19 @@ import { getUpdateActivityPubUrl } from '../url'
10import { broadcastToFollowers } from './utils' 10import { broadcastToFollowers } from './utils'
11import { audiencify, getAudience } from '../audience' 11import { audiencify, getAudience } from '../audience'
12import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
13import { videoFeedsValidator } from '../../../middlewares/validators'
14import { VideoCaptionModel } from '../../../models/video/video-caption'
13 15
14async function sendUpdateVideo (video: VideoModel, t: Transaction) { 16async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) {
15 logger.info('Creating job to update video %s.', video.url) 17 logger.info('Creating job to update video %s.', video.url)
16 18
17 const byActor = video.VideoChannel.Account.Actor 19 const byActor = overrodeByActor ? overrodeByActor : video.VideoChannel.Account.Actor
18 20
19 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) 21 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
22
23 // Needed to build the AP object
24 if (!video.VideoCaptions) video.VideoCaptions = await video.$get('VideoCaptions') as VideoCaptionModel[]
25
20 const videoObject = video.toActivityPubObject() 26 const videoObject = video.toActivityPubObject()
21 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) 27 const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC)
22 28
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index fe3d73e9b..3ff60a97c 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -22,6 +22,8 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction)
22} 22}
23 23
24async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 24async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) {
25 logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name)
26
25 await undoShareByVideoChannel(video, oldVideoChannel, t) 27 await undoShareByVideoChannel(video, oldVideoChannel, t)
26 28
27 await shareByVideoChannel(video, t) 29 await shareByVideoChannel(video, t)
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 4b13e47a0..680b1d52d 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -23,13 +23,13 @@ import {
23 isUserAutoPlayVideoValid, 23 isUserAutoPlayVideoValid,
24 isUserBlockedReasonValid, 24 isUserBlockedReasonValid,
25 isUserBlockedValid, 25 isUserBlockedValid,
26 isUserNSFWPolicyValid,
27 isUserEmailVerifiedValid, 26 isUserEmailVerifiedValid,
27 isUserNSFWPolicyValid,
28 isUserPasswordValid, 28 isUserPasswordValid,
29 isUserRoleValid, 29 isUserRoleValid,
30 isUserUsernameValid, 30 isUserUsernameValid,
31 isUserVideoQuotaValid, 31 isUserVideoQuotaDailyValid,
32 isUserVideoQuotaDailyValid 32 isUserVideoQuotaValid
33} from '../../helpers/custom-validators/users' 33} from '../../helpers/custom-validators/users'
34import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' 34import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
35import { OAuthTokenModel } from '../oauth/oauth-token' 35import { OAuthTokenModel } from '../oauth/oauth-token'
@@ -39,7 +39,6 @@ import { AccountModel } from './account'
39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' 39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
40import { values } from 'lodash' 40import { values } from 'lodash'
41import { NSFW_POLICY_TYPES } from '../../initializers' 41import { NSFW_POLICY_TYPES } from '../../initializers'
42import { VideoFileModel } from '../video/video-file'
43 42
44enum ScopeNames { 43enum ScopeNames {
45 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' 44 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
@@ -296,6 +295,20 @@ export class UserModel extends Model<UserModel> {
296 } 295 }
297 } 296 }
298 297
298 static autoComplete (search: string) {
299 const query = {
300 where: {
301 username: {
302 [ Sequelize.Op.like ]: `%${search}%`
303 }
304 },
305 limit: 10
306 }
307
308 return UserModel.findAll(query)
309 .then(u => u.map(u => u.username))
310 }
311
299 hasRight (right: UserRight) { 312 hasRight (right: UserRight) {
300 return hasUserRight(this.role, right) 313 return hasUserRight(this.role, right)
301 } 314 }
@@ -394,15 +407,4 @@ export class UserModel extends Model<UserModel> {
394 return parseInt(total, 10) 407 return parseInt(total, 10)
395 }) 408 })
396 } 409 }
397
398 static autocomplete (search: string) {
399 return UserModel.findAll({
400 where: {
401 username: {
402 [Sequelize.Op.like]: `%${search}%`
403 }
404 }
405 })
406 .then(u => u.map(u => u.username))
407 }
408} 410}
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts
index c9cff5054..48c07728f 100644
--- a/server/models/video/video-change-ownership.ts
+++ b/server/models/video/video-change-ownership.ts
@@ -39,7 +39,9 @@ enum ScopeNames {
39 { 39 {
40 model: () => VideoModel, 40 model: () => VideoModel,
41 required: true, 41 required: true,
42 include: [{ model: () => VideoFileModel }] 42 include: [
43 { model: () => VideoFileModel }
44 ]
43 } 45 }
44 ] 46 ]
45 } 47 }
@@ -94,15 +96,17 @@ export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel>
94 Video: VideoModel 96 Video: VideoModel
95 97
96 static listForApi (nextOwnerId: number, start: number, count: number, sort: string) { 98 static listForApi (nextOwnerId: number, start: number, count: number, sort: string) {
97 return VideoChangeOwnershipModel.scope(ScopeNames.FULL).findAndCountAll({ 99 const query = {
98 offset: start, 100 offset: start,
99 limit: count, 101 limit: count,
100 order: getSort(sort), 102 order: getSort(sort),
101 where: { 103 where: {
102 nextOwnerAccountId: nextOwnerId 104 nextOwnerAccountId: nextOwnerId
103 } 105 }
104 }) 106 }
105 .then(({ rows, count }) => ({ total: count, data: rows })) 107
108 return VideoChangeOwnershipModel.scope(ScopeNames.FULL).findAndCountAll(query)
109 .then(({ rows, count }) => ({ total: count, data: rows }))
106 } 110 }
107 111
108 static load (id: number) { 112 static load (id: number) {
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 475530daf..f4586917e 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -296,6 +296,12 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
296 }) 296 })
297 } 297 }
298 298
299 static loadByIdAndPopulateAccount (id: number) {
300 return VideoChannelModel.unscoped()
301 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
302 .findById(id)
303 }
304
299 static loadByIdAndAccount (id: number, accountId: number) { 305 static loadByIdAndAccount (id: number, accountId: number) {
300 const query = { 306 const query = {
301 where: { 307 where: {
@@ -304,13 +310,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
304 } 310 }
305 } 311 }
306 312
307 return VideoChannelModel 313 return VideoChannelModel.unscoped()
308 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 314 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
309 .findOne(query) 315 .findOne(query)
310 } 316 }
311 317
312 static loadAndPopulateAccount (id: number) { 318 static loadAndPopulateAccount (id: number) {
313 return VideoChannelModel 319 return VideoChannelModel.unscoped()
314 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 320 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
315 .findById(id) 321 .findById(id)
316 } 322 }
@@ -365,7 +371,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
365 ] 371 ]
366 } 372 }
367 373
368 return VideoChannelModel 374 return VideoChannelModel.unscoped()
369 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 375 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
370 .findOne(query) 376 .findOne(query)
371 } 377 }
@@ -390,7 +396,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
390 ] 396 ]
391 } 397 }
392 398
393 return VideoChannelModel 399 return VideoChannelModel.unscoped()
394 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 400 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
395 .findOne(query) 401 .findOne(query)
396 } 402 }
@@ -402,7 +408,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
402 ] 408 ]
403 } 409 }
404 410
405 return VideoChannelModel 411 return VideoChannelModel.unscoped()
406 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) 412 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ])
407 .findById(id, options) 413 .findById(id, options)
408 } 414 }
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index 8286ff356..a328a49c1 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -5,6 +5,7 @@ import './video-abuse'
5import './video-blacklist' 5import './video-blacklist'
6import './video-blacklist-management' 6import './video-blacklist-management'
7import './video-captions' 7import './video-captions'
8import './vidoe-change-ownership'
8import './video-channels' 9import './video-channels'
9import './video-comments' 10import './video-comments'
10import './video-description' 11import './video-description'
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts
index 275be40be..1578a471d 100644
--- a/server/tests/api/videos/video-change-ownership.ts
+++ b/server/tests/api/videos/video-change-ownership.ts
@@ -5,7 +5,7 @@ import 'mocha'
5import { 5import {
6 acceptChangeOwnership, 6 acceptChangeOwnership,
7 changeVideoOwnership, 7 changeVideoOwnership,
8 createUser, 8 createUser, doubleFollow, flushAndRunMultipleServers,
9 flushTests, 9 flushTests,
10 getMyUserInformation, 10 getMyUserInformation,
11 getVideoChangeOwnershipList, 11 getVideoChangeOwnershipList,
@@ -16,15 +16,17 @@ import {
16 ServerInfo, 16 ServerInfo,
17 setAccessTokensToServers, 17 setAccessTokensToServers,
18 uploadVideo, 18 uploadVideo,
19 userLogin 19 userLogin,
20 getVideo
20} from '../../utils' 21} from '../../utils'
21import { waitJobs } from '../../utils/server/jobs' 22import { waitJobs } from '../../utils/server/jobs'
22import { User } from '../../../../shared/models/users' 23import { User } from '../../../../shared/models/users'
24import { VideoDetails } from '../../../../shared/models/videos'
23 25
24const expect = chai.expect 26const expect = chai.expect
25 27
26describe('Test video change ownership - nominal', function () { 28describe('Test video change ownership - nominal', function () {
27 let server: ServerInfo = undefined 29 let servers: ServerInfo[] = []
28 const firstUser = { 30 const firstUser = {
29 username: 'first', 31 username: 'first',
30 password: 'My great password' 32 password: 'My great password'
@@ -40,43 +42,44 @@ describe('Test video change ownership - nominal', function () {
40 before(async function () { 42 before(async function () {
41 this.timeout(50000) 43 this.timeout(50000)
42 44
43 // Run one server 45 servers = await flushAndRunMultipleServers(2)
44 await flushTests() 46 await setAccessTokensToServers(servers)
45 server = await runServer(1)
46 await setAccessTokensToServers([server])
47 47
48 const videoQuota = 42000000 48 const videoQuota = 42000000
49 await createUser(server.url, server.accessToken, firstUser.username, firstUser.password, videoQuota) 49 await createUser(servers[0].url, servers[0].accessToken, firstUser.username, firstUser.password, videoQuota)
50 await createUser(server.url, server.accessToken, secondUser.username, secondUser.password, videoQuota) 50 await createUser(servers[0].url, servers[0].accessToken, secondUser.username, secondUser.password, videoQuota)
51 51
52 firstUserAccessToken = await userLogin(server, firstUser) 52 firstUserAccessToken = await userLogin(servers[0], firstUser)
53 secondUserAccessToken = await userLogin(server, secondUser) 53 secondUserAccessToken = await userLogin(servers[0], secondUser)
54 54
55 // Upload some videos on the server 55 const videoAttributes = {
56 const video1Attributes = {
57 name: 'my super name', 56 name: 'my super name',
58 description: 'my super description' 57 description: 'my super description'
59 } 58 }
60 await uploadVideo(server.url, firstUserAccessToken, video1Attributes) 59 await uploadVideo(servers[0].url, firstUserAccessToken, videoAttributes)
61 60
62 await waitJobs(server) 61 await waitJobs(servers)
63 62
64 const res = await getVideosList(server.url) 63 const res = await getVideosList(servers[0].url)
65 const videos = res.body.data 64 const videos = res.body.data
66 65
67 expect(videos.length).to.equal(1) 66 expect(videos.length).to.equal(1)
68 67
69 server.video = videos.find(video => video.name === 'my super name') 68 const video = videos.find(video => video.name === 'my super name')
69 expect(video.channel.name).to.equal('first_channel')
70 servers[0].video = video
71
72 await doubleFollow(servers[0], servers[1])
70 }) 73 })
71 74
72 it('Should not have video change ownership', async function () { 75 it('Should not have video change ownership', async function () {
73 const resFirstUser = await getVideoChangeOwnershipList(server.url, firstUserAccessToken) 76 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken)
74 77
75 expect(resFirstUser.body.total).to.equal(0) 78 expect(resFirstUser.body.total).to.equal(0)
76 expect(resFirstUser.body.data).to.be.an('array') 79 expect(resFirstUser.body.data).to.be.an('array')
77 expect(resFirstUser.body.data.length).to.equal(0) 80 expect(resFirstUser.body.data.length).to.equal(0)
78 81
79 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 82 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken)
80 83
81 expect(resSecondUser.body.total).to.equal(0) 84 expect(resSecondUser.body.total).to.equal(0)
82 expect(resSecondUser.body.data).to.be.an('array') 85 expect(resSecondUser.body.data).to.be.an('array')
@@ -86,17 +89,17 @@ describe('Test video change ownership - nominal', function () {
86 it('Should send a request to change ownership of a video', async function () { 89 it('Should send a request to change ownership of a video', async function () {
87 this.timeout(15000) 90 this.timeout(15000)
88 91
89 await changeVideoOwnership(server.url, firstUserAccessToken, server.video.id, secondUser.username) 92 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username)
90 }) 93 })
91 94
92 it('Should only return a request to change ownership for the second user', async function () { 95 it('Should only return a request to change ownership for the second user', async function () {
93 const resFirstUser = await getVideoChangeOwnershipList(server.url, firstUserAccessToken) 96 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken)
94 97
95 expect(resFirstUser.body.total).to.equal(0) 98 expect(resFirstUser.body.total).to.equal(0)
96 expect(resFirstUser.body.data).to.be.an('array') 99 expect(resFirstUser.body.data).to.be.an('array')
97 expect(resFirstUser.body.data.length).to.equal(0) 100 expect(resFirstUser.body.data.length).to.equal(0)
98 101
99 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 102 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken)
100 103
101 expect(resSecondUser.body.total).to.equal(1) 104 expect(resSecondUser.body.total).to.equal(1)
102 expect(resSecondUser.body.data).to.be.an('array') 105 expect(resSecondUser.body.data).to.be.an('array')
@@ -108,13 +111,13 @@ describe('Test video change ownership - nominal', function () {
108 it('Should accept the same change ownership request without crashing', async function () { 111 it('Should accept the same change ownership request without crashing', async function () {
109 this.timeout(10000) 112 this.timeout(10000)
110 113
111 await changeVideoOwnership(server.url, firstUserAccessToken, server.video.id, secondUser.username) 114 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username)
112 }) 115 })
113 116
114 it('Should not create multiple change ownership requests while one is waiting', async function () { 117 it('Should not create multiple change ownership requests while one is waiting', async function () {
115 this.timeout(10000) 118 this.timeout(10000)
116 119
117 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 120 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken)
118 121
119 expect(resSecondUser.body.total).to.equal(1) 122 expect(resSecondUser.body.total).to.equal(1)
120 expect(resSecondUser.body.data).to.be.an('array') 123 expect(resSecondUser.body.data).to.be.an('array')
@@ -124,29 +127,29 @@ describe('Test video change ownership - nominal', function () {
124 it('Should not be possible to refuse the change of ownership from first user', async function () { 127 it('Should not be possible to refuse the change of ownership from first user', async function () {
125 this.timeout(10000) 128 this.timeout(10000)
126 129
127 await refuseChangeOwnership(server.url, firstUserAccessToken, lastRequestChangeOwnershipId, 403) 130 await refuseChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, 403)
128 }) 131 })
129 132
130 it('Should be possible to refuse the change of ownership from second user', async function () { 133 it('Should be possible to refuse the change of ownership from second user', async function () {
131 this.timeout(10000) 134 this.timeout(10000)
132 135
133 await refuseChangeOwnership(server.url, secondUserAccessToken, lastRequestChangeOwnershipId) 136 await refuseChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId)
134 }) 137 })
135 138
136 it('Should send a new request to change ownership of a video', async function () { 139 it('Should send a new request to change ownership of a video', async function () {
137 this.timeout(15000) 140 this.timeout(15000)
138 141
139 await changeVideoOwnership(server.url, firstUserAccessToken, server.video.id, secondUser.username) 142 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username)
140 }) 143 })
141 144
142 it('Should return two requests to change ownership for the second user', async function () { 145 it('Should return two requests to change ownership for the second user', async function () {
143 const resFirstUser = await getVideoChangeOwnershipList(server.url, firstUserAccessToken) 146 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken)
144 147
145 expect(resFirstUser.body.total).to.equal(0) 148 expect(resFirstUser.body.total).to.equal(0)
146 expect(resFirstUser.body.data).to.be.an('array') 149 expect(resFirstUser.body.data).to.be.an('array')
147 expect(resFirstUser.body.data.length).to.equal(0) 150 expect(resFirstUser.body.data.length).to.equal(0)
148 151
149 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 152 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken)
150 153
151 expect(resSecondUser.body.total).to.equal(2) 154 expect(resSecondUser.body.total).to.equal(2)
152 expect(resSecondUser.body.data).to.be.an('array') 155 expect(resSecondUser.body.data).to.be.an('array')
@@ -158,23 +161,37 @@ describe('Test video change ownership - nominal', function () {
158 it('Should not be possible to accept the change of ownership from first user', async function () { 161 it('Should not be possible to accept the change of ownership from first user', async function () {
159 this.timeout(10000) 162 this.timeout(10000)
160 163
161 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken) 164 const secondUserInformationResponse = await getMyUserInformation(servers[0].url, secondUserAccessToken)
162 const secondUserInformation: User = secondUserInformationResponse.body 165 const secondUserInformation: User = secondUserInformationResponse.body
163 const channelId = secondUserInformation.videoChannels[0].id 166 const channelId = secondUserInformation.videoChannels[0].id
164 await acceptChangeOwnership(server.url, firstUserAccessToken, lastRequestChangeOwnershipId, channelId, 403) 167 await acceptChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, channelId, 403)
165 }) 168 })
166 169
167 it('Should be possible to accept the change of ownership from second user', async function () { 170 it('Should be possible to accept the change of ownership from second user', async function () {
168 this.timeout(10000) 171 this.timeout(10000)
169 172
170 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken) 173 const secondUserInformationResponse = await getMyUserInformation(servers[0].url, secondUserAccessToken)
171 const secondUserInformation: User = secondUserInformationResponse.body 174 const secondUserInformation: User = secondUserInformationResponse.body
172 const channelId = secondUserInformation.videoChannels[0].id 175 const channelId = secondUserInformation.videoChannels[0].id
173 await acceptChangeOwnership(server.url, secondUserAccessToken, lastRequestChangeOwnershipId, channelId) 176 await acceptChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId, channelId)
177
178 await waitJobs(servers)
179 })
180
181 it('Should have video channel updated', async function () {
182 for (const server of servers) {
183 const res = await getVideo(server.url, servers[0].video.uuid)
184
185 const video: VideoDetails = res.body
186
187 expect(video.name).to.equal('my super name')
188 expect(video.channel.displayName).to.equal('Main second channel')
189 expect(video.channel.name).to.equal('second_channel')
190 }
174 }) 191 })
175 192
176 after(async function () { 193 after(async function () {
177 killallServers([server]) 194 killallServers(servers)
178 }) 195 })
179}) 196})
180 197