From: Chocobozzz Date: Wed, 25 Apr 2018 14:15:39 +0000 (+0200) Subject: Update video-channel routes (again) X-Git-Tag: v1.0.0-beta.4~20 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=cc918ac3f45e32f031cce7b6e0473e5c2c34b8ae;p=oweals%2Fpeertube.git Update video-channel routes (again) Use /video-channels now, it's more simple for clients --- diff --git a/CHANGELOG.md b/CHANGELOG.md index ac3f61735..4a327737d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Hide by default NSFW videos. Update the `instance.default_nsfw_policy` configuration to `blur` to keep the old behaviour * Move video channels routes: - * `/videos/channels` routes to `/accounts/{accountId}/video-channels` + * `/videos/channels` routes to `/video-channels` * `/videos/accounts/{accountId}/channels` route to `/accounts/{accountId}/video-channels` * PeerTube now listen on 127.0.0.1 by default * Use ISO 639 for language (*en*, *es*, *fr*...) diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 85987c912..ccae0436b 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -1,30 +1,18 @@ import * as express from 'express' -import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils' +import { getFormattedObjects } from '../../helpers/utils' import { asyncMiddleware, - authenticate, listVideoAccountChannelsValidator, optionalAuthenticate, paginationValidator, setDefaultPagination, - setDefaultSort, - videoChannelsAddValidator, - videoChannelsGetValidator, - videoChannelsRemoveValidator, - videoChannelsUpdateValidator + setDefaultSort } from '../../middlewares' import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' import { VideoModel } from '../../models/video/video' import { isNSFWHidden } from '../../helpers/express-utils' import { VideoChannelModel } from '../../models/video/video-channel' -import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' -import { sendUpdateActor } from '../../lib/activitypub/send' -import { createVideoChannel } from '../../lib/video-channel' -import { setAsyncActorKeys } from '../../lib/activitypub' -import { sequelizeTypescript } from '../../initializers' -import { logger } from '../../helpers/logger' -import { retryTransactionWrapper } from '../../helpers/database-utils' const accountsRouter = express.Router() @@ -56,39 +44,6 @@ accountsRouter.get('/:accountId/video-channels', asyncMiddleware(listVideoAccountChannels) ) -accountsRouter.post('/:accountId/video-channels', - authenticate, - videoChannelsAddValidator, - asyncMiddleware(addVideoChannelRetryWrapper) -) - -accountsRouter.put('/:accountId/video-channels/:id', - authenticate, - asyncMiddleware(videoChannelsUpdateValidator), - updateVideoChannelRetryWrapper -) - -accountsRouter.delete('/:accountId/video-channels/:id', - authenticate, - asyncMiddleware(videoChannelsRemoveValidator), - asyncMiddleware(removeVideoChannelRetryWrapper) -) - -accountsRouter.get('/:accountId/video-channels/:id', - asyncMiddleware(videoChannelsGetValidator), - asyncMiddleware(getVideoChannel) -) - -accountsRouter.get('/:accountId/video-channels/:id/videos', - asyncMiddleware(videoChannelsGetValidator), - paginationValidator, - videosSortValidator, - setDefaultSort, - setDefaultPagination, - optionalAuthenticate, - asyncMiddleware(listVideoChannelVideos) -) - // --------------------------------------------------------------------------- export { @@ -115,125 +70,6 @@ async function listVideoAccountChannels (req: express.Request, res: express.Resp return res.json(getFormattedObjects(resultList.data, resultList.total)) } -// Wrapper to video channel add that retry the async function if there is a database error -// We need this because we run the transaction in SERIALIZABLE isolation that can fail -async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot insert the video video channel with many retries.' - } - - const videoChannel = await retryTransactionWrapper(addVideoChannel, options) - return res.json({ - videoChannel: { - id: videoChannel.id, - uuid: videoChannel.Actor.uuid - } - }).end() -} - -async function addVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInfo: VideoChannelCreate = req.body - const account: AccountModel = res.locals.oauth.token.User.Account - - const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { - return createVideoChannel(videoChannelInfo, account, t) - }) - - setAsyncActorKeys(videoChannelCreated.Actor) - .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) - - logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) - - return videoChannelCreated -} - -async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot update the video with many retries.' - } - - await retryTransactionWrapper(updateVideoChannel, options) - - return res.type('json').status(204).end() -} - -async function updateVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInstance = res.locals.videoChannel as VideoChannelModel - const videoChannelFieldsSave = videoChannelInstance.toJSON() - const videoChannelInfoToUpdate = req.body as VideoChannelUpdate - - try { - await sequelizeTypescript.transaction(async t => { - const sequelizeOptions = { - transaction: t - } - - if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) - if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) - if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) - - const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) - await sendUpdateActor(videoChannelInstanceUpdated, t) - }) - - logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) - } catch (err) { - logger.debug('Cannot update the video channel.', { err }) - - // Force fields we want to update - // If the transaction is retried, sequelize will think the object has not changed - // So it will skip the SQL request, even if the last one was ROLLBACKed! - resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) - - throw err - } -} - -async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { - const options = { - arguments: [ req, res ], - errorMessage: 'Cannot remove the video channel with many retries.' - } - - await retryTransactionWrapper(removeVideoChannel, options) - - return res.type('json').status(204).end() -} - -async function removeVideoChannel (req: express.Request, res: express.Response) { - const videoChannelInstance: VideoChannelModel = res.locals.videoChannel - - return sequelizeTypescript.transaction(async t => { - await videoChannelInstance.destroy({ transaction: t }) - - logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) - }) - -} - -async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { - const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) - - return res.json(videoChannelWithVideos.toFormattedJSON()) -} - -async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { - const videoChannelInstance: VideoChannelModel = res.locals.videoChannel - - const resultList = await VideoModel.listForApi({ - start: req.query.start, - count: req.query.count, - sort: req.query.sort, - hideNSFW: isNSFWHidden(res), - withFiles: false, - videoChannelId: videoChannelInstance.id - }) - - 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/video-channel.ts b/server/controllers/api/video-channel.ts index d57273747..6241aaa5c 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -1,13 +1,30 @@ import * as express from 'express' -import { getFormattedObjects } from '../../helpers/utils' +import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils' import { asyncMiddleware, + authenticate, + optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort, - videoChannelsSortValidator + videoChannelsAddValidator, + videoChannelsGetValidator, + videoChannelsRemoveValidator, + videoChannelsSortValidator, + videoChannelsUpdateValidator } from '../../middlewares' import { VideoChannelModel } from '../../models/video/video-channel' +import { videosSortValidator } from '../../middlewares/validators' +import { sendUpdateActor } from '../../lib/activitypub/send' +import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' +import { createVideoChannel } from '../../lib/video-channel' +import { isNSFWHidden } from '../../helpers/express-utils' +import { setAsyncActorKeys } from '../../lib/activitypub' +import { retryTransactionWrapper } from '../../helpers/database-utils' +import { AccountModel } from '../../models/account/account' +import { sequelizeTypescript } from '../../initializers' +import { logger } from '../../helpers/logger' +import { VideoModel } from '../../models/video/video' const videoChannelRouter = express.Router() @@ -19,6 +36,39 @@ videoChannelRouter.get('/', asyncMiddleware(listVideoChannels) ) +videoChannelRouter.post('/', + authenticate, + videoChannelsAddValidator, + asyncMiddleware(addVideoChannelRetryWrapper) +) + +videoChannelRouter.put('/:id', + authenticate, + asyncMiddleware(videoChannelsUpdateValidator), + updateVideoChannelRetryWrapper +) + +videoChannelRouter.delete('/:id', + authenticate, + asyncMiddleware(videoChannelsRemoveValidator), + asyncMiddleware(removeVideoChannelRetryWrapper) +) + +videoChannelRouter.get('/:id', + asyncMiddleware(videoChannelsGetValidator), + asyncMiddleware(getVideoChannel) +) + +videoChannelRouter.get('/:id/videos', + asyncMiddleware(videoChannelsGetValidator), + paginationValidator, + videosSortValidator, + setDefaultSort, + setDefaultPagination, + optionalAuthenticate, + asyncMiddleware(listVideoChannelVideos) +) + // --------------------------------------------------------------------------- export { @@ -32,3 +82,122 @@ async function listVideoChannels (req: express.Request, res: express.Response, n return res.json(getFormattedObjects(resultList.data, resultList.total)) } + +// Wrapper to video channel add that retry the async function if there is a database error +// We need this because we run the transaction in SERIALIZABLE isolation that can fail +async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot insert the video video channel with many retries.' + } + + const videoChannel = await retryTransactionWrapper(addVideoChannel, options) + return res.json({ + videoChannel: { + id: videoChannel.id, + uuid: videoChannel.Actor.uuid + } + }).end() +} + +async function addVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInfo: VideoChannelCreate = req.body + const account: AccountModel = res.locals.oauth.token.User.Account + + const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { + return createVideoChannel(videoChannelInfo, account, t) + }) + + setAsyncActorKeys(videoChannelCreated.Actor) + .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) + + logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) + + return videoChannelCreated +} + +async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot update the video with many retries.' + } + + await retryTransactionWrapper(updateVideoChannel, options) + + return res.type('json').status(204).end() +} + +async function updateVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInstance = res.locals.videoChannel as VideoChannelModel + const videoChannelFieldsSave = videoChannelInstance.toJSON() + const videoChannelInfoToUpdate = req.body as VideoChannelUpdate + + try { + await sequelizeTypescript.transaction(async t => { + const sequelizeOptions = { + transaction: t + } + + if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) + if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) + if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) + + const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) + await sendUpdateActor(videoChannelInstanceUpdated, t) + }) + + logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + } catch (err) { + logger.debug('Cannot update the video channel.', { err }) + + // Force fields we want to update + // If the transaction is retried, sequelize will think the object has not changed + // So it will skip the SQL request, even if the last one was ROLLBACKed! + resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave) + + throw err + } +} + +async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { + const options = { + arguments: [ req, res ], + errorMessage: 'Cannot remove the video channel with many retries.' + } + + await retryTransactionWrapper(removeVideoChannel, options) + + return res.type('json').status(204).end() +} + +async function removeVideoChannel (req: express.Request, res: express.Response) { + const videoChannelInstance: VideoChannelModel = res.locals.videoChannel + + return sequelizeTypescript.transaction(async t => { + await videoChannelInstance.destroy({ transaction: t }) + + logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + }) + +} + +async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { + const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) + + return res.json(videoChannelWithVideos.toFormattedJSON()) +} + +async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { + const videoChannelInstance: VideoChannelModel = res.locals.videoChannel + + const resultList = await VideoModel.listForApi({ + start: req.query.start, + count: req.query.count, + sort: req.query.sort, + hideNSFW: isNSFWHidden(res), + withFiles: false, + videoChannelId: videoChannelInstance.id + }) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index 9e6f459cf..a70f196df 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts @@ -27,7 +27,6 @@ const listVideoAccountChannelsValidator = [ ] const videoChannelsAddValidator = [ - param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'), body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), @@ -43,7 +42,6 @@ const videoChannelsAddValidator = [ const videoChannelsUpdateValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'), body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), @@ -52,9 +50,7 @@ const videoChannelsUpdateValidator = [ logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 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) { @@ -75,17 +71,13 @@ const videoChannelsUpdateValidator = [ const videoChannelsRemoveValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 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 - // 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 @@ -95,19 +87,14 @@ const videoChannelsRemoveValidator = [ const videoChannelsGetValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - param('accountId').optional().custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { 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() } ] @@ -160,15 +147,3 @@ 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/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts index 25b2dc9b9..7cda879ed 100644 --- a/server/tests/api/check-params/video-channels.ts +++ b/server/tests/api/check-params/video-channels.ts @@ -27,10 +27,8 @@ const expect = chai.expect describe('Test videos API validator', function () { const videoChannelPath = '/api/v1/video-channels' - const accountPath = '/api/v1/accounts/' let server: ServerInfo let accessTokenUser: string - let accountUUID: string let videoChannelUUID: string // --------------------------------------------------------------- @@ -57,7 +55,6 @@ describe('Test videos API validator', function () { { const res = await getMyUserInformation(server.url, server.accessToken) const user: User = res.body - accountUUID = user.account.uuid videoChannelUUID = user.videoChannels[0].uuid } }) @@ -92,45 +89,46 @@ describe('Test videos API validator', function () { description: 'super description', support: 'super support text' } - let path: string - - before(async function () { - path = accountPath + accountUUID + '/video-channels' - }) it('Should fail with a non authenticated user', async function () { - await makePostBodyRequest({ url: server.url, path, token: 'none', fields: baseCorrectParams, statusCodeExpected: 401 }) + await makePostBodyRequest({ + url: server.url, + path: videoChannelPath, + token: 'none', + fields: baseCorrectParams, + statusCodeExpected: 401 + }) }) it('Should fail with nothing', async function () { const fields = {} - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) it('Should fail without name', async function () { const fields = omit(baseCorrectParams, 'name') - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) it('Should fail with a long name', async function () { const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) }) - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) it('Should fail with a long description', async function () { const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(60) }) - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) it('Should fail with a long support text', async function () { const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) }) - await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) }) it('Should succeed with the correct parameters', async function () { await makePostBodyRequest({ url: server.url, - path, + path: videoChannelPath, token: server.accessToken, fields: baseCorrectParams, statusCodeExpected: 200 @@ -146,7 +144,7 @@ describe('Test videos API validator', function () { let path: string before(async function () { - path = accountPath + accountUUID + '/video-channels/' + videoChannelUUID + path = videoChannelPath + '/' + videoChannelUUID }) it('Should fail with a non authenticated user', async function () { @@ -196,16 +194,10 @@ describe('Test videos API validator', function () { }) describe('When getting a video channel', function () { - let basePath: string - - before(async function () { - basePath = accountPath + accountUUID + '/video-channels' - }) - it('Should return the list of the video channels with nothing', async function () { const res = await makeGetRequest({ url: server.url, - path: basePath, + path: videoChannelPath, statusCodeExpected: 200 }) @@ -215,7 +207,7 @@ describe('Test videos API validator', function () { it('Should fail without a correct uuid', async function () { await makeGetRequest({ url: server.url, - path: basePath + '/coucou', + path: videoChannelPath + '/coucou', statusCodeExpected: 400 }) }) @@ -223,7 +215,7 @@ describe('Test videos API validator', function () { it('Should return 404 with an incorrect video channel', async function () { await makeGetRequest({ url: server.url, - path: basePath + '/4da6fde3-88f7-4d16-b119-108df5630b06', + path: videoChannelPath + '/4da6fde3-88f7-4d16-b119-108df5630b06', statusCodeExpected: 404 }) }) @@ -231,7 +223,7 @@ describe('Test videos API validator', function () { it('Should succeed with the correct parameters', async function () { await makeGetRequest({ url: server.url, - path: basePath + '/' + videoChannelUUID, + path: videoChannelPath + '/' + videoChannelUUID, statusCodeExpected: 200 }) }) @@ -239,30 +231,26 @@ describe('Test videos API validator', function () { describe('When deleting a video channel', function () { it('Should fail with a non authenticated user', async function () { - await deleteVideoChannel(server.url, 'coucou', accountUUID, videoChannelUUID, 401) + await deleteVideoChannel(server.url, 'coucou', videoChannelUUID, 401) }) it('Should fail with another authenticated user', async function () { - 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,videoChannelUUID, 404) + await deleteVideoChannel(server.url, accessTokenUser, videoChannelUUID, 403) }) it('Should fail with an unknown video channel id', async function () { - await deleteVideoChannel(server.url, server.accessToken, accountUUID,454554, 404) + await deleteVideoChannel(server.url, server.accessToken,454554, 404) }) it('Should succeed with the correct parameters', async function () { - await deleteVideoChannel(server.url, server.accessToken, accountUUID, videoChannelUUID) + await deleteVideoChannel(server.url, server.accessToken, videoChannelUUID) }) it('Should fail to delete the last user video channel', async function () { const res = await getVideoChannelsList(server.url, 0, 1) const lastVideoChannelUUID = res.body.data[0].uuid - await deleteVideoChannel(server.url, server.accessToken, accountUUID, lastVideoChannelUUID, 409) + await deleteVideoChannel(server.url, server.accessToken, lastVideoChannelUUID, 409) }) }) diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index 04e7b8c6a..d24b8ab0b 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts @@ -63,7 +63,7 @@ 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, accountUUID, videoChannel) + const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) videoChannelId = res.body.videoChannel.id videoChannelUUID = res.body.videoChannel.uuid @@ -130,7 +130,7 @@ describe('Test video channels', function () { support: 'video channel support text updated' } - await updateVideoChannel(servers[0].url, servers[0].accessToken, accountUUID, videoChannelId, videoChannelAttributes) + await updateVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId, videoChannelAttributes) await wait(3000) }) @@ -149,7 +149,7 @@ describe('Test video channels', function () { }) it('Should get video channel', async function () { - const res = await getVideoChannel(servers[0].url, accountUUID, videoChannelId) + const res = await getVideoChannel(servers[0].url, videoChannelId) const videoChannel = res.body expect(videoChannel.displayName).to.equal('video channel updated') @@ -161,7 +161,7 @@ describe('Test video channels', function () { this.timeout(10000) for (const server of servers) { - const res = await getVideoChannelVideos(server.url, server.accessToken, accountUUID, videoChannelUUID, 0, 5) + const res = await getVideoChannelVideos(server.url, server.accessToken, 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) @@ -170,7 +170,7 @@ describe('Test video channels', function () { }) it('Should delete video channel', async function () { - await deleteVideoChannel(servers[0].url, servers[0].accessToken, accountUUID, videoChannelId) + await deleteVideoChannel(servers[0].url, servers[0].accessToken, 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 8901f38f9..b8c85f45b 100644 --- a/server/tests/api/videos/video-nsfw.ts +++ b/server/tests/api/videos/video-nsfw.ts @@ -39,7 +39,7 @@ describe('Test video NSFW policy', function () { getVideosListWithToken(server.url, token), searchVideoWithToken(server.url, 'n', token), getAccountVideos(server.url, token, accountUUID, 0, 5), - getVideoChannelVideos(server.url, token, accountUUID, videoChannelUUID, 0, 5) + getVideoChannelVideos(server.url, token, videoChannelUUID, 0, 5) ]) } @@ -47,7 +47,7 @@ describe('Test video NSFW policy', function () { getVideosList(server.url), searchVideo(server.url, 'n'), getAccountVideos(server.url, undefined, accountUUID, 0, 5), - getVideoChannelVideos(server.url, undefined, accountUUID, videoChannelUUID, 0, 5) + getVideoChannelVideos(server.url, undefined, videoChannelUUID, 0, 5) ]) }) } diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts index 0b4fa89b7..978e21b19 100644 --- a/server/tests/utils/videos/video-channels.ts +++ b/server/tests/utils/videos/video-channels.ts @@ -34,11 +34,10 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s function addVideoChannel ( url: string, token: string, - accountId: number | string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 200 ) { - const path = '/api/v1/accounts/' + accountId + '/video-channels/' + const path = '/api/v1/video-channels/' // Default attributes let attributes = { @@ -59,13 +58,12 @@ function addVideoChannel ( function updateVideoChannel ( url: string, token: string, - accountId: number | string, channelId: number | string, attributes: VideoChannelAttributes, expectedStatus = 204 ) { const body = {} - const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId + const path = '/api/v1/video-channels/' + channelId if (attributes.name) body['name'] = attributes.name if (attributes.description) body['description'] = attributes.description @@ -79,8 +77,8 @@ function updateVideoChannel ( .expect(expectedStatus) } -function deleteVideoChannel (url: string, token: string, accountId: number | string, channelId: number | string, expectedStatus = 204) { - const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId +function deleteVideoChannel (url: string, token: string, channelId: number | string, expectedStatus = 204) { + const path = '/api/v1/video-channels/' + channelId return request(url) .delete(path) @@ -89,8 +87,8 @@ function deleteVideoChannel (url: string, token: string, accountId: number | str .expect(expectedStatus) } -function getVideoChannel (url: string, accountId: number | string, channelId: number | string) { - const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId +function getVideoChannel (url: string, channelId: number | string) { + const path = '/api/v1/video-channels/' + channelId return request(url) .get(path) diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts index d1d8c07df..870dfd21f 100644 --- a/server/tests/utils/videos/videos.ts +++ b/server/tests/utils/videos/videos.ts @@ -186,13 +186,12 @@ function getAccountVideos (url: string, accessToken: string, accountId: number | 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' + const path = '/api/v1/video-channels/' + videoChannelId + '/videos' return makeGetRequest({ url, diff --git a/support/doc/api/html/index.html b/support/doc/api/html/index.html index 23162c307..d41fc3913 100644 --- a/support/doc/api/html/index.html +++ b/support/doc/api/html/index.html @@ -27,6 +27,9 @@
  • GET /accounts/{id}
  • +
  • + GET /accounts/{id}/videos +
  • GET /accounts
  • @@ -187,19 +190,22 @@ GET /video-channels
  • - GET /accounts/{accountId}/video-channels + POST /video-channels +
  • +
  • + GET /video-channels/{id}
  • - POST /accounts/{accountId}/video-channels + PUT /video-channels/{id}
  • - GET /account/{accountId}/video-channels/{id} + DELETE /video-channels/{id}
  • - PUT /account/{accountId}/video-channels/{id} + GET /video-channels/{id}/videos
  • - DELETE /account/{accountId}/video-channels/{id} + GET /accounts/{accountId}/video-channels
  • @@ -446,6 +452,117 @@ "updatedAt": "string" } } + + + + + + +
    + + +
    + Accounts + +
    + + +

    + + GET + /accounts/{id}/videos + +

    +
    +
    +
    +
    +
    +
    id
    + +
    in path
    +
    + string + +
    +
    +
    +

    The id of the account

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    200 OK
    +
    + + Video + +
    + +
    +
    +

    successful operation

    +
    +
    +
    +
    +
    +
    Response Content-Types: + application/json +
    +
    +
    Response Example + (200 OK) +
    +
    {
    +  "id": "number",
    +  "uuid": "string",
    +  "createdAt": "string",
    +  "publishedAt": "string",
    +  "updatedAt": "string",
    +  "category": {
    +    "id": "number",
    +    "label": "string"
    +  },
    +  "licence": {
    +    "id": "number",
    +    "label": "string"
    +  },
    +  "language": {
    +    "id": "string",
    +    "label": "string"
    +  },
    +  "privacy": "string",
    +  "description": "string",
    +  "duration": "number",
    +  "isLocal": "boolean",
    +  "name": "string",
    +  "thumbnailPath": "string",
    +  "previewPath": "string",
    +  "embedPath": "string",
    +  "views": "number",
    +  "likes": "number",
    +  "dislikes": "number",
    +  "nsfw": "boolean",
    +  "account": {
    +    "name": "string",
    +    "displayName": "string",
    +    "url": "string",
    +    "host": "string",
    +    "avatar": {
    +      "path": "string",
    +      "createdAt": "string",
    +      "updatedAt": "string"
    +    }
    +  }
    +}
     
    @@ -1596,54 +1713,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } ] } @@ -1848,54 +1921,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } ] } @@ -2125,54 +2154,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } ] } @@ -4867,54 +4852,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } ] @@ -4923,7 +4864,7 @@
    -
    +
    @@ -4934,201 +4875,50 @@

    - GET - /accounts/{accountId}/video-channels + POST + /video-channels

    -
    -
    +
    +
    -
    accountId
    - -
    in path
    -
    - string - +
    -
    -

    The account id

    +
    + +

    undefined

    +
    -
    +
    +
    +
    Request Content-Types: + application/json +
    +
    Request Example
    +
    {
    +  "name": "string",
    +  "description": "string"
    +}
    +
    + +
    +
    -
    200 OK
    -
    - - VideoChannel - -
    - -
    -
    -

    successful operation

    -
    -
    -
    -
    type
    -
    - - - VideoChannel - - - -
    -
    -
    -
    -
    -
    Response Content-Types: - application/json -
    -
    -
    Response Example - (200 OK) -
    -
    [
    -  {
    -    "displayName": "string",
    -    "description": "string",
    -    "isLocal": "boolean",
    -    "owner": {
    -      "name": "string",
    -      "uuid": "string"
    -    },
    -    "videos": [
    -      {
    -        "id": "number",
    -        "uuid": "string",
    -        "createdAt": "string",
    -        "publishedAt": "string",
    -        "updatedAt": "string",
    -        "category": {
    -          "id": "number",
    -          "label": "string"
    -        },
    -        "licence": {
    -          "id": "number",
    -          "label": "string"
    -        },
    -        "language": {
    -          "id": "string",
    -          "label": "string"
    -        },
    -        "privacy": "string",
    -        "description": "string",
    -        "duration": "number",
    -        "isLocal": "boolean",
    -        "name": "string",
    -        "thumbnailPath": "string",
    -        "previewPath": "string",
    -        "embedPath": "string",
    -        "views": "number",
    -        "likes": "number",
    -        "dislikes": "number",
    -        "nsfw": "boolean",
    -        "account": {
    -          "name": "string",
    -          "displayName": "string",
    -          "url": "string",
    -          "host": "string",
    -          "avatar": {
    -            "path": "string",
    -            "createdAt": "string",
    -            "updatedAt": "string"
    -          }
    -        }
    -      }
    -    ]
    -  }
    -]
    -
    - -
    -
    -
    -
    -
    - - -
    - VideoChannel - -
    - - -

    - - POST - /accounts/{accountId}/video-channels - -

    -
    -
    -
    -
    -
    - -
    -
    - -

    undefined

    - -
    -
    -
    -
    -
    -
    -
    accountId
    - -
    in path
    -
    - string - -
    -
    -
    -

    The account id

    -
    -
    -
    -
    -
    -
    -
    Request Content-Types: - application/json -
    -
    Request Example
    -
    {
    -  "name": "string",
    -  "description": "string"
    -}
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    204 No Content
    +
    204 No Content

    successful operation

    @@ -5165,7 +4955,7 @@
    -
    +
    @@ -5177,26 +4967,12 @@

    GET - /account/{accountId}/video-channels/{id} + /video-channels/{id}

    -
    -
    -
    accountId
    - -
    in path
    -
    - string - -
    -
    -
    -

    The account id

    -
    -
    id
    @@ -5246,54 +5022,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } @@ -5301,7 +5033,7 @@
    -
    +
    @@ -5313,7 +5045,7 @@

    PUT - /account/{accountId}/video-channels/{id} + /video-channels/{id}

    @@ -5335,20 +5067,6 @@
    -
    -
    -
    accountId
    - -
    in path
    -
    - string - -
    -
    -
    -

    The account id

    -
    -
    id
    @@ -5422,7 +5140,7 @@
    -
    +
    @@ -5434,26 +5152,12 @@

    DELETE - /account/{accountId}/video-channels/{id} + /video-channels/{id}

    -
    -
    -
    accountId
    - -
    in path
    -
    - string - -
    -
    -
    -

    The account id

    -
    -
    id
    @@ -5514,6 +5218,208 @@
    +
    + + +
    + VideoChannel + +
    + + +

    + + GET + /video-channels/{id}/videos + +

    +
    +
    +
    +
    +
    +
    id
    + +
    in path
    +
    + string + +
    +
    +
    +

    The video channel id

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    200 OK
    +
    + + Video + +
    + +
    +
    +

    successful operation

    +
    +
    +
    +
    +
    +
    Response Content-Types: + application/json +
    +
    +
    Response Example + (200 OK) +
    +
    {
    +  "id": "number",
    +  "uuid": "string",
    +  "createdAt": "string",
    +  "publishedAt": "string",
    +  "updatedAt": "string",
    +  "category": {
    +    "id": "number",
    +    "label": "string"
    +  },
    +  "licence": {
    +    "id": "number",
    +    "label": "string"
    +  },
    +  "language": {
    +    "id": "string",
    +    "label": "string"
    +  },
    +  "privacy": "string",
    +  "description": "string",
    +  "duration": "number",
    +  "isLocal": "boolean",
    +  "name": "string",
    +  "thumbnailPath": "string",
    +  "previewPath": "string",
    +  "embedPath": "string",
    +  "views": "number",
    +  "likes": "number",
    +  "dislikes": "number",
    +  "nsfw": "boolean",
    +  "account": {
    +    "name": "string",
    +    "displayName": "string",
    +    "url": "string",
    +    "host": "string",
    +    "avatar": {
    +      "path": "string",
    +      "createdAt": "string",
    +      "updatedAt": "string"
    +    }
    +  }
    +}
    +
    + +
    +
    +
    +
    +
    + + +
    + VideoChannel + +
    + + +

    + + GET + /accounts/{accountId}/video-channels + +

    +
    +
    +
    +
    +
    +
    accountId
    + +
    in path
    +
    + string + +
    +
    +
    +

    The account id

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    200 OK
    +
    + + VideoChannel + +
    + +
    +
    +

    successful operation

    +
    +
    +
    +
    type
    +
    + + + VideoChannel + + + +
    +
    +
    +
    +
    +
    Response Content-Types: + application/json +
    +
    +
    Response Example + (200 OK) +
    +
    [
    +  {
    +    "displayName": "string",
    +    "description": "string",
    +    "isLocal": "boolean",
    +    "ownerAccount": {
    +      "id": "number",
    +      "uuid": "string"
    +    }
    +  }
    +]
    +
    + +
    +
    +
    +

    VideoComment

    @@ -6760,20 +6666,11 @@ boolean -
    - owner: +
    + ownerAccount: object
    -
    - videos: - - - Video - - - -
    @@ -6784,54 +6681,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } @@ -7394,54 +7247,10 @@ "displayName": "string", "description": "string", "isLocal": "boolean", - "owner": { - "name": "string", + "ownerAccount": { + "id": "number", "uuid": "string" - }, - "videos": [ - { - "id": "number", - "uuid": "string", - "createdAt": "string", - "publishedAt": "string", - "updatedAt": "string", - "category": { - "id": "number", - "label": "string" - }, - "licence": { - "id": "number", - "label": "string" - }, - "language": { - "id": "string", - "label": "string" - }, - "privacy": "string", - "description": "string", - "duration": "number", - "isLocal": "boolean", - "name": "string", - "thumbnailPath": "string", - "previewPath": "string", - "embedPath": "string", - "views": "number", - "likes": "number", - "dislikes": "number", - "nsfw": "boolean", - "account": { - "name": "string", - "displayName": "string", - "url": "string", - "host": "string", - "avatar": { - "path": "string", - "createdAt": "string", - "updatedAt": "string" - } - } - } - ] + } } ] } diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 56941031b..46b73145a 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -1017,27 +1017,6 @@ paths: type: array items: $ref: '#/definitions/VideoChannel' - /accounts/{accountId}/video-channels: - get: - tags: - - VideoChannel - consumes: - - application/json - produces: - - application/json - parameters: - - name: accountId - in: path - required: true - type: string - description: 'The account id ' - responses: - '200': - description: successful operation - schema: - type: array - items: - $ref: '#/definitions/VideoChannel' post: security: - OAuth2: [ ] @@ -1048,11 +1027,6 @@ paths: produces: - application/json parameters: - - name: accountId - in: path - required: true - type: string - description: 'The account id ' - in: body name: body schema: @@ -1060,7 +1034,7 @@ paths: responses: '204': description: successful operation - "/account/{accountId}/video-channels/{id}": + "/video-channels/{id}": get: tags: - VideoChannel @@ -1069,11 +1043,6 @@ paths: produces: - application/json parameters: - - name: accountId - in: path - required: true - type: string - description: 'The account id ' - name: id in: path required: true @@ -1094,11 +1063,6 @@ paths: produces: - application/json parameters: - - name: accountId - in: path - required: true - type: string - description: 'The account id ' - name: id in: path required: true @@ -1121,20 +1085,34 @@ paths: produces: - application/json parameters: - - name: accountId + - name: id in: path required: true type: string - description: 'The account id ' + description: 'The video channel id ' + responses: + '204': + description: successful operation + "/video-channels/{id}/videos": + get: + tags: + - VideoChannel + consumes: + - application/json + produces: + - application/json + parameters: - name: id in: path required: true type: string description: 'The video channel id ' responses: - '204': + '200': description: successful operation - "/account/{accountId}/video-channels/{id}/videos": + schema: + $ref: '#/definitions/Video' + /accounts/{accountId}/video-channels: get: tags: - VideoChannel @@ -1148,16 +1126,13 @@ paths: 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' + type: array + items: + $ref: '#/definitions/VideoChannel' "/videos/{videoId}/comment-threads": get: tags: