move module functions to top level

GitOrigin-RevId: 73524dcec49faa05d1fd4dd4ffa6950803175f33
This commit is contained in:
Tim Alby 2022-01-07 12:48:39 +01:00 committed by Copybot
parent b5d2122f05
commit f3a5e8a0e8
4 changed files with 450 additions and 455 deletions

View file

@ -1,65 +1,66 @@
let MessageFormatter function formatMessageForClientSide(message) {
module.exports = MessageFormatter = { if (message._id) {
formatMessageForClientSide(message) { message.id = message._id.toString()
if (message._id) { delete message._id
message.id = message._id.toString() }
delete message._id const formattedMessage = {
} id: message.id,
const formattedMessage = { content: message.content,
id: message.id, timestamp: message.timestamp,
content: message.content, user_id: message.user_id,
timestamp: message.timestamp, }
user_id: message.user_id, if (message.edited_at) {
} formattedMessage.edited_at = message.edited_at
if (message.edited_at) { }
formattedMessage.edited_at = message.edited_at return formattedMessage
} }
return formattedMessage
}, function formatMessagesForClientSide(messages) {
return messages.map(message => formatMessageForClientSide(message))
formatMessagesForClientSide(messages) { }
return messages.map(message => this.formatMessageForClientSide(message))
}, function groupMessagesByThreads(rooms, messages) {
let room, thread
groupMessagesByThreads(rooms, messages) { const roomsById = {}
let room, thread for (room of rooms) {
const roomsById = {} roomsById[room._id.toString()] = room
for (room of rooms) { }
roomsById[room._id.toString()] = room
} const threads = {}
const getThread = function (room) {
const threads = {} const threadId = room.thread_id.toString()
const getThread = function (room) { if (threads[threadId]) {
const threadId = room.thread_id.toString() return threads[threadId]
if (threads[threadId]) { } else {
return threads[threadId] const thread = { messages: [] }
} else { if (room.resolved) {
const thread = { messages: [] } thread.resolved = true
if (room.resolved) { thread.resolved_at = room.resolved.ts
thread.resolved = true thread.resolved_by_user_id = room.resolved.user_id
thread.resolved_at = room.resolved.ts }
thread.resolved_by_user_id = room.resolved.user_id threads[threadId] = thread
} return thread
threads[threadId] = thread }
return thread }
}
} for (const message of messages) {
room = roomsById[message.room_id.toString()]
for (const message of messages) { if (room) {
room = roomsById[message.room_id.toString()] thread = getThread(room)
if (room) { thread.messages.push(formatMessageForClientSide(message))
thread = getThread(room) }
thread.messages.push( }
MessageFormatter.formatMessageForClientSide(message)
) for (const threadId in threads) {
} thread = threads[threadId]
} thread.messages.sort((a, b) => a.timestamp - b.timestamp)
}
for (const threadId in threads) {
thread = threads[threadId] return threads
thread.messages.sort((a, b) => a.timestamp - b.timestamp) }
}
module.exports = {
return threads formatMessagesForClientSide,
}, formatMessageForClientSide,
groupMessagesByThreads,
} }

View file

