Просмотр исходного кода

Add endpoints for resolving and reopening threads

James Allen 9 лет назад
Родитель
Сommit
1ff920e1b5

+ 22 - 8
services/chat/app/coffee/Features/Messages/MessageFormatter.coffee

@@ -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

+ 19 - 4
services/chat/app/coffee/Features/Messages/MessageHttpController.coffee

@@ -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"

+ 13 - 2
services/chat/app/coffee/Features/Messages/MessageManager.coffee

@@ -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
 	

+ 23 - 2
services/chat/app/coffee/Features/Threads/ThreadManager.coffee

@@ -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

+ 5 - 6
services/chat/app/coffee/router.coffee

@@ -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")
 

+ 10 - 10
services/chat/test/acceptance/coffee/GettingMessagesTests.coffee

@@ -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"

+ 83 - 0
services/chat/test/acceptance/coffee/ResolvingAThreadTests.coffee

@@ -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()

+ 2 - 2
services/chat/test/acceptance/coffee/SendingAMessageTests.coffee

@@ -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", ->

+ 13 - 0
services/chat/test/acceptance/coffee/helpers/ChatClient.coffee

@@ -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