diff options
-rw-r--r-- | server.ts | 3 | ||||
-rw-r--r-- | server/controllers/activitypub/client.ts | 2 | ||||
-rw-r--r-- | server/controllers/api/videos/comment.ts | 10 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 10 | ||||
-rw-r--r-- | server/lib/plugins/hooks.ts | 19 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 10 | ||||
-rw-r--r-- | server/lib/video-blacklist.ts | 5 | ||||
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 5 | ||||
-rw-r--r-- | server/models/video/video.ts | 7 | ||||
-rw-r--r-- | server/tests/cli/plugins.ts | 8 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-two/main.js | 21 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-two/package.json | 19 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test/main.js | 62 | ||||
-rw-r--r-- | server/tests/plugins/action-hooks.ts | 91 | ||||
-rw-r--r-- | server/tests/plugins/filter-hooks.ts | 69 | ||||
-rw-r--r-- | shared/core-utils/plugins/hooks.ts | 35 | ||||
-rw-r--r-- | shared/extra-utils/server/plugins.ts | 7 | ||||
-rw-r--r-- | shared/extra-utils/server/servers.ts | 3 |
18 files changed, 306 insertions, 80 deletions
@@ -114,6 +114,7 @@ import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto' | |||
114 | import { PeerTubeSocket } from './server/lib/peertube-socket' | 114 | import { PeerTubeSocket } from './server/lib/peertube-socket' |
115 | import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls' | 115 | import { updateStreamingPlaylistsInfohashesIfNeeded } from './server/lib/hls' |
116 | import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler' | 116 | import { PluginsCheckScheduler } from './server/lib/schedulers/plugins-check-scheduler' |
117 | import { 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 | ||
209 | async function videoController (req: express.Request, res: express.Response) { | 209 | async 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' | |||
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import * as Bluebird from 'bluebird' | 4 | import * as Bluebird from 'bluebird' |
5 | 5 | ||
6 | type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T> | ||
7 | type RawFunction <U, T> = (params: U) => T | ||
8 | |||
6 | // Helpers to run hooks | 9 | // Helpers to run hooks |
7 | const Hooks = { | 10 | const 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' | |||
9 | import { Hooks } from './plugins/hooks' | 9 | import { Hooks } from './plugins/hooks' |
10 | 10 | ||
11 | async function autoBlacklistVideoIfNeeded (video: VideoModel, user?: UserModel, transaction?: Transaction) { | 11 | async 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' |
15 | import { join } from 'path' | ||
16 | import { ServerConfig } from '../../../shared/models/server' | 16 | import { ServerConfig } from '../../../shared/models/server' |
17 | import { expect } from 'chai' | 17 | import { 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 @@ | |||
1 | async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) { | ||
2 | registerHook({ | ||
3 | target: 'filter:api.videos.list.params', | ||
4 | handler: obj => addToCount(obj) | ||
5 | }) | ||
6 | } | ||
7 | |||
8 | async function unregister () { | ||
9 | return | ||
10 | } | ||
11 | |||
12 | module.exports = { | ||
13 | register, | ||
14 | unregister | ||
15 | } | ||
16 | |||
17 | // ############################################################################ | ||
18 | |||
19 | function 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 @@ | |||
1 | async function register ({ registerHook, registerSetting, settingsManager, storageManager }) { | 1 | async 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 | ||
23 | async function unregister () { | 52 | async function unregister () { |
@@ -31,9 +60,6 @@ module.exports = { | |||
31 | 60 | ||
32 | // ############################################################################ | 61 | // ############################################################################ |
33 | 62 | ||
34 | async function displayHelloWorld (settingsManager, defaultAdmin) { | 63 | function 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 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' | 5 | import { |
6 | import { setAccessTokensToServers } from '../../../shared/extra-utils' | 6 | cleanupTests, |
7 | flushAndRunMultipleServers, | ||
8 | flushAndRunServer, killallServers, reRunServer, | ||
9 | ServerInfo, | ||
10 | waitUntilLog | ||
11 | } from '../../../shared/extra-utils/server/servers' | ||
12 | import { | ||
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 | ||
8 | const expect = chai.expect | 23 | const expect = chai.expect |
9 | 24 | ||
10 | describe('Test plugin action hooks', function () { | 25 | describe('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 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' | 5 | import { |
6 | import { setAccessTokensToServers } from '../../../shared/extra-utils' | 6 | cleanupTests, |
7 | flushAndRunMultipleServers, | ||
8 | flushAndRunServer, killallServers, reRunServer, | ||
9 | ServerInfo, | ||
10 | waitUntilLog | ||
11 | } from '../../../shared/extra-utils/server/servers' | ||
12 | import { | ||
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 | ||
8 | const expect = chai.expect | 24 | const expect = chai.expect |
9 | 25 | ||
10 | describe('Test plugin filter hooks', function () { | 26 | describe('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 | ||
11 | async function internalRunHook (handler: Function, hookType: HookType, param: any, onError: (err: Error) => void) { | 11 | async 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 | ||
204 | function getPluginTestPath (suffix = '') { | ||
205 | return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix) | ||
206 | } | ||
207 | |||
204 | export { | 208 | export { |
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` |