@ -1,236 +1,221 @@
let MessageHttpController
const logger = require('@overleaf/logger') const logger = require('@overleaf/logger')
const MessageManager = require('./MessageManager') const MessageManager = require('./MessageManager')
const MessageFormatter = require('./MessageFormatter') const MessageFormatter = require('./MessageFormatter')
const ThreadManager = require('../Threads/ThreadManager') const ThreadManager = require('../Threads/ThreadManager')
const { ObjectId } = require('../../mongodb') const { ObjectId } = require('../../mongodb')
module.exports = MessageHttpController = { const DEFAULT_MESSAGE_LIMIT = 50
DEFAULT_MESSAGE_LIMIT: 50, const MAX_MESSAGE_LENGTH = 10 * 1024 // 10kb, about 1,500 words
MAX_MESSAGE_LENGTH: 10 * 1024, // 10kb, about 1,500 words
getGlobalMessages(req, res, next) { function getGlobalMessages(req, res, next) {
MessageHttpController._getMessages( _getMessages(ThreadManager.GLOBAL_THREAD, req, res, next)
ThreadManager.GLOBAL_THREAD, }
req,
res,
next
)
},
sendGlobalMessage(req, res, next) { function sendGlobalMessage(req, res, next) {
MessageHttpController._sendMessage( _sendMessage(ThreadManager.GLOBAL_THREAD, req, res, next)
ThreadManager.GLOBAL_THREAD, }
req,
res,
next
)
},
sendThreadMessage(req, res, next) { function sendThreadMessage(req, res, next) {
MessageHttpController._sendMessage(req.params.threadId, req, res, next) _sendMessage(req.params.threadId, req, res, next)
}, }
getAllThreads(req, res, next) { function getAllThreads(req, res, next) {
const { projectId } = req.params const { projectId } = req.params
logger.log({ projectId }, 'getting all threads') logger.log({ projectId }, 'getting all threads')
ThreadManager.findAllThreadRooms(projectId, function (error, rooms) { ThreadManager.findAllThreadRooms(projectId, function (error, rooms) {
if (error) {
return next(error)
}
const roomIds = rooms.map(r => r._id)
MessageManager.findAllMessagesInRooms(roomIds, function (error, messages) {
if (error) { if (error) {
return next(error) return next(error)
} }
const roomIds = rooms.map(r => r._id) const threads = MessageFormatter.groupMessagesByThreads(rooms, messages)
MessageManager.findAllMessagesInRooms( res.json(threads)
roomIds,
function (error, messages) {
if (error) {
return next(error)
}
const threads = MessageFormatter.groupMessagesByThreads(
rooms,
messages
)
res.json(threads)
}
)
}) })
}, })
}
resolveThread(req, res, next) { function resolveThread(req, res, next) {
const { projectId, threadId } = req.params const { projectId, threadId } = req.params
const { user_id: userId } = req.body const { user_id: userId } = req.body
logger.log({ userId, projectId, threadId }, 'marking thread as resolved') logger.log({ userId, projectId, threadId }, 'marking thread as resolved')
ThreadManager.resolveThread(projectId, threadId, userId, function (error) { ThreadManager.resolveThread(projectId, threadId, userId, function (error) {
if (error) {
return next(error)
}
res.sendStatus(204)
})
}
function reopenThread(req, res, next) {
const { projectId, threadId } = req.params
logger.log({ projectId, threadId }, 'reopening thread')
ThreadManager.reopenThread(projectId, threadId, function (error) {
if (error) {
return next(error)
}
res.sendStatus(204)
})
}
function deleteThread(req, res, next) {
const { projectId, threadId } = req.params
logger.log({ projectId, threadId }, 'deleting thread')
ThreadManager.deleteThread(projectId, threadId, function (error, roomId) {
if (error) {
return next(error)
}
MessageManager.deleteAllMessagesInRoom(roomId, function (error) {
if (error) { if (error) {
return next(error) return next(error)
} }
res.sendStatus(204) res.sendStatus(204)
}) })
}, // No content })
}
reopenThread(req, res, next) { function editMessage(req, res, next) {
const { projectId, threadId } = req.params const { content } = req.body
logger.log({ projectId, threadId }, 'reopening thread') const { projectId, threadId, messageId } = req.params
ThreadManager.reopenThread(projectId, threadId, function (error) { logger.log({ projectId, threadId, messageId, content }, 'editing message')
if (error) { ThreadManager.findOrCreateThread(projectId, threadId, function (error, room) {
return next(error) if (error) {
} return next(error)
res.sendStatus(204) }
}) MessageManager.updateMessage(
}, // No content room._id,
messageId,
deleteThread(req, res, next) { content,
const { projectId, threadId } = req.params Date.now(),
logger.log({ projectId, threadId }, 'deleting thread') function (error) {
ThreadManager.deleteThread(projectId, threadId, function (error, roomId) {
if (error) {
return next(error)
}
MessageManager.deleteAllMessagesInRoom(roomId, function (error) {
if (error) { if (error) {
return next(error) return next(error)
} }
res.sendStatus(204) res.sendStatus(204)
})
})
}, // No content
editMessage(req, res, next) {
const { content } = req.body
const { projectId, threadId, messageId } = req.params
logger.log({ projectId, threadId, messageId, content }, 'editing message')
ThreadManager.findOrCreateThread(
projectId,
threadId,
function (error, room) {
if (error) {
return next(error)
}
MessageManager.updateMessage(
room._id,
messageId,
content,
Date.now(),
function (error) {
if (error) {
return next(error)
}
res.sendStatus(204)
}
)
} }
) )
}, })
}
deleteMessage(req, res, next) {
const { projectId, threadId, messageId } = req.params function deleteMessage(req, res, next) {
logger.log({ projectId, threadId, messageId }, 'deleting message') const { projectId, threadId, messageId } = req.params
ThreadManager.findOrCreateThread( logger.log({ projectId, threadId, messageId }, 'deleting message')
projectId, ThreadManager.findOrCreateThread(projectId, threadId, function (error, room) {
threadId, if (error) {
function (error, room) { return next(error)
if (error) { }
return next(error) MessageManager.deleteMessage(
} room._id,
MessageManager.deleteMessage( messageId,
room._id, function (error, message) {
messageId, if (error) {
function (error, message) { return next(error)
if (error) { }
return next(error) res.sendStatus(204)
} }
res.sendStatus(204) )
} })
) }
}
) function _sendMessage(clientThreadId, req, res, next) {
}, const { user_id: userId, content } = req.body
const { projectId } = req.params
_sendMessage(clientThreadId, req, res, next) { if (!ObjectId.isValid(userId)) {
const { user_id: userId, content } = req.body return res.status(400).send('Invalid userId')
const { projectId } = req.params }
if (!ObjectId.isValid(userId)) { if (!content) {
return res.status(400).send('Invalid userId') return res.status(400).send('No content provided')
} }
if (!content) { if (content.length > MAX_MESSAGE_LENGTH) {
return res.status(400).send('No content provided') return res
} .status(400)
if (content.length > this.MAX_MESSAGE_LENGTH) { .send(`Content too long (> ${MAX_MESSAGE_LENGTH} bytes)`)
return res }
.status(400) logger.log(
.send(`Content too long (> ${this.MAX_MESSAGE_LENGTH} bytes)`) { clientThreadId, projectId, userId, content },
} 'new message received'
logger.log( )
{ clientThreadId, projectId, userId, content }, ThreadManager.findOrCreateThread(
'new message received' projectId,
) clientThreadId,
ThreadManager.findOrCreateThread( function (error, thread) {
projectId, if (error) {
clientThreadId, return next(error)
function (error, thread) { }
if (error) { MessageManager.createMessage(
return next(error) thread._id,
} userId,
MessageManager.createMessage( content,
thread._id, Date.now(),
userId, function (error, message) {
content, if (error) {
Date.now(), return next(error)
function (error, message) { }
if (error) { message = MessageFormatter.formatMessageForClientSide(message)
return next(error) message.room_id = projectId
} res.status(201).send(message)
message = MessageFormatter.formatMessageForClientSide(message) }
message.room_id = projectId )
res.status(201).send(message) }
} )
) }
}
) function _getMessages(clientThreadId, req, res, next) {
}, let before, limit
const { projectId } = req.params
_getMessages(clientThreadId, req, res, next) { if (req.query.before) {
let before, limit before = parseInt(req.query.before, 10)
const { projectId } = req.params } else {
if (req.query.before) { before = null
before = parseInt(req.query.before, 10) }
} else { if (req.query.limit) {
before = null limit = parseInt(req.query.limit, 10)
} } else {
if (req.query.limit) { limit = DEFAULT_MESSAGE_LIMIT
limit = parseInt(req.query.limit, 10) }
} else { logger.log(
limit = MessageHttpController.DEFAULT_MESSAGE_LIMIT { limit, before, projectId, clientThreadId },
} 'get message request received'
logger.log( )
{ limit, before, projectId, clientThreadId }, ThreadManager.findOrCreateThread(
'get message request received' projectId,
) clientThreadId,
ThreadManager.findOrCreateThread( function (error, thread) {
projectId, if (error) {
clientThreadId, return next(error)
function (error, thread) { }
if (error) { const threadObjectId = thread._id
return next(error) logger.log(
} { limit, before, projectId, clientThreadId, threadObjectId },
const threadObjectId = thread._id 'found or created thread'
logger.log( )
{ limit, before, projectId, clientThreadId, threadObjectId }, MessageManager.getMessages(
'found or created thread' threadObjectId,
) limit,
MessageManager.getMessages( before,
threadObjectId, function (error, messages) {
limit, if (error) {
before, return next(error)
function (error, messages) { }
if (error) { messages = MessageFormatter.formatMessagesForClientSide(messages)
return next(error) logger.log({ projectId, messages }, 'got messages')
} res.status(200).send(messages)
messages = MessageFormatter.formatMessagesForClientSide(messages) }
logger.log({ projectId, messages }, 'got messages') )
res.status(200).send(messages) }
} )
) }
}
) module.exports = {
}, getGlobalMessages,
sendGlobalMessage,
sendThreadMessage,
getAllThreads,
resolveThread,
reopenThread,
deleteThread,
editMessage,
deleteMessage,
} }

View file

@ -3,91 +3,94 @@ const { db, ObjectId } = require('../../mongodb')
const metrics = require('@overleaf/metrics') const metrics = require('@overleaf/metrics')
const logger = require('@overleaf/logger') const logger = require('@overleaf/logger')
function createMessage(roomId, userId, content, timestamp, callback) {
let newMessageOpts = {
content,
room_id: roomId,
user_id: userId,
timestamp,
}
newMessageOpts = _ensureIdsAreObjectIds(newMessageOpts)
db.messages.insertOne(newMessageOpts, function (error, confirmation) {
if (error) {
return callback(error)
}
newMessageOpts._id = confirmation.insertedId
callback(null, newMessageOpts)
})
}
function getMessages(roomId, limit, before, callback) {
let query = { room_id: roomId }
if (before) {
query.timestamp = { $lt: before }
}
query = _ensureIdsAreObjectIds(query)
db.messages.find(query).sort({ timestamp: -1 }).limit(limit).toArray(callback)
}
function findAllMessagesInRooms(roomIds, callback) {
db.messages
.find({
room_id: { $in: roomIds },
})
.toArray(callback)
}
function deleteAllMessagesInRoom(roomId, callback) {
db.messages.deleteMany(
{
room_id: roomId,
},
callback
)
}
function updateMessage(roomId, messageId, content, timestamp, callback) {
const query = _ensureIdsAreObjectIds({
_id: messageId,
room_id: roomId,
})
db.messages.updateOne(
query,
{
$set: {
content,
edited_at: timestamp,
},
},
callback
)
}
function deleteMessage(roomId, messageId, callback) {
const query = _ensureIdsAreObjectIds({
_id: messageId,
room_id: roomId,
})
db.messages.deleteOne(query, callback)
}
function _ensureIdsAreObjectIds(query) {
if (query.user_id && !(query.user_id instanceof ObjectId)) {
query.user_id = ObjectId(query.user_id)
}
if (query.room_id && !(query.room_id instanceof ObjectId)) {
query.room_id = ObjectId(query.room_id)
}
if (query._id && !(query._id instanceof ObjectId)) {
query._id = ObjectId(query._id)
}
return query
}
module.exports = MessageManager = { module.exports = MessageManager = {
createMessage(roomId, userId, content, timestamp, callback) { createMessage,
let newMessageOpts = { getMessages,
content, findAllMessagesInRooms,
room_id: roomId, deleteAllMessagesInRoom,
user_id: userId, updateMessage,
timestamp, deleteMessage,
}
newMessageOpts = this._ensureIdsAreObjectIds(newMessageOpts)
db.messages.insertOne(newMessageOpts, function (error, confirmation) {
if (error) {
return callback(error)
}
newMessageOpts._id = confirmation.insertedId
callback(null, newMessageOpts)
})
},
getMessages(roomId, limit, before, callback) {
let query = { room_id: roomId }
if (before) {
query.timestamp = { $lt: before }
}
query = this._ensureIdsAreObjectIds(query)
db.messages
.find(query)
.sort({ timestamp: -1 })
.limit(limit)
.toArray(callback)
},
findAllMessagesInRooms(roomIds, callback) {
db.messages
.find({
room_id: { $in: roomIds },
})
.toArray(callback)
},
deleteAllMessagesInRoom(roomId, callback) {
db.messages.deleteMany(
{
room_id: roomId,
},
callback
)
},
updateMessage(roomId, messageId, content, timestamp, callback) {
const query = this._ensureIdsAreObjectIds({
_id: messageId,
room_id: roomId,
})
db.messages.updateOne(
query,
{
$set: {
content,
edited_at: timestamp,
},
},
callback
)
},
deleteMessage(roomId, messageId, callback) {
const query = this._ensureIdsAreObjectIds({
_id: messageId,
room_id: roomId,
})
db.messages.deleteOne(query, callback)
},
_ensureIdsAreObjectIds(query) {
if (query.user_id && !(query.user_id instanceof ObjectId)) {
query.user_id = ObjectId(query.user_id)
}
if (query.room_id && !(query.room_id instanceof ObjectId)) {
query.room_id = ObjectId(query.room_id)
}
if (query._id && !(query._id instanceof ObjectId)) {
query._id = ObjectId(query._id)
}
return query
},
} }
;[ ;[
'createMessage', 'createMessage',

View file

@ -3,114 +3,120 @@ const { db, ObjectId } = require('../../mongodb')
const logger = require('@overleaf/logger') const logger = require('@overleaf/logger')
const metrics = require('@overleaf/metrics') const metrics = require('@overleaf/metrics')
module.exports = ThreadManager = { const GLOBAL_THREAD = 'GLOBAL'
GLOBAL_THREAD: 'GLOBAL',
findOrCreateThread(projectId, threadId, callback) { function findOrCreateThread(projectId, threadId, callback) {
let query, update let query, update
projectId = ObjectId(projectId.toString()) projectId = ObjectId(projectId.toString())
if (threadId !== ThreadManager.GLOBAL_THREAD) { if (threadId !== GLOBAL_THREAD) {
threadId = ObjectId(threadId.toString()) threadId = ObjectId(threadId.toString())
}
if (threadId === GLOBAL_THREAD) {
query = {
project_id: projectId,
thread_id: { $exists: false },
} }
update = {
if (threadId === ThreadManager.GLOBAL_THREAD) { project_id: projectId,
query = {
project_id: projectId,
thread_id: { $exists: false },
}
update = {
project_id: projectId,
}
} else {
query = {
project_id: projectId,
thread_id: threadId,
}
update = {
project_id: projectId,
thread_id: threadId,
}
} }
} else {
query = {
project_id: projectId,
thread_id: threadId,
}
update = {
project_id: projectId,
thread_id: threadId,
}
}
db.rooms.findOneAndUpdate( db.rooms.findOneAndUpdate(
query, query,
{ $set: update }, { $set: update },
{ upsert: true, returnDocument: 'after' }, { upsert: true, returnDocument: 'after' },
function (error, result) { function (error, result) {
if (error) {
return callback(error)
}
callback(null, result.value)
}
)
},
findAllThreadRooms(projectId, callback) {
db.rooms
.find(
{
project_id: ObjectId(projectId.toString()),
thread_id: { $exists: true },
},
{
thread_id: 1,
resolved: 1,
}
)
.toArray(callback)
},
resolveThread(projectId, threadId, userId, callback) {
db.rooms.updateOne(
{
project_id: ObjectId(projectId.toString()),
thread_id: ObjectId(threadId.toString()),
},
{
$set: {
resolved: {
user_id: userId,
ts: new Date(),
},
},
},
callback
)
},
reopenThread(projectId, threadId, callback) {
db.rooms.updateOne(
{
project_id: ObjectId(projectId.toString()),
thread_id: ObjectId(threadId.toString()),
},
{
$unset: {
resolved: true,
},
},
callback
)
},
deleteThread(projectId, threadId, callback) {
this.findOrCreateThread(projectId, threadId, function (error, room) {
if (error) { if (error) {
return callback(error) return callback(error)
} }
db.rooms.deleteOne( callback(null, result.value)
{ }
_id: room._id, )
}
function findAllThreadRooms(projectId, callback) {
db.rooms
.find(
{
project_id: ObjectId(projectId.toString()),
thread_id: { $exists: true },
},
{
thread_id: 1,
resolved: 1,
}
)
.toArray(callback)
}
function resolveThread(projectId, threadId, userId, callback) {
db.rooms.updateOne(
{
project_id: ObjectId(projectId.toString()),
thread_id: ObjectId(threadId.toString()),
},
{
$set: {
resolved: {
user_id: userId,
ts: new Date(),
}, },
function (error) { },
if (error) { },
return callback(error) callback
} )
callback(null, room._id) }
function reopenThread(projectId, threadId, callback) {
db.rooms.updateOne(
{
project_id: ObjectId(projectId.toString()),
thread_id: ObjectId(threadId.toString()),
},
{
$unset: {
resolved: true,
},
}
)
}
function deleteThread(projectId, threadId, callback) {
findOrCreateThread(projectId, threadId, function (error, room) {
if (error) {
return callback(error)
}
db.rooms.deleteOne(
{
_id: room._id,
},
function (error) {
if (error) {
return callback(error)
} }
) callback(null, room._id)
}) }
}, )
})
}
module.exports = ThreadManager = {
GLOBAL_THREAD,
findOrCreateThread,
findAllThreadRooms,
resolveThread,
reopenThread,
deleteThread,
} }
;[ ;[
'findOrCreateThread', 'findOrCreateThread',