aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server.ts3
-rw-r--r--server/controllers/activitypub/client.ts2
-rw-r--r--server/controllers/api/videos/comment.ts10
-rw-r--r--server/controllers/api/videos/index.ts10
-rw-r--r--server/lib/plugins/hooks.ts19
-rw-r--r--server/lib/plugins/plugin-manager.ts10
-rw-r--r--server/lib/video-blacklist.ts5
-rw-r--r--server/middlewares/validators/videos/videos.ts5
-rw-r--r--server/models/video/video.ts7
-rw-r--r--server/tests/cli/plugins.ts8
-rw-r--r--server/tests/fixtures/peertube-plugin-test-two/main.js21
-rw-r--r--server/tests/fixtures/peertube-plugin-test-two/package.json19
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js62
-rw-r--r--server/tests/plugins/action-hooks.ts91
-rw-r--r--server/tests/plugins/filter-hooks.ts69
-rw-r--r--shared/core-utils/plugins/hooks.ts35
-rw-r--r--shared/extra-utils/server/plugins.ts7
-rw-r--r--shared/extra-utils/server/servers.ts3
18 files changed, 306 insertions, 80 deletions
diff --git a/server.ts b/server.ts
index b75c78b07..abfeeed2e 100644
--- a/server.ts
+++ b/server.ts
@@ -114,6 +114,7 @@ import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
114import { PeerTubeSocket } from './server/lib/peertube-socket' 114import { PeerTubeSocket } from './server/lib/peertube-socket'
115import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls' 115import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls'
116import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler' 116import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler'
117import { Hooks } from './server/lib/plugins/hooks'
117 118
118// ----------- Command line ----------- 119// ----------- Command line -----------
119 120
@@ -269,7 +270,7 @@ async function startApplication () {
269 logger.info('Server listening on %s:%d', hostname, port) 270 logger.info('Server listening on %s:%d', hostname, port)
270 logger.info('Web server: %s', WEBSERVER.URL) 271 logger.info('Web server: %s', WEBSERVER.URL)
271 272
272 PluginManager.Instance.runHook('action:application.listening') 273 Hooks.runAction('action:application.listening')
273 }) 274 })
274 275
275 process.on('exit', () => { 276 process.on('exit', () => {
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index d36d10de1..11504b354 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -208,7 +208,7 @@ function getAccountVideoRate (rateType: VideoRateType) {
208 208
209async function videoController (req: express.Request, res: express.Response) { 209async function videoController (req: express.Request, res: express.Response) {
210 // We need more attributes 210 // We need more attributes
211 const video = await VideoModel.loadForGetAPI(res.locals.video.id) 211 const video = await VideoModel.loadForGetAPI({ id: res.locals.video.id })
212 212
213 if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url) 213 if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)
214 214
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index a95392543..feda71bdd 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -85,8 +85,9 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
85 user: user 85 user: user
86 }, 'filter:api.video-threads.list.params') 86 }, 'filter:api.video-threads.list.params')
87 87
88 resultList = await Hooks.wrapPromise( 88 resultList = await Hooks.wrapPromiseFun(
89 VideoCommentModel.listThreadsForApi(apiOptions), 89 VideoCommentModel.listThreadsForApi,
90 apiOptions,
90 'filter:api.video-threads.list.result' 91 'filter:api.video-threads.list.result'
91 ) 92 )
92 } else { 93 } else {
@@ -112,8 +113,9 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo
112 user: user 113 user: user
113 }, 'filter:api.video-thread-comments.list.params') 114 }, 'filter:api.video-thread-comments.list.params')
114 115
115 resultList = await Hooks.wrapPromise( 116 resultList = await Hooks.wrapPromiseFun(
116 VideoCommentModel.listThreadCommentsForApi(apiOptions), 117 VideoCommentModel.listThreadCommentsForApi,
118 apiOptions,
117 'filter:api.video-thread-comments.list.result' 119 'filter:api.video-thread-comments.list.result'
118 ) 120 )
119 } else { 121 } else {
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index a3b1dde29..11e468df2 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -436,8 +436,9 @@ async function getVideo (req: express.Request, res: express.Response) {
436 // We need more attributes 436 // We need more attributes
437 const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null 437 const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null
438 438
439 const video = await Hooks.wrapPromise( 439 const video = await Hooks.wrapPromiseFun(
440 VideoModel.loadForGetAPI(res.locals.video.id, undefined, userId), 440 VideoModel.loadForGetAPI,
441 { id: res.locals.video.id, userId },
441 'filter:api.video.get.result' 442 'filter:api.video.get.result'
442 ) 443 )
443 444
@@ -502,8 +503,9 @@ async function listVideos (req: express.Request, res: express.Response) {
502 user: res.locals.oauth ? res.locals.oauth.token.User : undefined 503 user: res.locals.oauth ? res.locals.oauth.token.User : undefined
503 }, 'filter:api.videos.list.params') 504 }, 'filter:api.videos.list.params')
504 505
505 const resultList = await Hooks.wrapPromise( 506 const resultList = await Hooks.wrapPromiseFun(
506 VideoModel.listForApi(apiOptions), 507 VideoModel.listForApi,
508 apiOptions,
507 'filter:api.videos.list.result' 509 'filter:api.videos.list.result'
508 ) 510 )
509 511
diff --git a/server/lib/plugins/hooks.ts b/server/lib/plugins/hooks.ts
index 7bb907e6a..b694d4118 100644
--- a/server/lib/plugins/hooks.ts
+++ b/server/lib/plugins/hooks.ts
@@ -3,16 +3,25 @@ import { PluginManager } from './plugin-manager'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import * as Bluebird from 'bluebird' 4import * as Bluebird from 'bluebird'
5 5
6type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T>
7type RawFunction <U, T> = (params: U) => T
8
6// Helpers to run hooks 9// Helpers to run hooks
7const Hooks = { 10const Hooks = {
8 wrapObject: <T, U extends ServerFilterHookName>(obj: T, hookName: U) => { 11 wrapObject: <T, U extends ServerFilterHookName>(result: T, hookName: U) => {
9 return PluginManager.Instance.runHook(hookName, obj) as Promise<T> 12 return PluginManager.Instance.runHook(hookName, result) as Promise<T>
13 },
14
15 wrapPromiseFun: async <U, T, V extends ServerFilterHookName>(fun: PromiseFunction<U, T>, params: U, hookName: V) => {
16 const result = await fun(params)
17
18 return PluginManager.Instance.runHook(hookName, result, params)
10 }, 19 },
11 20
12 wrapPromise: async <T, U extends ServerFilterHookName>(fun: Promise<T> | Bluebird<T>, hookName: U) => { 21 wrapFun: async <U, T, V extends ServerFilterHookName>(fun: RawFunction<U, T>, params: U, hookName: V) => {
13 const result = await fun 22 const result = fun(params)
14 23
15 return PluginManager.Instance.runHook(hookName, result) 24 return PluginManager.Instance.runHook(hookName, result, params)
16 }, 25 },
17 26
18 runAction: <T, U extends ServerActionHookName>(hookName: U, params?: T) => { 27 runAction: <T, U extends ServerActionHookName>(hookName: U, params?: T) => {
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 9afda97ea..6485a47c5 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -98,15 +98,15 @@ export class PluginManager implements ServerHook {
98 98
99 // ###################### Hooks ###################### 99 // ###################### Hooks ######################
100 100
101 async runHook (hookName: ServerHookName, param?: any) { 101 async runHook <T> (hookName: ServerHookName, result?: T, params?: any): Promise<T> {
102 let result = param 102 if (!this.hooks[hookName]) return Promise.resolve(result)
103
104 if (!this.hooks[hookName]) return result
105 103
106 const hookType = getHookType(hookName) 104 const hookType = getHookType(hookName)
107 105
108 for (const hook of this.hooks[hookName]) { 106 for (const hook of this.hooks[hookName]) {
109 result = await internalRunHook(hook.handler, hookType, param, err => { 107 logger.debug('Running hook %s of plugin %s.', hookName, hook.npmName)
108
109 result = await internalRunHook(hook.handler, hookType, result, params, err => {
110 logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err }) 110 logger.error('Cannot run hook %s of plugin %s.', hookName, hook.pluginName, { err })
111 }) 111 })
112 } 112 }
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts
index 32b1a28fa..9bc996f5a 100644
--- a/server/lib/video-blacklist.ts
+++ b/server/lib/video-blacklist.ts
@@ -9,8 +9,9 @@ import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
9import { Hooks } from './plugins/hooks' 9import { Hooks } from './plugins/hooks'
10 10
11async function autoBlacklistVideoIfNeeded (video: VideoModel, user?: UserModel, transaction?: Transaction) { 11async function autoBlacklistVideoIfNeeded (video: VideoModel, user?: UserModel, transaction?: Transaction) {
12 const doAutoBlacklist = await Hooks.wrapPromise( 12 const doAutoBlacklist = await Hooks.wrapPromiseFun(
13 autoBlacklistNeeded({ video, user }), 13 autoBlacklistNeeded,
14 { video, user },
14 'filter:video.auto-blacklist.result' 15 'filter:video.auto-blacklist.result'
15 ) 16 )
16 17
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index cb2c071ba..5593ede64 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -444,8 +444,9 @@ async function isVideoAccepted (req: express.Request, res: express.Response, vid
444 videoFile, 444 videoFile,
445 user: res.locals.oauth.token.User 445 user: res.locals.oauth.token.User
446 } 446 }
447 const acceptedResult = await Hooks.wrapObject( 447 const acceptedResult = await Hooks.wrapFun(
448 isLocalVideoAccepted(acceptParameters), 448 isLocalVideoAccepted,
449 acceptParameters,
449 'filter:api.video.upload.accept.result' 450 'filter:api.video.upload.accept.result'
450 ) 451 )
451 452
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index ec3d5ddb0..443aec9c2 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1472,7 +1472,12 @@ export class VideoModel extends Model<VideoModel> {
1472 .findOne(options) 1472 .findOne(options)
1473 } 1473 }
1474 1474
1475 static loadForGetAPI (id: number | string, t?: Transaction, userId?: number) { 1475 static loadForGetAPI (parameters: {
1476 id: number | string,
1477 t?: Transaction,
1478 userId?: number
1479 }) {
1480 const { id, t, userId } = parameters
1476 const where = buildWhereIdOrUUID(id) 1481 const where = buildWhereIdOrUUID(id)
1477 1482
1478 const options = { 1483 const options = {
diff --git a/server/tests/cli/plugins.ts b/server/tests/cli/plugins.ts
index d7bf8a690..a5257d671 100644
--- a/server/tests/cli/plugins.ts
+++ b/server/tests/cli/plugins.ts
@@ -6,13 +6,13 @@ import {
6 execCLI, 6 execCLI,
7 flushAndRunServer, 7 flushAndRunServer,
8 getConfig, 8 getConfig,
9 getEnvCli, killallServers, 9 getEnvCli,
10 getPluginTestPath,
11 killallServers,
10 reRunServer, 12 reRunServer,
11 root,
12 ServerInfo, 13 ServerInfo,
13 setAccessTokensToServers 14 setAccessTokensToServers
14} from '../../../shared/extra-utils' 15} from '../../../shared/extra-utils'
15import { join } from 'path'
16import { ServerConfig } from '../../../shared/models/server' 16import { ServerConfig } from '../../../shared/models/server'
17import { expect } from 'chai' 17import { expect } from 'chai'
18 18
@@ -29,7 +29,7 @@ describe('Test plugin scripts', function () {
29 it('Should install a plugin from stateless CLI', async function () { 29 it('Should install a plugin from stateless CLI', async function () {
30 this.timeout(60000) 30 this.timeout(60000)
31 31
32 const packagePath = join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test') 32 const packagePath = getPluginTestPath()
33 33
34 const env = getEnvCli(server) 34 const env = getEnvCli(server)
35 await execCLI(`${env} npm run plugin:install -- --plugin-path ${packagePath}`) 35 await execCLI(`${env} npm run plugin:install -- --plugin-path ${packagePath}`)
diff --git a/server/tests/fixtures/peertube-plugin-test-two/main.js b/server/tests/fixtures/peertube-plugin-test-two/main.js
new file mode 100644
index 000000000..71c11b2ba
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-two/main.js
@@ -0,0 +1,21 @@
1async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) {
2 registerHook({
3 target: 'filter:api.videos.list.params',
4 handler: obj => addToCount(obj)
5 })
6}
7
8async function unregister () {
9 return
10}
11
12module.exports = {
13 register,
14 unregister
15}
16
17// ############################################################################
18
19function addToCount (obj) {
20 return Object.assign({}, obj, { count: obj.count + 1 })
21}
diff --git a/server/tests/fixtures/peertube-plugin-test-two/package.json b/server/tests/fixtures/peertube-plugin-test-two/package.json
new file mode 100644
index 000000000..52ebb5ac1
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-two/package.json
@@ -0,0 +1,19 @@
1{
2 "name": "peertube-plugin-test-two",
3 "version": "0.0.1",
4 "description": "Plugin test 2",
5 "engine": {
6 "peertube": ">=1.3.0"
7 },
8 "keywords": [
9 "peertube",
10 "plugin"
11 ],
12 "homepage": "https://github.com/Chocobozzz/PeerTube",
13 "author": "Chocobozzz",
14 "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
15 "library": "./main.js",
16 "staticDirs": {},
17 "css": [],
18 "clientScripts": []
19}
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index fae0ef948..c5317ab41 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -1,23 +1,52 @@
1async function register ({ registerHook, registerSetting, settingsManager, storageManager }) { 1async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) {
2 const defaultAdmin = 'PeerTube admin' 2 const actionHooks = [
3 'action:application.listening',
4
5 'action:api.video.updated',
6 'action:api.video.deleted',
7 'action:api.video.uploaded',
8 'action:api.video.viewed',
9
10 'action:api.video-thread.created',
11 'action:api.video-comment-reply.created',
12 'action:api.video-comment.deleted'
13 ]
14
15 for (const h of actionHooks) {
16 registerHook({
17 target: h,
18 handler: () => peertubeHelpers.logger.debug('Run hook %s.', h)
19 })
20 }
3 21
4 registerHook({ 22 registerHook({
5 target: 'action:application.listening', 23 target: 'filter:api.videos.list.params',
6 handler: () => displayHelloWorld(settingsManager, defaultAdmin) 24 handler: obj => addToCount(obj)
7 }) 25 })
8 26
9 registerSetting({ 27 registerHook({
10 name: 'admin-name', 28 target: 'filter:api.videos.list.result',
11 label: 'Admin name', 29 handler: obj => ({ data: obj.data, total: obj.total + 1 })
12 type: 'input',
13 default: defaultAdmin
14 }) 30 })
15 31
16 const value = await storageManager.getData('toto') 32 registerHook({
17 console.log(value) 33 target: 'filter:api.video.get.result',
18 console.log(value.coucou) 34 handler: video => {
35 video.name += ' <3'
19 36
20 await storageManager.storeData('toto', { coucou: 'hello' + new Date() }) 37 return video
38 }
39 })
40
41 registerHook({
42 target: 'filter:api.video.upload.accept.result',
43 handler: ({ accepted }, { videoBody }) => {
44 if (accepted !== false) return { accepted: true }
45 if (videoBody.name.indexOf('bad word') !== -1) return { accepted: false, errorMessage: 'bad word '}
46
47 return { accepted: true }
48 }
49 })
21} 50}
22 51
23async function unregister () { 52async function unregister () {
@@ -31,9 +60,6 @@ module.exports = {
31 60
32// ############################################################################ 61// ############################################################################
33 62
34async function displayHelloWorld (settingsManager, defaultAdmin) { 63function addToCount (obj) {
35 let value = await settingsManager.getSetting('admin-name') 64 return Object.assign({}, obj, { count: obj.count + 1 })
36 if (!value) value = defaultAdmin
37
38 console.log('hello world ' + value)
39} 65}
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index 93dc57d09..2a941148a 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -2,26 +2,101 @@
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 5import {
6import { setAccessTokensToServers } from '../../../shared/extra-utils' 6 cleanupTests,
7 flushAndRunMultipleServers,
8 flushAndRunServer, killallServers, reRunServer,
9 ServerInfo,
10 waitUntilLog
11} from '../../../shared/extra-utils/server/servers'
12import {
13 addVideoCommentReply,
14 addVideoCommentThread, deleteVideoComment,
15 getPluginTestPath,
16 installPlugin, removeVideo,
17 setAccessTokensToServers,
18 updateVideo,
19 uploadVideo,
20 viewVideo
21} from '../../../shared/extra-utils'
7 22
8const expect = chai.expect 23const expect = chai.expect
9 24
10describe('Test plugin action hooks', function () { 25describe('Test plugin action hooks', function () {
11 let server: ServerInfo 26 let servers: ServerInfo[]
27 let videoUUID: string
28 let threadId: number
29
30 function checkHook (hook: string) {
31 return waitUntilLog(servers[0], 'Run hook ' + hook)
32 }
12 33
13 before(async function () { 34 before(async function () {
14 this.timeout(30000) 35 this.timeout(30000)
15 server = await flushAndRunServer(1)
16 36
17 await setAccessTokensToServers([ server ]) 37 servers = await flushAndRunMultipleServers(2)
38 await setAccessTokensToServers(servers)
39
40 await installPlugin({
41 url: servers[0].url,
42 accessToken: servers[0].accessToken,
43 path: getPluginTestPath()
44 })
45
46 await killallServers([ servers[0] ])
47
48 await reRunServer(servers[0])
18 }) 49 })
19 50
20 it('Should execute ', async function () { 51 it('Should run action:application.listening', async function () {
21 // empty 52 await checkHook('action:application.listening')
53 })
54
55 it('Should run action:api.video.uploaded', async function () {
56 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' })
57 videoUUID = res.body.video.uuid
58
59 await checkHook('action:api.video.uploaded')
60 })
61
62 it('Should run action:api.video.updated', async function () {
63 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video updated' })
64
65 await checkHook('action:api.video.updated')
66 })
67
68 it('Should run action:api.video.viewed', async function () {
69 await viewVideo(servers[0].url, videoUUID)
70
71 await checkHook('action:api.video.viewed')
72 })
73
74 it('Should run action:api.video-thread.created', async function () {
75 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread')
76 threadId = res.body.comment.id
77
78 await checkHook('action:api.video-thread.created')
79 })
80
81 it('Should run action:api.video-comment-reply.created', async function () {
82 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'reply')
83
84 await checkHook('action:api.video-comment-reply.created')
85 })
86
87 it('Should run action:api.video-comment.deleted', async function () {
88 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId)
89
90 await checkHook('action:api.video-comment.deleted')
91 })
92
93 it('Should run action:api.video.deleted', async function () {
94 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID)
95
96 await checkHook('action:api.video.deleted')
22 }) 97 })
23 98
24 after(async function () { 99 after(async function () {
25 await cleanupTests([ server ]) 100 await cleanupTests(servers)
26 }) 101 })
27}) 102})
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 500728712..4fc2c437b 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -2,26 +2,79 @@
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 5import {
6import { setAccessTokensToServers } from '../../../shared/extra-utils' 6 cleanupTests,
7 flushAndRunMultipleServers,
8 flushAndRunServer, killallServers, reRunServer,
9 ServerInfo,
10 waitUntilLog
11} from '../../../shared/extra-utils/server/servers'
12import {
13 addVideoCommentReply,
14 addVideoCommentThread, deleteVideoComment,
15 getPluginTestPath, getVideosList,
16 installPlugin, removeVideo,
17 setAccessTokensToServers,
18 updateVideo,
19 uploadVideo,
20 viewVideo,
21 getVideosListPagination, getVideo
22} from '../../../shared/extra-utils'
7 23
8const expect = chai.expect 24const expect = chai.expect
9 25
10describe('Test plugin filter hooks', function () { 26describe('Test plugin filter hooks', function () {
11 let server: ServerInfo 27 let servers: ServerInfo[]
28 let videoUUID: string
29 let threadId: number
12 30
13 before(async function () { 31 before(async function () {
14 this.timeout(30000) 32 this.timeout(30000)
15 server = await flushAndRunServer(1)
16 33
17 await setAccessTokensToServers([ server ]) 34 servers = await flushAndRunMultipleServers(2)
35 await setAccessTokensToServers(servers)
36
37 await installPlugin({
38 url: servers[0].url,
39 accessToken: servers[0].accessToken,
40 path: getPluginTestPath()
41 })
42
43 await installPlugin({
44 url: servers[0].url,
45 accessToken: servers[0].accessToken,
46 path: getPluginTestPath('-two')
47 })
48
49 for (let i = 0; i < 10; i++) {
50 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'default video ' + i })
51 }
52
53 const res = await getVideosList(servers[0].url)
54 videoUUID = res.body.data[0].uuid
18 }) 55 })
19 56
20 it('Should execute ', async function () { 57 it('Should run filter:api.videos.list.params hook', async function () {
21 // empty 58 const res = await getVideosListPagination(servers[0].url, 0, 2)
59
60 // 2 plugins do +1 to the count parameter
61 expect(res.body.data).to.have.lengthOf(4)
62 })
63
64 it('Should run filter:api.videos.list.result', async function () {
65 const res = await getVideosListPagination(servers[0].url, 0, 0)
66
67 // Plugin do +1 to the total result
68 expect(res.body.total).to.equal(11)
69 })
70
71 it('Should run filter:api.video.get.result', async function () {
72 const res = await getVideo(servers[0].url, videoUUID)
73
74 expect(res.body.name).to.contain('<3')
22 }) 75 })
23 76
24 after(async function () { 77 after(async function () {
25 await cleanupTests([ server ]) 78 await cleanupTests(servers)
26 }) 79 })
27}) 80})
diff --git a/shared/core-utils/plugins/hooks.ts b/shared/core-utils/plugins/hooks.ts
index 047c04f7b..60f4130f5 100644
--- a/shared/core-utils/plugins/hooks.ts
+++ b/shared/core-utils/plugins/hooks.ts
@@ -8,25 +8,30 @@ function getHookType (hookName: string) {
8 return HookType.STATIC 8 return HookType.STATIC
9} 9}
10 10
11async function internalRunHook (handler: Function, hookType: HookType, param: any, onError: (err: Error) => void) { 11async function internalRunHook <T>(handler: Function, hookType: HookType, result: T, params: any, onError: (err: Error) => void) {
12 let result = param
13
14 try { 12 try {
15 const p = handler(result) 13 if (hookType === HookType.FILTER) {
14 const p = handler(result, params)
15
16 if (isPromise(p)) result = await p
17 else result = p
18
19 return result
20 }
16 21
17 switch (hookType) { 22 // Action/static hooks do not have result value
18 case HookType.FILTER: 23 const p = handler(params)
19 if (isPromise(p)) result = await p 24
20 else result = p 25 if (hookType === HookType.STATIC) {
21 break 26 if (isPromise(p)) await p
27
28 return undefined
29 }
22 30
23 case HookType.STATIC: 31 if (hookType === HookType.ACTION) {
24 if (isPromise(p)) await p 32 if (isCatchable(p)) p.catch(err => onError(err))
25 break
26 33
27 case HookType.ACTION: 34 return undefined
28 if (isCatchable(p)) p.catch(err => onError(err))
29 break
30 } 35 }
31 } catch (err) { 36 } catch (err) {
32 onError(err) 37 onError(err)
diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts
index 7a5c5344b..2302208a8 100644
--- a/shared/extra-utils/server/plugins.ts
+++ b/shared/extra-utils/server/plugins.ts
@@ -201,6 +201,10 @@ function getPluginPackageJSON (server: ServerInfo, npmName: string) {
201 return readJSON(path) 201 return readJSON(path)
202} 202}
203 203
204function getPluginTestPath (suffix = '') {
205 return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix)
206}
207
204export { 208export {
205 listPlugins, 209 listPlugins,
206 listAvailablePlugins, 210 listAvailablePlugins,
@@ -213,5 +217,6 @@ export {
213 getPluginRegisteredSettings, 217 getPluginRegisteredSettings,
214 getPackageJSONPath, 218 getPackageJSONPath,
215 updatePluginPackageJSON, 219 updatePluginPackageJSON,
216 getPluginPackageJSON 220 getPluginPackageJSON,
221 getPluginTestPath
217} 222}
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 9167ebe5b..40cf7f0f3 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -171,7 +171,8 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = []
171 thumbnails: `test${server.internalServerNumber}/thumbnails/`, 171 thumbnails: `test${server.internalServerNumber}/thumbnails/`,
172 torrents: `test${server.internalServerNumber}/torrents/`, 172 torrents: `test${server.internalServerNumber}/torrents/`,
173 captions: `test${server.internalServerNumber}/captions/`, 173 captions: `test${server.internalServerNumber}/captions/`,
174 cache: `test${server.internalServerNumber}/cache/` 174 cache: `test${server.internalServerNumber}/cache/`,
175 plugins: `test${server.internalServerNumber}/plugins/`
175 }, 176 },
176 admin: { 177 admin: {
177 email: `admin${server.internalServerNumber}@example.com` 178 email: `admin${server.internalServerNumber}@example.com`