Add endpoints for resolving and reopening threads

This commit is contained in:
James Allen 2017-01-04 14:51:08 +01:00
parent cb88e1e41b
commit 1ff920e1b5
9 changed files with 190 additions and 34 deletions

View file

@ -16,17 +16,31 @@ module.exports = MessageFormatter =
(@formatMessageForClientSide(message) for message in messages)
groupMessagesByThreads: (rooms, messages) ->
room_id_to_thread_id = {}
rooms_by_id = {}
for room in rooms
room_id_to_thread_id[room._id.toString()] = room.thread_id.toString()
rooms_by_id[room._id.toString()] = room
threads = {}
getThread = (room) ->
thread_id = room.thread_id.toString()
if threads[thread_id]?
return threads[thread_id]
else
thread = { messages: [] }
if room.resolved?
thread.resolved = true
thread.resolved_at = room.resolved.ts
thread.resolved_by_user = UserFormatter.formatUserForClientSide(room.resolved.user)
threads[thread_id] = thread
return thread
for message in messages
thread_id = room_id_to_thread_id[message.room_id.toString()]
threads[thread_id] ?= []
threads[thread_id].push MessageFormatter.formatMessageForClientSide(message)
room = rooms_by_id[message.room_id.toString()]
if room?
thread = getThread(room)
thread.messages.push MessageFormatter.formatMessageForClientSide(message)
for thread_id, messages of threads
messages.sort (a,b) -> a.timestamp - b.timestamp
for thread_id, thread of threads
thread.messages.sort (a,b) -> a.timestamp - b.timestamp
return threads

View file

@ -25,10 +25,25 @@ module.exports = MessageHttpController =
room_ids = rooms.map (r) -> r._id
MessageManager.findAllMessagesInRooms room_ids, (error, messages) ->
return next(error) if error?
MessageManager.populateMessagesWithUsers messages, (error, messages) ->
MessageManager.populateMessagesAndRoomsWithUsers messages, rooms, (error) ->
return next(error) if error?
threads = MessageFormatter.groupMessagesByThreads rooms, messages
res.json threads
resolveThread: (req, res, next) ->
{project_id, thread_id} = req.params
{user_id} = req.body
logger.log {user_id, project_id, thread_id}, "marking thread as resolved"
ThreadManager.resolveThread project_id, thread_id, user_id, (error) ->
return next(error) if error?
res.send 204 # No content
reopenThread: (req, res, next) ->
{project_id, thread_id} = req.params
logger.log {project_id, thread_id}, "reopening thread"
ThreadManager.reopenThread project_id, thread_id, (error) ->
return next(error) if error?
res.send 204 # No content
_sendMessage: (client_thread_id, req, res, next) ->
{user_id, content} = req?.body
@ -40,9 +55,9 @@ module.exports = MessageHttpController =
return next(error) if error?
MessageManager.createMessage thread._id, user_id, content, Date.now(), (error, message) ->
return next(error) if error?
MessageManager.populateMessagesWithUsers [message], (error, messages) ->
MessageManager.populateMessagesAndRoomsWithUsers [message], [], (error) ->
return next(error) if error?
message = MessageFormatter.formatMessageForClientSide(messages[0])
message = MessageFormatter.formatMessageForClientSide(message)
message.room =
id: project_id
res.send(201, message)
@ -64,7 +79,7 @@ module.exports = MessageHttpController =
logger.log {limit, before, project_id, client_thread_id, thread_object_id}, "found or created thread"
MessageManager.getMessages thread_object_id, limit, before, (error, messages) ->
return next(error) if error?
MessageManager.populateMessagesWithUsers messages, (error, messages) ->
MessageManager.populateMessagesAndRoomsWithUsers messages, [], (error) ->
return next(error) if error?
messages = MessageFormatter.formatMessagesForClientSide messages
logger.log {project_id, messages}, "got messages"

View file

