mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #18637 from overleaf/mj-chat-duplicate-threads
[chat] Add endpoint for duplicating comment threads GitOrigin-RevId: 0b3fb1b836150ccb6d213ab2bba6ce7ff6c69b4a
This commit is contained in:
parent
f0eba8e742
commit
304f572f9c
6 changed files with 199 additions and 0 deletions
|
@ -82,6 +82,10 @@ export async function destroyProject(context) {
|
|||
return await callMessageHttpController(context, _destroyProject)
|
||||
}
|
||||
|
||||
export async function duplicateCommentThreads(context) {
|
||||
return await callMessageHttpController(context, _duplicateCommentThreads)
|
||||
}
|
||||
|
||||
export async function getStatus(context) {
|
||||
const message = 'chat is alive'
|
||||
context.res.status(200).setBody(message)
|
||||
|
@ -254,3 +258,29 @@ async function _getMessages(clientThreadId, req, res) {
|
|||
logger.debug({ projectId, messages }, 'got messages')
|
||||
res.status(200).setBody(messages)
|
||||
}
|
||||
|
||||
async function _duplicateCommentThreads(req, res) {
|
||||
const { projectId } = req.params
|
||||
const { threads } = req.body
|
||||
const result = {}
|
||||
for (const id of threads) {
|
||||
logger.debug({ projectId, thread: id }, 'duplicating thread')
|
||||
try {
|
||||
const { oldRoom, newRoom } = await ThreadManager.duplicateThread(
|
||||
projectId,
|
||||
id
|
||||
)
|
||||
await MessageManager.duplicateRoomToOtherRoom(oldRoom._id, newRoom._id)
|
||||
result[id] = { duplicateId: newRoom.thread_id }
|
||||
} catch (error) {
|
||||
if (error instanceof ThreadManager.MissingThreadError) {
|
||||
// Expected error when the comment has been deleted prior to duplication
|
||||
result[id] = { error: 'not found' }
|
||||
} else {
|
||||
logger.err({ error }, 'error duplicating thread')
|
||||
result[id] = { error: 'unknown' }
|
||||
}
|
||||
}
|
||||
}
|
||||
res.json({ newThreads: result })
|
||||
}
|
||||
|
|
|
@ -89,3 +89,16 @@ function _ensureIdsAreObjectIds(query) {
|
|||
}
|
||||
return query
|
||||
}
|
||||
|
||||
export async function duplicateRoomToOtherRoom(sourceRoomId, targetRoomId) {
|
||||
const sourceMessages = await findAllMessagesInRooms([sourceRoomId])
|
||||
const targetMessages = sourceMessages.map(comment => {
|
||||
return _ensureIdsAreObjectIds({
|
||||
room_id: targetRoomId,
|
||||
content: comment.content,
|
||||
timestamp: comment.timestamp,
|
||||
user_id: comment.user_id,
|
||||
})
|
||||
})
|
||||
await db.messages.insertMany(targetMessages)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { db, ObjectId } from '../../mongodb.js'
|
||||
|
||||
export class MissingThreadError extends Error {}
|
||||
|
||||
export const GLOBAL_THREAD = 'GLOBAL'
|
||||
|
||||
export async function findOrCreateThread(projectId, threadId) {
|
||||
|
@ -124,3 +126,23 @@ export async function getResolvedThreadIds(projectId) {
|
|||
.toArray()
|
||||
return resolvedThreadIds
|
||||
}
|
||||
|
||||
export async function duplicateThread(projectId, threadId) {
|
||||
const room = await db.rooms.findOne({
|
||||
project_id: new ObjectId(projectId),
|
||||
thread_id: new ObjectId(threadId),
|
||||
})
|
||||
if (!room) {
|
||||
throw new MissingThreadError('Trying to duplicate a non-existent thread')
|
||||
}
|
||||
const newRoom = {
|
||||
project_id: room.project_id,
|
||||
thread_id: new ObjectId(),
|
||||
}
|
||||
if (room.resolved) {
|
||||
newRoom.resolved = room.resolved
|
||||
}
|
||||
const confirmation = await db.rooms.insertOne(newRoom)
|
||||
newRoom._id = confirmation.insertedId
|
||||
return { oldRoom: room, newRoom }
|
||||
}
|
||||
|
|
|
@ -303,6 +303,37 @@ paths:
|
|||
description: chat is alive
|
||||
operationId: getStatus
|
||||
description: Check that the Chat service is alive
|
||||
'/project/{projectId}/duplicate-comment-threads':
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
name: projectId
|
||||
in: path
|
||||
required: true
|
||||
post:
|
||||
summary: Duplicate comment threads
|
||||
operationId: duplicateCommentThreads
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
threads:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
newThreads:
|
||||
type: object
|
||||
description: Mapping of old thread ids to their duplicated thread ids
|
||||
description: Duplicate a list of comment threads
|
||||
components:
|
||||
schemas:
|
||||
Message:
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import { ObjectId } from '../../../app/js/mongodb.js'
|
||||
import { expect } from 'chai'
|
||||
|
||||
import * as ChatClient from './helpers/ChatClient.js'
|
||||
import * as ChatApp from './helpers/ChatApp.js'
|
||||
|
||||
const user1Id = new ObjectId().toString()
|
||||
const user2Id = new ObjectId().toString()
|
||||
|
||||
async function createCommentThread(projectId, threadId = new ObjectId()) {
|
||||
const { response: response1 } = await ChatClient.sendMessage(
|
||||
projectId,
|
||||
threadId.toString(),
|
||||
user1Id,
|
||||
'message 1'
|
||||
)
|
||||
expect(response1.statusCode).to.equal(201)
|
||||
const { response: response2 } = await ChatClient.sendMessage(
|
||||
projectId,
|
||||
threadId,
|
||||
user2Id,
|
||||
'message 2'
|
||||
)
|
||||
expect(response2.statusCode).to.equal(201)
|
||||
return threadId.toString()
|
||||
}
|
||||
|
||||
describe('Cloning comment threads', async function () {
|
||||
const projectId = new ObjectId().toString()
|
||||
|
||||
before(async function () {
|
||||
await ChatApp.ensureRunning()
|
||||
this.thread1Id = await createCommentThread(projectId)
|
||||
this.thread2Id = await createCommentThread(projectId)
|
||||
this.thread3Id = await createCommentThread(projectId)
|
||||
})
|
||||
|
||||
describe('with non-orphaned threads', async function () {
|
||||
before(async function () {
|
||||
const {
|
||||
response: { body: result, statusCode },
|
||||
} = await ChatClient.duplicateCommentThreads(projectId, [this.thread3Id])
|
||||
this.result = result
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(this.result).to.have.property('newThreads')
|
||||
this.newThreadId = this.result.newThreads[this.thread3Id].duplicateId
|
||||
})
|
||||
|
||||
it('should duplicate threads', function () {
|
||||
expect(this.result.newThreads).to.have.property(this.thread3Id)
|
||||
expect(this.result.newThreads[this.thread3Id]).to.have.property(
|
||||
'duplicateId'
|
||||
)
|
||||
expect(this.result.newThreads[this.thread3Id].duplicateId).to.not.equal(
|
||||
this.thread3Id
|
||||
)
|
||||
})
|
||||
|
||||
it('should not duplicate other threads threads', function () {
|
||||
expect(this.result.newThreads).to.not.have.property(this.thread1Id)
|
||||
expect(this.result.newThreads).to.not.have.property(this.thread2Id)
|
||||
})
|
||||
|
||||
it('should duplicate the messages in the thread', async function () {
|
||||
const {
|
||||
response: { body: threads },
|
||||
} = await ChatClient.getThreads(projectId)
|
||||
function ignoreId(comment) {
|
||||
return {
|
||||
...comment,
|
||||
id: undefined,
|
||||
}
|
||||
}
|
||||
expect(threads[this.thread3Id].messages.map(ignoreId)).to.deep.equal(
|
||||
threads[this.newThreadId].messages.map(ignoreId)
|
||||
)
|
||||
})
|
||||
|
||||
it('should have two separate unlinked threads', async function () {
|
||||
await ChatClient.sendMessage(
|
||||
projectId,
|
||||
this.newThreadId,
|
||||
user1Id,
|
||||
'third message'
|
||||
)
|
||||
const {
|
||||
response: { body: threads },
|
||||
} = await ChatClient.getThreads(projectId)
|
||||
expect(threads[this.thread3Id].messages.length).to.equal(2)
|
||||
expect(threads[this.newThreadId].messages.length).to.equal(3)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -144,3 +144,13 @@ export async function destroyProject(projectId) {
|
|||
url: `/project/${projectId}`,
|
||||
})
|
||||
}
|
||||
|
||||
export async function duplicateCommentThreads(projectId, threads) {
|
||||
return await asyncRequest({
|
||||
method: 'post',
|
||||
url: `/project/${projectId}/duplicate-comment-threads`,
|
||||
json: {
|
||||
threads,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue