diff --git a/services/real-time/app/coffee/Router.coffee b/services/real-time/app/coffee/Router.coffee index 06cd7bb7a0..ac0d06f8c3 100644 --- a/services/real-time/app/coffee/Router.coffee +++ b/services/real-time/app/coffee/Router.coffee @@ -62,4 +62,13 @@ module.exports = Router = return callback {message: "Something went wrong"} else callback(null, args...) - \ No newline at end of file + + client.on "leaveDoc", (doc_id, callback) -> + WebsocketController.leaveDoc client, doc_id, (err, args...) -> + if err? + Router._getClientData client, (_, client) -> + logger.error {err, client, doc_id}, "server side error in leaveDoc" + # Don't return raw error to prevent leaking server side info + return callback {message: "Something went wrong"} + else + callback(null, args...) \ No newline at end of file diff --git a/services/real-time/app/coffee/WebsocketController.coffee b/services/real-time/app/coffee/WebsocketController.coffee index f409f39375..2f57aa4005 100644 --- a/services/real-time/app/coffee/WebsocketController.coffee +++ b/services/real-time/app/coffee/WebsocketController.coffee @@ -34,9 +34,8 @@ module.exports = WebsocketController = callback null, project, privilegeLevel, WebsocketController.PROTOCOL_VERSION joinDoc: (client, doc_id, fromVersion = -1, callback = (error, doclines, version, ops) ->) -> - client.get "user_id", (error, user_id) -> - client.get "project_id", (error, project_id) -> - logger.log {user_id, project_id, doc_id, fromVersion, client_id: client.id}, "client joining doc" + WebsocketController._getClientData client, (error, {client_id, user_id, project_id}) -> + logger.log {user_id, project_id, doc_id, fromVersion, client_id}, "client joining doc" AuthorizationManager.assertClientCanViewProject client, (error) -> return callback(error) if error? @@ -57,4 +56,16 @@ module.exports = WebsocketController = escapedLines.push line client.join(doc_id) callback null, escapedLines, version, ops + + leaveDoc: (client, doc_id, callback = (error) ->) -> + WebsocketController._getClientData client, (error, {client_id, user_id, project_id}) -> + logger.log {user_id, project_id, doc_id, client_id}, "client leaving doc" + client.leave doc_id + callback() + + # Only used in logging. + _getClientData: (client, callback = (error, data) ->) -> + client.get "user_id", (error, user_id) -> + client.get "project_id", (error, project_id) -> + callback null, {client_id: client.id, project_id, user_id} \ No newline at end of file diff --git a/services/real-time/test/unit/coffee/WebsocketControllerTests.coffee b/services/real-time/test/unit/coffee/WebsocketControllerTests.coffee index eea0e7e1f0..3be89d26e9 100644 --- a/services/real-time/test/unit/coffee/WebsocketControllerTests.coffee +++ b/services/real-time/test/unit/coffee/WebsocketControllerTests.coffee @@ -23,6 +23,7 @@ describe 'WebsocketController', -> set: sinon.stub() get: (param, cb) -> cb null, @params[param] join: sinon.stub() + leave: sinon.stub() @WebsocketController = SandboxedModule.require modulePath, requires: "./WebApiManager": @WebApiManager = {} "./AuthorizationManager": @AuthorizationManager = {} @@ -153,4 +154,17 @@ describe 'WebsocketController', -> @callback.calledWith(@err).should.equal true it "should not call the DocumentUpdaterManager", -> - @DocumentUpdaterManager.getDocument.called.should.equal false \ No newline at end of file + @DocumentUpdaterManager.getDocument.called.should.equal false + + describe "leaveDoc", -> + beforeEach -> + @doc_id = "doc-id-123" + @client.params.project_id = @project_id + @WebsocketController.leaveDoc @client, @doc_id, @callback + + it "should remove the client from the doc_id room", -> + @client.leave + .calledWith(@doc_id).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true \ No newline at end of file