mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
Convert to async/await
GitOrigin-RevId: 45c139a64c848650f1f51e64e8ebd233211241fb
This commit is contained in:
Generated
+2
-3
@@ -44163,6 +44163,7 @@
|
|||||||
"name": "@overleaf/notifications",
|
"name": "@overleaf/notifications",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@overleaf/fetch-utils": "*",
|
||||||
"@overleaf/logger": "*",
|
"@overleaf/logger": "*",
|
||||||
"@overleaf/metrics": "*",
|
"@overleaf/metrics": "*",
|
||||||
"@overleaf/mongo-utils": "*",
|
"@overleaf/mongo-utils": "*",
|
||||||
@@ -44173,11 +44174,9 @@
|
|||||||
"bunyan": "^1.8.15",
|
"bunyan": "^1.8.15",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"method-override": "^3.0.0",
|
"method-override": "^3.0.0",
|
||||||
"mongodb-legacy": "6.1.3",
|
"mongodb-legacy": "6.1.3"
|
||||||
"request": "^2.88.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/request": "^2.48.12",
|
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
|
|||||||
@@ -38,16 +38,7 @@ app.delete(
|
|||||||
|
|
||||||
app.get('/status', (req, res) => res.send('notifications is up'))
|
app.get('/status', (req, res) => res.send('notifications is up'))
|
||||||
|
|
||||||
app.get('/health_check', (req, res) =>
|
app.get('/health_check', HealthCheckController.check)
|
||||||
HealthCheckController.check(function (err) {
|
|
||||||
if (err) {
|
|
||||||
logger.err({ err }, 'error performing health check')
|
|
||||||
res.sendStatus(500)
|
|
||||||
} else {
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
app.get('*', (req, res) => res.sendStatus(404))
|
app.get('*', (req, res) => res.sendStatus(404))
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +1,130 @@
|
|||||||
// TODO: This file was created by bulk-decaffeinate.
|
|
||||||
// Fix any style issues and re-enable lint.
|
|
||||||
/*
|
|
||||||
* decaffeinate suggestions:
|
|
||||||
* DS102: Remove unnecessary code created because of implicit returns
|
|
||||||
* DS207: Consider shorter variations of null checks
|
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
||||||
*/
|
|
||||||
import { db, ObjectId } from './mongodb.js'
|
import { db, ObjectId } from './mongodb.js'
|
||||||
import request from 'request'
|
|
||||||
import async from 'async'
|
|
||||||
import settings from '@overleaf/settings'
|
import settings from '@overleaf/settings'
|
||||||
import logger from '@overleaf/logger'
|
import logger from '@overleaf/logger'
|
||||||
|
import {
|
||||||
|
fetchJson,
|
||||||
|
fetchNothing,
|
||||||
|
RequestFailedError,
|
||||||
|
} from '@overleaf/fetch-utils'
|
||||||
|
import { expressify } from '@overleaf/promise-utils'
|
||||||
|
|
||||||
const { port } = settings.internal.notifications
|
const { port } = settings.internal.notifications
|
||||||
|
|
||||||
export default {
|
function makeUrl(userId, endPath = '') {
|
||||||
check(callback) {
|
return new URL(`/user/${userId}/${endPath}`, `http://127.0.0.1:${port}`)
|
||||||
const userId = new ObjectId()
|
}
|
||||||
const cleanupNotifications = callback =>
|
|
||||||
db.notifications.deleteOne({ user_id: userId }, callback)
|
async function makeNotification(notificationKey, userId) {
|
||||||
|
const postOpts = {
|
||||||
let notificationKey = `smoke-test-notification-${new ObjectId()}`
|
method: 'POST',
|
||||||
const getOpts = endPath => ({
|
json: {
|
||||||
url: `http://127.0.0.1:${port}/user/${userId}${endPath}`,
|
key: notificationKey,
|
||||||
timeout: 5000,
|
messageOpts: '',
|
||||||
})
|
templateKey: 'f4g5',
|
||||||
logger.debug(
|
user_id: userId,
|
||||||
{ opts: getOpts(), key: notificationKey, userId },
|
},
|
||||||
'Health Check: running'
|
signal: AbortSignal.timeout(5000),
|
||||||
)
|
}
|
||||||
const jobs = [
|
const url = makeUrl(userId)
|
||||||
function (cb) {
|
await fetchNothing(url, postOpts)
|
||||||
const opts = getOpts('/')
|
}
|
||||||
opts.json = {
|
|
||||||
key: notificationKey,
|
async function getUsersNotifications(userId) {
|
||||||
messageOpts: '',
|
const url = makeUrl(userId)
|
||||||
templateKey: 'f4g5',
|
try {
|
||||||
user_id: userId,
|
return await fetchJson(url, {
|
||||||
}
|
signal: AbortSignal.timeout(5000),
|
||||||
return request.post(opts, cb)
|
})
|
||||||
},
|
} catch (err) {
|
||||||
function (cb) {
|
if (err instanceof RequestFailedError) {
|
||||||
const opts = getOpts('/')
|
logger.err({ err }, 'Non-2xx status code received')
|
||||||
opts.json = true
|
throw err
|
||||||
return request.get(opts, function (err, res, body) {
|
}
|
||||||
if (err != null) {
|
logger.err({ err }, 'Health Check: error getting notification')
|
||||||
logger.err({ err }, 'Health Check: error getting notification')
|
throw err
|
||||||
return callback(err)
|
}
|
||||||
} else if (res.statusCode !== 200) {
|
}
|
||||||
const e = `status code not 200 ${res.statusCode}`
|
|
||||||
logger.err({ err }, e)
|
async function userHasNotification(userId, notificationKey) {
|
||||||
return cb(e)
|
const body = await getUsersNotifications(userId)
|
||||||
}
|
const hasNotification = body.some(
|
||||||
const hasNotification = body.some(
|
notification =>
|
||||||
notification =>
|
notification.key === notificationKey && notification.user_id === userId
|
||||||
notification.key === notificationKey &&
|
)
|
||||||
notification.user_id === userId.toString()
|
if (hasNotification) {
|
||||||
)
|
return body
|
||||||
if (hasNotification) {
|
} else {
|
||||||
return cb(null, body)
|
logger.err(
|
||||||
} else {
|
{ body, notificationKey },
|
||||||
logger.err(
|
'Health Check: notification not in response'
|
||||||
{ body, notificationKey },
|
)
|
||||||
'Health Check: notification not in response'
|
throw new Error('notification not found in response')
|
||||||
)
|
}
|
||||||
return cb(new Error('notification not found in response'))
|
}
|
||||||
}
|
|
||||||
})
|
async function cleanupNotifications(userId) {
|
||||||
},
|
await db.notifications.deleteOne({ user_id: userId })
|
||||||
]
|
}
|
||||||
return async.series(jobs, function (err, body) {
|
|
||||||
if (err != null) {
|
async function deleteNotification(userId, notificationId, notificationKey) {
|
||||||
logger.err({ err }, 'Health Check: error running health check')
|
const deleteByIdUrl = makeUrl(userId, `notification/${notificationId}`)
|
||||||
return cleanupNotifications(() => callback(err))
|
try {
|
||||||
} else {
|
await fetchNothing(deleteByIdUrl, {
|
||||||
const notificationId = body[1][0]._id
|
signal: AbortSignal.timeout(5000),
|
||||||
notificationKey = body[1][0].key
|
method: 'DELETE',
|
||||||
let opts = getOpts(`/notification/${notificationId}`)
|
})
|
||||||
logger.debug(
|
} catch (err) {
|
||||||
{ notificationId, notificationKey },
|
logger.err(
|
||||||
'Health Check: doing cleanup'
|
{ err, url: deleteByIdUrl },
|
||||||
)
|
'Health Check: error cleaning up notification'
|
||||||
return request.del(opts, function (err, res, body) {
|
)
|
||||||
if (err != null) {
|
throw err
|
||||||
logger.err(
|
}
|
||||||
err,
|
|
||||||
opts,
|
const deleteByKeyUrl = makeUrl(userId)
|
||||||
'Health Check: error cleaning up notification'
|
|
||||||
)
|
try {
|
||||||
return callback(err)
|
await fetchNothing(deleteByKeyUrl, {
|
||||||
}
|
signal: AbortSignal.timeout(5000),
|
||||||
opts = getOpts('')
|
method: 'DELETE',
|
||||||
opts.json = { key: notificationKey }
|
body: {
|
||||||
return request.del(opts, function (err, res, body) {
|
key: notificationKey,
|
||||||
if (err != null) {
|
},
|
||||||
logger.err(
|
})
|
||||||
err,
|
} catch (err) {
|
||||||
opts,
|
logger.err(
|
||||||
'Health Check: error cleaning up notification'
|
{ err, url: deleteByKeyUrl },
|
||||||
)
|
'Health Check: error cleaning up notification'
|
||||||
return callback(err)
|
)
|
||||||
}
|
throw err
|
||||||
return cleanupNotifications(callback)
|
}
|
||||||
})
|
}
|
||||||
})
|
|
||||||
}
|
async function check(req, res) {
|
||||||
})
|
const userId = new ObjectId().toString()
|
||||||
},
|
let notificationKey = `smoke-test-notification-${new ObjectId()}`
|
||||||
|
|
||||||
|
logger.debug({ userId, key: notificationKey }, 'Health Check: running')
|
||||||
|
|
||||||
|
await makeNotification(notificationKey, userId)
|
||||||
|
try {
|
||||||
|
const body = await userHasNotification(userId, notificationKey)
|
||||||
|
const notificationId = body[0]._id
|
||||||
|
notificationKey = body[0].key
|
||||||
|
logger.debug(
|
||||||
|
{ notificationId, notificationKey },
|
||||||
|
'Health Check: doing cleanup'
|
||||||
|
)
|
||||||
|
await deleteNotification(userId, notificationId, notificationKey)
|
||||||
|
res.sendStatus(200)
|
||||||
|
} catch (err) {
|
||||||
|
logger.err({ err }, 'Health Check: error running health check')
|
||||||
|
res.sendStatus(500)
|
||||||
|
} finally {
|
||||||
|
await cleanupNotifications(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
check: expressify(check),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +1,109 @@
|
|||||||
import logger from '@overleaf/logger'
|
import logger from '@overleaf/logger'
|
||||||
import { db, ObjectId } from './mongodb.js'
|
import { db, ObjectId } from './mongodb.js'
|
||||||
|
|
||||||
export default {
|
async function getUserNotifications(userId) {
|
||||||
getUserNotifications(userId, callback) {
|
const query = {
|
||||||
if (callback == null) {
|
user_id: new ObjectId(userId),
|
||||||
callback = function () {}
|
templateKey: { $exists: true },
|
||||||
}
|
}
|
||||||
const query = {
|
return await db.notifications.find(query).toArray()
|
||||||
user_id: new ObjectId(userId),
|
}
|
||||||
templateKey: { $exists: true },
|
|
||||||
}
|
async function _countExistingNotifications(userId, notification) {
|
||||||
db.notifications.find(query).toArray(callback)
|
const query = {
|
||||||
},
|
user_id: new ObjectId(userId),
|
||||||
|
key: notification.key,
|
||||||
_countExistingNotifications(userId, notification, callback) {
|
}
|
||||||
if (callback == null) {
|
return await db.notifications.count(query)
|
||||||
callback = function () {}
|
}
|
||||||
}
|
|
||||||
const query = {
|
async function addNotification(userId, notification, callback) {
|
||||||
user_id: new ObjectId(userId),
|
const count = await _countExistingNotifications(userId, notification)
|
||||||
key: notification.key,
|
if (count !== 0 && !notification.forceCreate) {
|
||||||
}
|
return
|
||||||
return db.notifications.count(query, function (err, count) {
|
}
|
||||||
if (err != null) {
|
const doc = {
|
||||||
return callback(err)
|
user_id: new ObjectId(userId),
|
||||||
}
|
key: notification.key,
|
||||||
return callback(null, count)
|
messageOpts: notification.messageOpts,
|
||||||
})
|
templateKey: notification.templateKey,
|
||||||
},
|
}
|
||||||
|
// TTL index on the optional `expires` field, which should arrive as an iso date-string, corresponding to
|
||||||
addNotification(userId, notification, callback) {
|
// a datetime in the future when the document should be automatically removed.
|
||||||
return this._countExistingNotifications(
|
// in Mongo, TTL indexes only work on date fields, and ignore the document when that field is missing
|
||||||
userId,
|
// see `README.md` for instruction on creating TTL index
|
||||||
notification,
|
if (notification.expires != null) {
|
||||||
function (err, count) {
|
try {
|
||||||
if (err != null) {
|
doc.expires = new Date(notification.expires)
|
||||||
return callback(err)
|
// _testValue assignment will throw if `expires` is not a valid date
|
||||||
}
|
// eslint-disable-next-line no-unused-vars
|
||||||
if (count !== 0 && !notification.forceCreate) {
|
const _testValue = doc.expires.toISOString()
|
||||||
return callback()
|
} catch (error) {
|
||||||
}
|
logger.error(
|
||||||
const doc = {
|
{ userId, expires: notification.expires },
|
||||||
user_id: new ObjectId(userId),
|
'error converting `expires` field to Date'
|
||||||
key: notification.key,
|
)
|
||||||
messageOpts: notification.messageOpts,
|
throw error
|
||||||
templateKey: notification.templateKey,
|
}
|
||||||
}
|
}
|
||||||
// TTL index on the optional `expires` field, which should arrive as an iso date-string, corresponding to
|
return await db.notifications.updateOne(
|
||||||
// a datetime in the future when the document should be automatically removed.
|
{ user_id: doc.user_id, key: notification.key },
|
||||||
// in Mongo, TTL indexes only work on date fields, and ignore the document when that field is missing
|
{ $set: doc },
|
||||||
// see `README.md` for instruction on creating TTL index
|
{ upsert: true }
|
||||||
if (notification.expires != null) {
|
)
|
||||||
try {
|
}
|
||||||
doc.expires = new Date(notification.expires)
|
|
||||||
const _testValue = doc.expires.toISOString()
|
async function removeNotificationId(userId, notificationId) {
|
||||||
} catch (error) {
|
const searchOps = {
|
||||||
err = error
|
user_id: new ObjectId(userId),
|
||||||
logger.error(
|
_id: new ObjectId(notificationId),
|
||||||
{ userId, expires: notification.expires },
|
}
|
||||||
'error converting `expires` field to Date'
|
const updateOperation = { $unset: { templateKey: true, messageOpts: true } }
|
||||||
)
|
return await db.notifications.updateOne(searchOps, updateOperation)
|
||||||
return callback(err)
|
}
|
||||||
}
|
|
||||||
}
|
async function removeNotificationKey(userId, notificationKey) {
|
||||||
db.notifications.updateOne(
|
const searchOps = {
|
||||||
{ user_id: doc.user_id, key: notification.key },
|
user_id: new ObjectId(userId),
|
||||||
{ $set: doc },
|
key: notificationKey,
|
||||||
{ upsert: true },
|
}
|
||||||
callback
|
const updateOperation = { $unset: { templateKey: true } }
|
||||||
)
|
return await db.notifications.updateOne(searchOps, updateOperation)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
},
|
async function removeNotificationByKeyOnly(notificationKey) {
|
||||||
|
const searchOps = { key: notificationKey }
|
||||||
removeNotificationId(userId, notificationId, callback) {
|
const updateOperation = { $unset: { templateKey: true } }
|
||||||
const searchOps = {
|
return await db.notifications.updateOne(searchOps, updateOperation)
|
||||||
user_id: new ObjectId(userId),
|
}
|
||||||
_id: new ObjectId(notificationId),
|
|
||||||
}
|
async function countNotificationsByKeyOnly(notificationKey) {
|
||||||
const updateOperation = { $unset: { templateKey: true, messageOpts: true } }
|
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
||||||
db.notifications.updateOne(searchOps, updateOperation, callback)
|
return await db.notifications.countDocuments(searchOps)
|
||||||
},
|
}
|
||||||
|
|
||||||
removeNotificationKey(userId, notificationKey, callback) {
|
async function deleteUnreadNotificationsByKeyOnlyBulk(notificationKey) {
|
||||||
const searchOps = {
|
if (typeof notificationKey !== 'string') {
|
||||||
user_id: new ObjectId(userId),
|
throw new Error('refusing to bulk delete arbitrary notifications')
|
||||||
key: notificationKey,
|
}
|
||||||
}
|
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
||||||
const updateOperation = { $unset: { templateKey: true } }
|
const result = await db.notifications.deleteMany(searchOps)
|
||||||
db.notifications.updateOne(searchOps, updateOperation, callback)
|
return result.deletedCount
|
||||||
},
|
}
|
||||||
|
|
||||||
removeNotificationByKeyOnly(notificationKey, callback) {
|
// hard delete of doc, rather than removing the templateKey
|
||||||
const searchOps = { key: notificationKey }
|
async function deleteNotificationByKeyOnly(notificationKey) {
|
||||||
const updateOperation = { $unset: { templateKey: true } }
|
const searchOps = { key: notificationKey }
|
||||||
db.notifications.updateOne(searchOps, updateOperation, callback)
|
return await db.notifications.deleteOne(searchOps)
|
||||||
},
|
}
|
||||||
|
|
||||||
countNotificationsByKeyOnly(notificationKey, callback) {
|
export default {
|
||||||
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
addNotification,
|
||||||
db.notifications.count(searchOps, callback)
|
getUserNotifications,
|
||||||
},
|
removeNotificationId,
|
||||||
|
removeNotificationKey,
|
||||||
deleteUnreadNotificationsByKeyOnlyBulk(notificationKey, callback) {
|
removeNotificationByKeyOnly,
|
||||||
if (typeof notificationKey !== 'string') {
|
countNotificationsByKeyOnly,
|
||||||
throw new Error('refusing to bulk delete arbitrary notifications')
|
deleteUnreadNotificationsByKeyOnlyBulk,
|
||||||
}
|
deleteNotificationByKeyOnly,
|
||||||
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
|
||||||
db.notifications.deleteMany(searchOps, (err, result) => {
|
|
||||||
if (err) return callback(err)
|
|
||||||
callback(null, result.deletedCount)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// hard delete of doc, rather than removing the templateKey
|
|
||||||
deleteNotificationByKeyOnly(notificationKey, callback) {
|
|
||||||
const searchOps = { key: notificationKey }
|
|
||||||
db.notifications.deleteOne(searchOps, callback)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +1,103 @@
|
|||||||
// TODO: This file was created by bulk-decaffeinate.
|
|
||||||
// Fix any style issues and re-enable lint.
|
|
||||||
/*
|
|
||||||
* decaffeinate suggestions:
|
|
||||||
* DS102: Remove unnecessary code created because of implicit returns
|
|
||||||
* DS207: Consider shorter variations of null checks
|
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
||||||
*/
|
|
||||||
import logger from '@overleaf/logger'
|
import logger from '@overleaf/logger'
|
||||||
import metrics from '@overleaf/metrics'
|
import metrics from '@overleaf/metrics'
|
||||||
import Notifications from './Notifications.js'
|
import Notifications from './Notifications.js'
|
||||||
|
import { expressify } from '@overleaf/promise-utils'
|
||||||
|
|
||||||
|
async function getUserNotifications(req, res, next) {
|
||||||
|
logger.debug(
|
||||||
|
{ userId: req.params.user_id },
|
||||||
|
'getting user unread notifications'
|
||||||
|
)
|
||||||
|
metrics.inc('getUserNotifications')
|
||||||
|
const notifications = await Notifications.getUserNotifications(
|
||||||
|
req.params.user_id
|
||||||
|
)
|
||||||
|
res.json(notifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addNotification(req, res) {
|
||||||
|
logger.debug(
|
||||||
|
{ userId: req.params.user_id, notification: req.body },
|
||||||
|
'adding notification'
|
||||||
|
)
|
||||||
|
metrics.inc('addNotification')
|
||||||
|
try {
|
||||||
|
await Notifications.addNotification(req.params.user_id, req.body)
|
||||||
|
res.sendStatus(200)
|
||||||
|
} catch (err) {
|
||||||
|
res.sendStatus(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeNotificationId(req, res) {
|
||||||
|
logger.debug(
|
||||||
|
{
|
||||||
|
userId: req.params.user_id,
|
||||||
|
notificationId: req.params.notification_id,
|
||||||
|
},
|
||||||
|
'mark id notification as read'
|
||||||
|
)
|
||||||
|
metrics.inc('removeNotificationId')
|
||||||
|
await Notifications.removeNotificationId(
|
||||||
|
req.params.user_id,
|
||||||
|
req.params.notification_id
|
||||||
|
)
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeNotificationKey(req, res) {
|
||||||
|
logger.debug(
|
||||||
|
{ userId: req.params.user_id, notificationKey: req.body.key },
|
||||||
|
'mark key notification as read'
|
||||||
|
)
|
||||||
|
metrics.inc('removeNotificationKey')
|
||||||
|
await Notifications.removeNotificationKey(req.params.user_id, req.body.key)
|
||||||
|
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeNotificationByKeyOnly(req, res) {
|
||||||
|
const notificationKey = req.params.key
|
||||||
|
logger.debug({ notificationKey }, 'mark notification as read by key only')
|
||||||
|
metrics.inc('removeNotificationKey')
|
||||||
|
await Notifications.removeNotificationByKeyOnly(notificationKey)
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function countNotificationsByKeyOnly(req, res) {
|
||||||
|
const notificationKey = req.params.key
|
||||||
|
try {
|
||||||
|
const count =
|
||||||
|
await Notifications.countNotificationsByKeyOnly(notificationKey)
|
||||||
|
res.json({ count })
|
||||||
|
} catch (err) {
|
||||||
|
logger.err({ err, notificationKey }, 'cannot count by key')
|
||||||
|
res.sendStatus(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUnreadNotificationsByKeyOnlyBulk(req, res) {
|
||||||
|
const notificationKey = req.params.key
|
||||||
|
try {
|
||||||
|
const count =
|
||||||
|
await Notifications.deleteUnreadNotificationsByKeyOnlyBulk(
|
||||||
|
notificationKey
|
||||||
|
)
|
||||||
|
res.json({ count })
|
||||||
|
} catch (err) {
|
||||||
|
logger.err({ err, notificationKey }, 'cannot bulk remove by key')
|
||||||
|
res.sendStatus(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getUserNotifications(req, res, next) {
|
getUserNotifications: expressify(getUserNotifications),
|
||||||
logger.debug(
|
addNotification: expressify(addNotification),
|
||||||
{ userId: req.params.user_id },
|
deleteUnreadNotificationsByKeyOnlyBulk: expressify(
|
||||||
'getting user unread notifications'
|
deleteUnreadNotificationsByKeyOnlyBulk
|
||||||
)
|
),
|
||||||
metrics.inc('getUserNotifications')
|
removeNotificationByKeyOnly: expressify(removeNotificationByKeyOnly),
|
||||||
return Notifications.getUserNotifications(
|
removeNotificationId: expressify(removeNotificationId),
|
||||||
req.params.user_id,
|
removeNotificationKey: expressify(removeNotificationKey),
|
||||||
(err, notifications) => {
|
countNotificationsByKeyOnly: expressify(countNotificationsByKeyOnly),
|
||||||
if (err) return next(err)
|
|
||||||
res.json(notifications)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
addNotification(req, res) {
|
|
||||||
logger.debug(
|
|
||||||
{ userId: req.params.user_id, notification: req.body },
|
|
||||||
'adding notification'
|
|
||||||
)
|
|
||||||
metrics.inc('addNotification')
|
|
||||||
return Notifications.addNotification(
|
|
||||||
req.params.user_id,
|
|
||||||
req.body,
|
|
||||||
function (err, notifications) {
|
|
||||||
if (err != null) {
|
|
||||||
return res.sendStatus(500)
|
|
||||||
} else {
|
|
||||||
return res.sendStatus(200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeNotificationId(req, res, next) {
|
|
||||||
logger.debug(
|
|
||||||
{
|
|
||||||
userId: req.params.user_id,
|
|
||||||
notificationId: req.params.notification_id,
|
|
||||||
},
|
|
||||||
'mark id notification as read'
|
|
||||||
)
|
|
||||||
metrics.inc('removeNotificationId')
|
|
||||||
return Notifications.removeNotificationId(
|
|
||||||
req.params.user_id,
|
|
||||||
req.params.notification_id,
|
|
||||||
err => {
|
|
||||||
if (err) return next(err)
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeNotificationKey(req, res, next) {
|
|
||||||
logger.debug(
|
|
||||||
{ userId: req.params.user_id, notificationKey: req.body.key },
|
|
||||||
'mark key notification as read'
|
|
||||||
)
|
|
||||||
metrics.inc('removeNotificationKey')
|
|
||||||
return Notifications.removeNotificationKey(
|
|
||||||
req.params.user_id,
|
|
||||||
req.body.key,
|
|
||||||
(err, notifications) => {
|
|
||||||
if (err) return next(err)
|
|
||||||
res.sendStatus(200)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
removeNotificationByKeyOnly(req, res, next) {
|
|
||||||
const notificationKey = req.params.key
|
|
||||||
logger.debug({ notificationKey }, 'mark notification as read by key only')
|
|
||||||
metrics.inc('removeNotificationKey')
|
|
||||||
return Notifications.removeNotificationByKeyOnly(notificationKey, err => {
|
|
||||||
if (err) return next(err)
|
|
||||||
res.sendStatus(200)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
countNotificationsByKeyOnly(req, res) {
|
|
||||||
const notificationKey = req.params.key
|
|
||||||
Notifications.countNotificationsByKeyOnly(notificationKey, (err, count) => {
|
|
||||||
if (err) {
|
|
||||||
logger.err({ err, notificationKey }, 'cannot count by key')
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
res.json({ count })
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteUnreadNotificationsByKeyOnlyBulk(req, res) {
|
|
||||||
const notificationKey = req.params.key
|
|
||||||
Notifications.deleteUnreadNotificationsByKeyOnlyBulk(
|
|
||||||
notificationKey,
|
|
||||||
(err, count) => {
|
|
||||||
if (err) {
|
|
||||||
logger.err({ err, notificationKey }, 'cannot bulk remove by key')
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
res.json({ count })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@overleaf/fetch-utils": "*",
|
||||||
"@overleaf/logger": "*",
|
"@overleaf/logger": "*",
|
||||||
"@overleaf/metrics": "*",
|
"@overleaf/metrics": "*",
|
||||||
"@overleaf/mongo-utils": "*",
|
"@overleaf/mongo-utils": "*",
|
||||||
@@ -29,11 +30,9 @@
|
|||||||
"bunyan": "^1.8.15",
|
"bunyan": "^1.8.15",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"method-override": "^3.0.0",
|
"method-override": "^3.0.0",
|
||||||
"mongodb-legacy": "6.1.3",
|
"mongodb-legacy": "6.1.3"
|
||||||
"request": "^2.88.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/request": "^2.48.12",
|
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe('Notifications Tests', () => {
|
|||||||
ObjectId,
|
ObjectId,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
notifications = await import(modulePath)
|
notifications = (await import(modulePath)).default
|
||||||
|
|
||||||
stubbedNotification = {
|
stubbedNotification = {
|
||||||
user_id: new ObjectId(userId),
|
user_id: new ObjectId(userId),
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ describe('Notifications Controller', () => {
|
|||||||
removeNotificationKey: vi.fn(),
|
removeNotificationKey: vi.fn(),
|
||||||
}
|
}
|
||||||
|
|
||||||
vi.doMock('../../../app/js/Notifications', () => notifications)
|
vi.doMock('../../../app/js/Notifications', () => ({
|
||||||
|
default: notifications,
|
||||||
|
}))
|
||||||
|
|
||||||
vi.doMock('@overleaf/metrics', () => ({
|
vi.doMock('@overleaf/metrics', () => ({
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
Reference in New Issue
Block a user