Merge pull request #28077 from overleaf/em-promisify-chat-controller

Promisify ChatController

GitOrigin-RevId: c2af5f7bc24b1e6c682bb1dfd1146c3dcc90ae25
This commit is contained in:
Eric Mc Sween
2025-08-22 10:30:01 -04:00
committed by Copybot
parent 31bfb82d72
commit bae0a88dcb
2 changed files with 68 additions and 116 deletions

View File

@@ -1,84 +1,46 @@
/* eslint-disable
n/handle-callback-err,
max-len,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* 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
*/
let ChatController
const { expressify } = require('@overleaf/promise-utils')
const ChatApiHandler = require('./ChatApiHandler')
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
const SessionManager = require('../Authentication/SessionManager')
const UserInfoManager = require('../User/UserInfoManager')
const UserInfoController = require('../User/UserInfoController')
const ChatManager = require('./ChatManager')
const logger = require('@overleaf/logger')
module.exports = ChatController = {
sendMessage(req, res, next) {
const { project_id: projectId } = req.params
const { content, client_id: clientId } = req.body
const userId = SessionManager.getLoggedInUserId(req.session)
if (userId == null) {
const err = new Error('no logged-in user')
return next(err)
}
return ChatApiHandler.sendGlobalMessage(
projectId,
userId,
content,
function (err, message) {
if (err != null) {
return next(err)
}
return UserInfoManager.getPersonalInfo(
message.user_id,
function (err, user) {
if (err != null) {
return next(err)
}
message.user = UserInfoController.formatPersonalInfo(user)
message.clientId = clientId
EditorRealTimeController.emitToRoom(
projectId,
'new-chat-message',
message
)
return res.sendStatus(204)
}
)
}
)
},
async function sendMessage(req, res) {
const { project_id: projectId } = req.params
const { content, client_id: clientId } = req.body
const userId = SessionManager.getLoggedInUserId(req.session)
if (userId == null) {
throw new Error('no logged-in user')
}
getMessages(req, res, next) {
const { project_id: projectId } = req.params
const { query } = req
return ChatApiHandler.getGlobalMessages(
projectId,
query.limit,
query.before,
function (err, messages) {
if (err != null) {
return next(err)
}
return ChatManager.injectUserInfoIntoThreads(
{ global: { messages } },
function (err) {
if (err != null) {
return next(err)
}
return res.json(messages)
}
)
}
)
},
const message = await ChatApiHandler.promises.sendGlobalMessage(
projectId,
userId,
content
)
const user = await UserInfoManager.promises.getPersonalInfo(message.user_id)
message.user = UserInfoController.formatPersonalInfo(user)
message.clientId = clientId
EditorRealTimeController.emitToRoom(projectId, 'new-chat-message', message)
res.sendStatus(204)
}
async function getMessages(req, res) {
const { project_id: projectId } = req.params
const { query } = req
const messages = await ChatApiHandler.promises.getGlobalMessages(
projectId,
query.limit,
query.before
)
await ChatManager.promises.injectUserInfoIntoThreads({ global: { messages } })
res.json(messages)
}
module.exports = {
sendMessage: expressify(sendMessage),
getMessages: expressify(getMessages),
}

View File

@@ -1,21 +1,8 @@
/* eslint-disable
n/handle-callback-err,
max-len,
no-return-assign,
no-unused-vars,
*/
// 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
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const SandboxedModule = require('sandboxed-module')
const assert = require('assert')
const path = require('path')
const sinon = require('sinon')
const modulePath = path.join(
const MODULE_PATH = path.join(
__dirname,
'../../../../app/src/Features/Chat/ChatController'
)
@@ -24,21 +11,25 @@ describe('ChatController', function () {
beforeEach(function () {
this.user_id = 'mock-user-id'
this.settings = {}
this.ChatApiHandler = {}
this.ChatManager = {}
this.ChatApiHandler = { promises: {} }
this.ChatManager = { promises: {} }
this.EditorRealTimeController = { emitToRoom: sinon.stub() }
this.SessionManager = {
getLoggedInUserId: sinon.stub().returns(this.user_id),
}
this.ChatController = SandboxedModule.require(modulePath, {
this.UserInfoManager = {
promises: {},
}
this.UserInfoController = {}
this.ChatController = SandboxedModule.require(MODULE_PATH, {
requires: {
'@overleaf/settings': this.settings,
'./ChatApiHandler': this.ChatApiHandler,
'./ChatManager': this.ChatManager,
'../Editor/EditorRealTimeController': this.EditorRealTimeController,
'../Authentication/SessionManager': this.SessionManager,
'../User/UserInfoManager': (this.UserInfoManager = {}),
'../User/UserInfoController': (this.UserInfoController = {}),
'../User/UserInfoManager': this.UserInfoManager,
'../User/UserInfoController': this.UserInfoController,
},
})
this.req = {
@@ -54,25 +45,22 @@ describe('ChatController', function () {
})
describe('sendMessage', function () {
beforeEach(function () {
beforeEach(async function () {
this.req.body = { content: (this.content = 'message-content') }
this.UserInfoManager.getPersonalInfo = sinon
this.UserInfoManager.promises.getPersonalInfo = sinon
.stub()
.yields(null, (this.user = { unformatted: 'user' }))
.resolves((this.user = { unformatted: 'user' }))
this.UserInfoController.formatPersonalInfo = sinon
.stub()
.returns((this.formatted_user = { formatted: 'user' }))
this.ChatApiHandler.sendGlobalMessage = sinon
this.ChatApiHandler.promises.sendGlobalMessage = sinon
.stub()
.yields(
null,
(this.message = { mock: 'message', user_id: this.user_id })
)
return this.ChatController.sendMessage(this.req, this.res)
.resolves((this.message = { mock: 'message', user_id: this.user_id }))
await this.ChatController.sendMessage(this.req, this.res)
})
it('should look up the user', function () {
return this.UserInfoManager.getPersonalInfo
this.UserInfoManager.promises.getPersonalInfo
.calledWith(this.user_id)
.should.equal(true)
})
@@ -81,47 +69,49 @@ describe('ChatController', function () {
this.UserInfoController.formatPersonalInfo
.calledWith(this.user)
.should.equal(true)
return this.message.user.should.deep.equal(this.formatted_user)
this.message.user.should.deep.equal(this.formatted_user)
})
it('should tell the chat handler about the message', function () {
return this.ChatApiHandler.sendGlobalMessage
this.ChatApiHandler.promises.sendGlobalMessage
.calledWith(this.project_id, this.user_id, this.content)
.should.equal(true)
})
it('should tell the editor real time controller about the update with the data from the chat handler', function () {
return this.EditorRealTimeController.emitToRoom
this.EditorRealTimeController.emitToRoom
.calledWith(this.project_id, 'new-chat-message', this.message)
.should.equal(true)
})
it('should return a 204 status code', function () {
return this.res.sendStatus.calledWith(204).should.equal(true)
this.res.sendStatus.calledWith(204).should.equal(true)
})
})
describe('getMessages', function () {
beforeEach(function () {
beforeEach(async function () {
this.req.query = {
limit: (this.limit = '30'),
before: (this.before = '12345'),
}
this.ChatManager.injectUserInfoIntoThreads = sinon.stub().yields()
this.ChatApiHandler.getGlobalMessages = sinon
this.ChatManager.promises.injectUserInfoIntoThreads = sinon
.stub()
.yields(null, (this.messages = ['mock', 'messages']))
return this.ChatController.getMessages(this.req, this.res)
.resolves()
this.ChatApiHandler.promises.getGlobalMessages = sinon
.stub()
.resolves((this.messages = ['mock', 'messages']))
await this.ChatController.getMessages(this.req, this.res)
})
it('should ask the chat handler about the request', function () {
return this.ChatApiHandler.getGlobalMessages
this.ChatApiHandler.promises.getGlobalMessages
.calledWith(this.project_id, this.limit, this.before)
.should.equal(true)
})
it('should return the messages', function () {
return this.res.json.calledWith(this.messages).should.equal(true)
this.res.json.calledWith(this.messages).should.equal(true)
})
})
})