Server: Bulk update videos support field

pull/1868/head
Chocobozzz 2019-05-31 16:30:11 +02:00
parent 9977c12838
commit 7d14d4d2ca
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
11 changed files with 154 additions and 32 deletions

View File

@ -19,7 +19,7 @@ import { VideoChannelModel } from '../../models/video/video-channel'
import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
import { sendUpdateActor } from '../../lib/activitypub/send'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
import { createVideoChannel } from '../../lib/video-channel'
import { createVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
import { setAsyncActorKeys } from '../../lib/activitypub'
import { AccountModel } from '../../models/account/account'
@ -160,6 +160,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
const videoChannelFieldsSave = videoChannelInstance.toJSON()
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
let doBulkVideoUpdate = false
try {
await sequelizeTypescript.transaction(async t => {
@ -167,9 +168,18 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
transaction: t
}
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.displayName)
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description
if (videoChannelInfoToUpdate.support !== undefined) {
const oldSupportField = videoChannelInstance.support
videoChannelInstance.support = videoChannelInfoToUpdate.support
if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) {
doBulkVideoUpdate = true
await VideoModel.bulkUpdateSupportField(videoChannelInstance, t)
}
}
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
await sendUpdateActor(videoChannelInstanceUpdated, t)
@ -179,6 +189,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
oldVideoChannelAuditKeys
)
logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
})
} catch (err) {
@ -192,7 +203,12 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
throw err
}
return res.type('json').status(204).end()
res.type('json').status(204).end()
// Don't process in a transaction, and after the response because it could be long
if (doBulkVideoUpdate) {
await federateAllVideosOfChannel(videoChannelInstance)
}
}
async function removeVideoChannel (req: express.Request, res: express.Response) {

View File

@ -87,6 +87,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Act
commentObject.inReplyTo,
{ err }
)
return
}
const { comment, created } = await addVideoComment(video, commentObject.id)

View File

@ -3,7 +3,8 @@ import * as uuidv4 from 'uuid/v4'
import { VideoChannelCreate } from '../../shared/models'
import { AccountModel } from '../models/account/account'
import { VideoChannelModel } from '../models/video/video-channel'
import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub'
import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
import { VideoModel } from '../models/video/video'
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
const uuid = uuidv4()
@ -33,8 +34,19 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
return videoChannelCreated
}
async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
for (const videoId of videoIds) {
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
await federateVideoIfNeeded(video, false)
}
}
// ---------------------------------------------------------------------------
export {
createVideoChannel
createVideoChannel,
federateAllVideosOfChannel
}

View File

