From 2f1548fda32c3ba9e53913270394eedfacd55986 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jan 2019 11:26:41 +0100 Subject: Add notifications in the client --- server/controllers/api/users/my-notifications.ts | 15 ++++++- server/helpers/custom-validators/misc.ts | 6 +-- .../custom-validators/user-notifications.ts | 8 +++- .../migrations/0315-user-notifications.ts | 2 +- server/lib/notifier.ts | 4 +- server/lib/user.ts | 18 ++++---- .../middlewares/validators/user-notifications.ts | 5 ++- server/models/account/user-notification.ts | 6 +++ .../tests/api/check-params/user-notifications.ts | 51 +++++++++++++++++----- server/tests/api/check-params/users.ts | 3 +- server/tests/api/users/user-notifications.ts | 50 ++++++++++++++------- server/tests/api/users/users.ts | 4 -- 12 files changed, 120 insertions(+), 52 deletions(-) (limited to 'server') diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index d74d26add..76cf97587 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts @@ -45,6 +45,11 @@ myNotificationsRouter.post('/me/notifications/read', asyncMiddleware(markAsReadUserNotifications) ) +myNotificationsRouter.post('/me/notifications/read-all', + authenticate, + asyncMiddleware(markAsReadAllUserNotifications) +) + export { myNotificationsRouter } @@ -70,7 +75,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re myVideoImportFinished: body.myVideoImportFinished, newFollow: body.newFollow, newUserRegistration: body.newUserRegistration, - commentMention: body.commentMention, + commentMention: body.commentMention } await UserNotificationSettingModel.update(values, query) @@ -93,3 +98,11 @@ async function markAsReadUserNotifications (req: express.Request, res: express.R return res.status(204).end() } + +async function markAsReadAllUserNotifications (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + await UserNotificationModel.markAllAsRead(user.id) + + return res.status(204).end() +} diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index a093e3e1b..b6f0ebe6f 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts @@ -9,8 +9,8 @@ function isArray (value: any) { return Array.isArray(value) } -function isIntArray (value: any) { - return Array.isArray(value) && value.every(v => validator.isInt('' + v)) +function isNotEmptyIntArray (value: any) { + return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0 } function isDateValid (value: string) { @@ -82,7 +82,7 @@ function isFileValid ( export { exists, - isIntArray, + isNotEmptyIntArray, isArray, isIdValid, isUUIDValid, diff --git a/server/helpers/custom-validators/user-notifications.ts b/server/helpers/custom-validators/user-notifications.ts index 4fb5d922d..02ea3bbc2 100644 --- a/server/helpers/custom-validators/user-notifications.ts +++ b/server/helpers/custom-validators/user-notifications.ts @@ -9,8 +9,12 @@ function isUserNotificationTypeValid (value: any) { function isUserNotificationSettingValid (value: any) { return exists(value) && - validator.isInt('' + value) && - UserNotificationSettingValue[ value ] !== undefined + validator.isInt('' + value) && ( + value === UserNotificationSettingValue.NONE || + value === UserNotificationSettingValue.WEB || + value === UserNotificationSettingValue.EMAIL || + value === (UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL) + ) } export { diff --git a/server/initializers/migrations/0315-user-notifications.ts b/server/initializers/migrations/0315-user-notifications.ts index 34f9fd193..8284c58a0 100644 --- a/server/initializers/migrations/0315-user-notifications.ts +++ b/server/initializers/migrations/0315-user-notifications.ts @@ -31,7 +31,7 @@ PRIMARY KEY ("id")) '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' + '"myVideoPublished", "myVideoImportFinished", "newUserRegistration", "newFollow", "commentMention", ' + '"userId", "createdAt", "updatedAt") ' + - '(SELECT 2, 2, 4, 4, 2, 2, 2, 2, 2, id, NOW(), NOW() FROM "user")' + '(SELECT 1, 1, 3, 3, 1, 1, 1, 1, 1, id, NOW(), NOW() FROM "user")' await utils.sequelize.query(query) } diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index 2c51d7101..d1b331346 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts @@ -436,11 +436,11 @@ class Notifier { private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified !== true) return false - return value === UserNotificationSettingValue.EMAIL || value === UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL + return value & UserNotificationSettingValue.EMAIL } private isWebNotificationEnabled (value: UserNotificationSettingValue) { - return value === UserNotificationSettingValue.WEB_NOTIFICATION || value === UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL + return value & UserNotificationSettingValue.WEB } static get Instance () { diff --git a/server/lib/user.ts b/server/lib/user.ts index 9e24e85a0..a39ef6c3d 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -98,15 +98,15 @@ export { function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Transaction | undefined) { const values: UserNotificationSetting & { userId: number } = { userId: user.id, - newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, - newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, - myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, - myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION, - videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - newUserRegistration: UserNotificationSettingValue.WEB_NOTIFICATION, - commentMention: UserNotificationSettingValue.WEB_NOTIFICATION, - newFollow: UserNotificationSettingValue.WEB_NOTIFICATION + newVideoFromSubscription: UserNotificationSettingValue.WEB, + newCommentOnMyVideo: UserNotificationSettingValue.WEB, + myVideoImportFinished: UserNotificationSettingValue.WEB, + myVideoPublished: UserNotificationSettingValue.WEB, + videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newUserRegistration: UserNotificationSettingValue.WEB, + commentMention: UserNotificationSettingValue.WEB, + newFollow: UserNotificationSettingValue.WEB } return UserNotificationSettingModel.create(values, { transaction: t }) diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts index 1c31f0a73..46486e081 100644 --- a/server/middlewares/validators/user-notifications.ts +++ b/server/middlewares/validators/user-notifications.ts @@ -4,7 +4,7 @@ import { body, query } from 'express-validator/check' import { logger } from '../../helpers/logger' import { areValidationErrors } from './utils' import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' -import { isIntArray } from '../../helpers/custom-validators/misc' +import { isNotEmptyIntArray } from '../../helpers/custom-validators/misc' const listUserNotificationsValidator = [ query('unread') @@ -42,7 +42,8 @@ const updateNotificationSettingsValidator = [ const markAsReadUserNotificationsValidator = [ body('ids') - .custom(isIntArray).withMessage('Should have a valid notification ids to mark as read'), + .optional() + .custom(isNotEmptyIntArray).withMessage('Should have a valid notification ids to mark as read'), (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking markAsReadUserNotificationsValidator parameters', { parameters: req.body }) diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index 79afce600..9e4f982a3 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts @@ -290,6 +290,12 @@ export class UserNotificationModel extends Model { return UserNotificationModel.update({ read: true }, query) } + static markAllAsRead (userId: number) { + const query = { where: { userId } } + + return UserNotificationModel.update({ read: true }, query) + } + toFormattedJSON (): UserNotification { const video = this.Video ? Object.assign(this.formatVideo(this.Video), { channel: { diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts index 635f5c9a3..714f481e9 100644 --- a/server/tests/api/check-params/user-notifications.ts +++ b/server/tests/api/check-params/user-notifications.ts @@ -96,6 +96,16 @@ describe('Test user notifications API validators', function () { statusCodeExpected: 400 }) + await makePostBodyRequest({ + url: server.url, + path, + fields: { + ids: [ ] + }, + token: server.accessToken, + statusCodeExpected: 400 + }) + await makePostBodyRequest({ url: server.url, path, @@ -131,18 +141,39 @@ describe('Test user notifications API validators', function () { }) }) + describe('When marking as read my notifications', function () { + const path = '/api/v1/users/me/notifications/read-all' + + it('Should fail with a non authenticated user', async function () { + await makePostBodyRequest({ + url: server.url, + path, + statusCodeExpected: 401 + }) + }) + + it('Should succeed with the correct parameters', async function () { + await makePostBodyRequest({ + url: server.url, + path, + token: server.accessToken, + statusCodeExpected: 204 + }) + }) + }) + describe('When updating my notification settings', function () { const path = '/api/v1/users/me/notification-settings' const correctFields: UserNotificationSetting = { - newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION, - newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, - videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION, - blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION, - myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION, - myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION, - commentMention: UserNotificationSettingValue.WEB_NOTIFICATION, - newFollow: UserNotificationSettingValue.WEB_NOTIFICATION, - newUserRegistration: UserNotificationSettingValue.WEB_NOTIFICATION + newVideoFromSubscription: UserNotificationSettingValue.WEB, + newCommentOnMyVideo: UserNotificationSettingValue.WEB, + videoAbuseAsModerator: UserNotificationSettingValue.WEB, + blacklistOnMyVideo: UserNotificationSettingValue.WEB, + myVideoImportFinished: UserNotificationSettingValue.WEB, + myVideoPublished: UserNotificationSettingValue.WEB, + commentMention: UserNotificationSettingValue.WEB, + newFollow: UserNotificationSettingValue.WEB, + newUserRegistration: UserNotificationSettingValue.WEB } it('Should fail with missing fields', async function () { @@ -150,7 +181,7 @@ describe('Test user notifications API validators', function () { url: server.url, path, token: server.accessToken, - fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION }, + fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB }, statusCodeExpected: 400 }) }) diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index f8044cbd4..a3e8e2e9c 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts @@ -485,11 +485,10 @@ describe('Test users API validators', function () { email: 'email@example.com', emailVerified: true, videoQuota: 42, - role: UserRole.MODERATOR + role: UserRole.USER } await makePutBodyRequest({ url: server.url, path: path + userId, token: server.accessToken, fields, statusCodeExpected: 204 }) - userAccessToken = await userLogin(server, user) }) }) diff --git a/server/tests/api/users/user-notifications.ts b/server/tests/api/users/user-notifications.ts index ae77b4db2..ad68d8e69 100644 --- a/server/tests/api/users/user-notifications.ts +++ b/server/tests/api/users/user-notifications.ts @@ -37,7 +37,8 @@ import { getLastNotification, getUserNotifications, markAsReadNotifications, - updateMyNotificationSettings + updateMyNotificationSettings, + markAsReadAllNotifications } from '../../../../shared/utils/users/user-notifications' import { User, @@ -88,15 +89,15 @@ describe('Test users notifications', function () { let channelId: number const allNotificationSettings: UserNotificationSetting = { - newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - myVideoImportFinished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - myVideoPublished: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - commentMention: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - newFollow: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL, - newUserRegistration: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL + newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL } before(async function () { @@ -174,7 +175,10 @@ describe('Test users notifications', function () { }) it('Should send a new video notification if the user follows the local video publisher', async function () { + this.timeout(10000) + await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001') + await waitJobs(servers) const { name, uuid } = await uploadVideoByLocalAccount(servers) await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') @@ -184,6 +188,7 @@ describe('Test users notifications', function () { this.timeout(50000) // Server 2 has transcoding enabled await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002') + await waitJobs(servers) const { name, uuid } = await uploadVideoByRemoteAccount(servers) await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') @@ -822,8 +827,9 @@ describe('Test users notifications', function () { }) it('Should notify when a local channel is following one of our channel', async function () { - await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') + this.timeout(10000) + await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') await waitJobs(servers) await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') @@ -832,8 +838,9 @@ describe('Test users notifications', function () { }) it('Should notify when a remote channel is following one of our channel', async function () { - await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') + this.timeout(10000) + await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') await waitJobs(servers) await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') @@ -895,6 +902,15 @@ describe('Test users notifications', function () { expect(notification.read).to.be.false } }) + + it('Should mark as read all notifications', async function () { + await markAsReadAllNotifications(servers[ 0 ].url, userAccessToken) + + const res = await getUserNotifications(servers[ 0 ].url, userAccessToken, 0, 10, true) + + expect(res.body.total).to.equal(0) + expect(res.body.data).to.have.lengthOf(0) + }) }) describe('Notification settings', function () { @@ -928,13 +944,13 @@ describe('Test users notifications', function () { it('Should only have web notifications', async function () { await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { - newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION + newVideoFromSubscription: UserNotificationSettingValue.WEB })) { const res = await getMyUserInformation(servers[0].url, userAccessToken) const info = res.body as User - expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION) + expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB) } const { name, uuid } = await uploadVideoByLocalAccount(servers) @@ -976,13 +992,15 @@ describe('Test users notifications', function () { it('Should have email and web notifications', async function () { await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(allNotificationSettings, { - newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL + newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL })) { const res = await getMyUserInformation(servers[0].url, userAccessToken) const info = res.body as User - expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL) + expect(info.notificationSettings.newVideoFromSubscription).to.equal( + UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL + ) } const { name, uuid } = await uploadVideoByLocalAccount(servers) diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 4914c8ed5..ad98ab1c7 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts @@ -501,10 +501,6 @@ describe('Test users', function () { accessTokenUser = await userLogin(server, user) }) - it('Should not be able to delete a user by a moderator', async function () { - await removeUser(server.url, 2, accessTokenUser, 403) - }) - it('Should be able to list video blacklist by a moderator', async function () { await getBlacklistedVideosList(server.url, accessTokenUser) }) -- cgit v1.2.3