@ -28,7 +28,7 @@ module.exports = MessageManager =
room_id: { $in: room_ids }
}, callback
populateMessagesWithUsers: (messages, callback = (error, messages) ->) ->
populateMessagesAndRoomsWithUsers: (messages, rooms, callback = (error) ->) ->
jobs = new Array()
userCache = {}
@ -48,7 +48,18 @@ module.exports = MessageManager =
return callback(error) if error?
delete message.user_id
message.user = user
callback(null, message)
callback()
for room in rooms
do (room) ->
if !room?.resolved?.user_id?
return
jobs.push (callback) ->
getUserDetails room.resolved.user_id.toString(), (error, user) ->
return callback(error) if error?
delete room.resolved.user_id
room.resolved.user = user
callback()
async.series jobs, callback

View file

@ -27,8 +27,29 @@ module.exports = ThreadManager =
project_id: ObjectId(project_id.toString())
thread_id: { $exists: true }
}, {
thread_id: 1
thread_id: 1,
resolved: 1
}, callback
resolveThread: (project_id, thread_id, user_id, callback = (error) ->) ->
db.rooms.update {
project_id: ObjectId(project_id.toString())
thread_id: ObjectId(thread_id.toString())
}, {
$set: {
resolved: {
user_id: user_id
ts: new Date()
}
}
}, callback
reopenThread: (project_id, thread_id, callback = (error) ->) ->
db.rooms.update {
project_id: ObjectId(project_id.toString())
thread_id: ObjectId(thread_id.toString())
}, {
$unset: {
resolved: true
}
}, callback

View file

@ -24,15 +24,14 @@ module.exports = Router =
app.post "/project/:project_id/thread/:thread_id/messages", MessageHttpController.sendThreadMessage
app.get "/project/:project_id/threads", MessageHttpController.getAllThreads
# app.get "/project/:project_id/thread", MessageHttpController.getAllThreadMessages
#
# app.post "/project/:project_id/thread/:thread_id/messages/:message_id/edit", MessageHttpController.editMessage
# app.del "/project/:project_id/thread/:thread_id/messages/:message_id", MessageHttpController.deleteMessage
#
# app.post "/project/:project_id/thread/:thread_id/resolve", MessageHttpController.resolveThread
# app.post "/project/:project_id/thread/:thread_id/reopen", MessageHttpController.reopenThread
app.post "/project/:project_id/thread/:thread_id/resolve", MessageHttpController.resolveThread
app.post "/project/:project_id/thread/:thread_id/reopen", MessageHttpController.reopenThread
# app.del "/project/:project_id/thread/:thread_id", MessageHttpController.deleteThread
#
app.get "/status", (req, res, next) ->
res.send("chat is alive")

View file