@ -14,6 +14,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
import { areValidationErrors } from '../utils'
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
import { ActorModel } from '../../../models/activitypub/actor'
import { isBooleanValid } from '../../../helpers/custom-validators/misc'
const videoChannelsAddValidator = [
body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
@ -40,9 +41,18 @@ const videoChannelsAddValidator = [
const videoChannelsUpdateValidator = [
param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
body('displayName')
.optional()
.custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
body('description')
.optional()
.custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
body('support')
.optional()
.custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
body('bulkVideosSupportUpdate')
.optional()
.custom(isBooleanValid).withMessage('Should have a valid bulkVideosSupportUpdate boolean field'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })

View File

@ -1515,6 +1515,29 @@ export class VideoModel extends Model<VideoModel> {
.then(results => results.length === 1)
}
static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) {
const options = {
where: {
channelId: videoChannel.id
},
transaction: t
}
return VideoModel.update({ support: videoChannel.support }, options)
}
static getAllIdsFromChannel (videoChannel: VideoChannelModel) {
const query = {
attributes: [ 'id' ],
where: {
channelId: videoChannel.id
}
}
return VideoModel.findAll(query)
.then(videos => videos.map(v => v.id))
}
// threshold corresponds to how many video the field should have to be returned
static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) {
const serverActor = await getServerActor()

View File

@ -24,6 +24,7 @@ import {
checkBadStartPagination
} from '../../../../shared/extra-utils/requests/check-api-params'
import { join } from 'path'
import { VideoChannelUpdate } from '../../../../shared/models/videos'
const expect = chai.expect
@ -169,9 +170,11 @@ describe('Test video channels API validator', function () {
})
describe('When updating a video channel', function () {
const baseCorrectParams = {
const baseCorrectParams: VideoChannelUpdate = {
displayName: 'hello',
description: 'super description'
description: 'super description',
support: 'toto',
bulkVideosSupportUpdate: false
}
let path: string
@ -214,6 +217,11 @@ describe('Test video channels API validator', function () {
await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
})
it('Should fail with a bad bulkVideosSupportUpdate field', async function () {
const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' })
await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
})
it('Should succeed with the correct parameters', async function () {
await makePutBodyRequest({
url: server.url,

View File

@ -2,12 +2,12 @@
import * as chai from 'chai'
import 'mocha'
import { User, Video, VideoChannel } from '../../../../shared/index'
import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
import {
cleanupTests,
createUser,
doubleFollow,
flushAndRunMultipleServers,
flushAndRunMultipleServers, getVideo,
getVideoChannelVideos,
testImage,
updateVideo,
@ -79,7 +79,8 @@ describe('Test video channels', function () {
// The channel is 1 is propagated to servers 2
{
const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'my video name', channelId: secondVideoChannelId })
const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' }
const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributesArg)
videoUUID = res.body.video.uuid
}
@ -201,12 +202,12 @@ describe('Test video channels', function () {
})
it('Should update video channel', async function () {
this.timeout(5000)
this.timeout(15000)
const videoChannelAttributes = {
displayName: 'video channel updated',
description: 'video channel description updated',
support: 'video channel support text updated'
support: 'support updated'
}
await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
@ -224,7 +225,36 @@ describe('Test video channels', function () {
expect(res.body.data[0].name).to.equal('second_video_channel')
expect(res.body.data[0].displayName).to.equal('video channel updated')
expect(res.body.data[0].description).to.equal('video channel description updated')
expect(res.body.data[0].support).to.equal('video channel support text updated')
expect(res.body.data[0].support).to.equal('support updated')
}
})
it('Should not have updated the video support field', async function () {
for (const server of servers) {
const res = await getVideo(server.url, videoUUID)
const video: VideoDetails = res.body
expect(video.support).to.equal('video support field')
}
})
it('Should update the channel support field and update videos too', async function () {
this.timeout(35000)
const videoChannelAttributes = {
support: 'video channel support text updated',
bulkVideosSupportUpdate: true
}
await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
await waitJobs(servers)
for (const server of servers) {
const res = await getVideo(server.url, videoUUID)
const video: VideoDetails = res.body
expect(video.support).to.equal(videoChannelAttributes.support)
}
})

View File

@ -74,12 +74,13 @@ function updateVideoChannel (
attributes: VideoChannelUpdate,
expectedStatus = 204
) {
const body = {}
const body: any = {}
const path = '/api/v1/video-channels/' + channelName
if (attributes.displayName) body['displayName'] = attributes.displayName
if (attributes.description) body['description'] = attributes.description
if (attributes.support) body['support'] = attributes.support
if (attributes.displayName) body.displayName = attributes.displayName
if (attributes.description) body.description = attributes.description
if (attributes.support) body.support = attributes.support
if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate
return request(url)
.put(path)

View File

@ -355,6 +355,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
.set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + accessToken)
.field('name', attributes.name)
.field('support', attributes.support)
.field('nsfw', JSON.stringify(attributes.nsfw))
.field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
.field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))

View File

@ -1,5 +1,7 @@
export interface VideoChannelUpdate {
displayName: string
displayName?: string
description?: string
support?: string
bulkVideosSupportUpdate?: boolean
}

View File

@ -1322,7 +1322,10 @@ paths:
'204':
$ref: '#/paths/~1users~1me/put/responses/204'
requestBody:
$ref: '#/components/requestBodies/VideoChannelInput'
content:
application/json:
schema:
$ref: '#/components/schemas/VideoChannelCreate'
'/video-channels/{channelHandle}':
get:
summary: Get a video channel by its id
@ -1349,7 +1352,10 @@ paths:
'204':
$ref: '#/paths/~1users~1me/put/responses/204'
requestBody:
$ref: '#/components/requestBodies/VideoChannelInput'
content:
application/json:
schema:
$ref: '#/components/schemas/VideoChannelUpdate'
delete:
summary: Delete a video channel by its id
security:
@ -1775,12 +1781,6 @@ components:
type: array
items:
type: string
requestBodies:
VideoChannelInput:
content:
application/json:
schema:
$ref: '#/components/schemas/VideoChannelInput'
securitySchemes:
OAuth2:
description: >
@ -2294,10 +2294,28 @@ components:
- username
- password
- email
VideoChannelInput:
VideoChannelCreate:
properties:
name:
type: string
displayName:
type: string
description:
type: string
support:
type: string
required:
- name
- displayName
VideoChannelUpdate:
properties:
displayName:
type: string
description:
type: string
support:
type: string
bulkVideosSupportUpdate:
type: boolean
description: 'Update all videos support field of this channel'