@ -71,20 +71,20 @@ describe "Getting messages", ->
ChatClient.getThreads @project_id, (error, response, threads) =>
expect(Object.keys(threads).length).to.equal 2
thread1 = threads[@thread_id1]
expect(thread1.length).to.equal 2
expect(thread1.messages.length).to.equal 2
thread2 = threads[@thread_id2]
expect(thread2.length).to.equal 2
expect(thread2.messages.length).to.equal 2
expect(thread1[0].content).to.equal "one"
expect(thread1[0].user).to.deep.equal {
expect(thread1.messages[0].content).to.equal "one"
expect(thread1.messages[0].user).to.deep.equal {
id: @user_id1
first_name: "Jane"
last_name: "Smith"
email: "jane@example.com"
gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("jane@example.com").digest("hex")}"
}
expect(thread1[1].content).to.equal "three"
expect(thread1[1].user).to.deep.equal {
expect(thread1.messages[1].content).to.equal "three"
expect(thread1.messages[1].user).to.deep.equal {
id: @user_id1
first_name: "Jane"
last_name: "Smith"
@ -92,16 +92,16 @@ describe "Getting messages", ->
gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("jane@example.com").digest("hex")}"
}
expect(thread2[0].content).to.equal "two"
expect(thread2[0].user).to.deep.equal {
expect(thread2.messages[0].content).to.equal "two"
expect(thread2.messages[0].user).to.deep.equal {
id: @user_id2
first_name: "John"
last_name: "Doe"
email: "john@example.com"
gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("john@example.com").digest("hex")}"
}
expect(thread2[1].content).to.equal "four"
expect(thread2[1].user).to.deep.equal {
expect(thread2.messages[1].content).to.equal "four"
expect(thread2.messages[1].user).to.deep.equal {
id: @user_id2
first_name: "John"
last_name: "Doe"

View file

@ -0,0 +1,83 @@
{ObjectId} = require "../../../app/js/mongojs"
expect = require("chai").expect
crypto = require "crypto"
MockWebApi = require "./helpers/MockWebApi"
ChatClient = require "./helpers/ChatClient"
describe "Resolving a thread", ->
before ->
@project_id = ObjectId().toString()
@user_id = ObjectId().toString()
MockWebApi.addUser @user_id, @user = {
id: @user_id
first_name: "Jane"
last_name: "Smith"
email: "jane@example.com"
}
describe "with a resolved thread", ->
before (done) ->
@thread_id = ObjectId().toString()
@content = "resolved message"
ChatClient.sendMessage @project_id, @thread_id, @user_id, @content, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 201
ChatClient.resolveThread @project_id, @thread_id, @user_id, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 204
done()
it "should then list the thread as resolved", (done) ->
ChatClient.getThreads @project_id, (error, response, threads) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 200
expect(threads[@thread_id].resolved).to.equal true
expect(threads[@thread_id].resolved_by_user).to.deep.equal {
id: @user_id
first_name: "Jane"
last_name: "Smith"
email: "jane@example.com"
gravatar_url: "//www.gravatar.com/avatar/#{crypto.createHash("md5").update("jane@example.com").digest("hex")}"
}
resolved_at = new Date(threads[@thread_id].resolved_at)
expect(new Date() - resolved_at).to.be.below 1000
done()
describe "when a thread is not resolved", ->
before (done) ->
@thread_id = ObjectId().toString()
@content = "open message"
ChatClient.sendMessage @project_id, @thread_id, @user_id, @content, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 201
done()
it "should not list the thread as resolved", (done) ->
ChatClient.getThreads @project_id, (error, response, threads) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 200
expect(threads[@thread_id].resolved).to.be.undefined
done()
describe "when a thread is resolved then reopened", ->
before (done) ->
@thread_id = ObjectId().toString()
@content = "resolved message"
ChatClient.sendMessage @project_id, @thread_id, @user_id, @content, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 201
ChatClient.resolveThread @project_id, @thread_id, @user_id, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 204
ChatClient.reopenThread @project_id, @thread_id, (error, response, body) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 204
done()
it "should not list the thread as resolved", (done) ->
ChatClient.getThreads @project_id, (error, response, threads) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 200
expect(threads[@thread_id].resolved).to.be.undefined
done()

View file

@ -50,8 +50,8 @@ describe "Sending a message", ->
ChatClient.getThreads @project_id, (error, response, threads) =>
expect(error).to.be.null
expect(response.statusCode).to.equal 200
expect(threads[@thread_id].length).to.equal 1
expect(threads[@thread_id][0].content).to.equal @content
expect(threads[@thread_id].messages.length).to.equal 1
expect(threads[@thread_id].messages[0].content).to.equal @content
done()
describe "with a malformed user_id", ->

View file

@ -27,4 +27,17 @@ module.exports =
request.get {
url: "/project/#{project_id}/threads",
json: true
}, callback
resolveThread: (project_id, thread_id, user_id, callback) ->
request.post {
url: "/project/#{project_id}/thread/#{thread_id}/resolve",
json: {
user_id: user_id
}
}, callback
reopenThread: (project_id, thread_id, callback) ->
request.post {
url: "/project/#{project_id}/thread/#{thread_id}/reopen",
}, callback