mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Create joinProject socket.io endpoint
This commit is contained in:
parent
fbf983c2ff
commit
02c0a3a867
10 changed files with 294 additions and 4 deletions
|
@ -1,5 +1,6 @@
|
|||
Metrics = require "metrics-sharelatex"
|
||||
logger = require "logger-sharelatex"
|
||||
WebsocketController = require "./WebsocketController"
|
||||
|
||||
module.exports = Router =
|
||||
configure: (app, io, session) ->
|
||||
|
@ -14,7 +15,11 @@ module.exports = Router =
|
|||
logger.log session: session, "got session"
|
||||
|
||||
user = session.user
|
||||
if !user?
|
||||
if !user? or !user._id?
|
||||
logger.log "terminating session without authenticated user"
|
||||
client.disconnect()
|
||||
return
|
||||
return
|
||||
|
||||
client.on "joinProject", (data = {}, callback) ->
|
||||
WebsocketController.joinProject(client, user, data.project_id, callback)
|
||||
|
21
services/real-time/app/coffee/WebApiManager.coffee
Normal file
21
services/real-time/app/coffee/WebApiManager.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
request = require "request"
|
||||
settings = require "settings-sharelatex"
|
||||
logger = require "logger-sharelatex"
|
||||
|
||||
module.exports = WebApiManager =
|
||||
joinProject: (project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
|
||||
logger.log {project_id, user_id}, "sending join project request to web"
|
||||
url = "#{settings.apis.web.url}/project/#{project_id}/join"
|
||||
request.post {
|
||||
url: url
|
||||
qs: {user_id}
|
||||
json: true
|
||||
jar: false
|
||||
}, (error, response, data) ->
|
||||
return callback(error) if error?
|
||||
if 200 <= response.statusCode < 300
|
||||
callback null, data?.project, data?.privilegeLevel
|
||||
else
|
||||
err = new Error("non-success status code from web: #{response.statusCode}")
|
||||
logger.error {err, project_id, user_id}, "error accessing web api"
|
||||
callback err
|
31
services/real-time/app/coffee/WebsocketController.coffee
Normal file
31
services/real-time/app/coffee/WebsocketController.coffee
Normal file
|
@ -0,0 +1,31 @@
|
|||
logger = require "logger-sharelatex"
|
||||
WebApiManager = require "./WebApiManager"
|
||||
|
||||
module.exports = WebsocketController =
|
||||
# If the protocol version changes when the client reconnects,
|
||||
# it will force a full refresh of the page. Useful for non-backwards
|
||||
# compatible protocol changes. Use only in extreme need.
|
||||
PROTOCOL_VERSION: 2
|
||||
|
||||
joinProject: (client, user, project_id, callback = (error, project, privilegeLevel, protocolVersion) ->) ->
|
||||
user_id = user?._id
|
||||
logger.log {user_id, project_id}, "user joining project"
|
||||
WebApiManager.joinProject project_id, user_id, (error, project, privilegeLevel) ->
|
||||
return callback(error) if error?
|
||||
|
||||
if !privilegeLevel or privilegeLevel == ""
|
||||
err = new Error("not authorized")
|
||||
logger.error {err, project_id, user_id}, "user is not authorized to join project"
|
||||
return callback(err)
|
||||
|
||||
client.set("user_id", user_id)
|
||||
client.set("project_id", project_id)
|
||||
client.set("owner_id", project?.owner?._id)
|
||||
client.set("first_name", user?.first_name)
|
||||
client.set("last_name", user?.last_name)
|
||||
client.set("email", user?.email)
|
||||
client.set("connected_time", new Date())
|
||||
client.set("signup_date", user?.signUpDate)
|
||||
client.set("login_count", user?.loginCount)
|
||||
|
||||
callback null, project, privilegeLevel, WebsocketController.PROTOCOL_VERSION
|
|
@ -10,6 +10,10 @@ module.exports =
|
|||
port: 3026
|
||||
host: "localhost"
|
||||
|
||||
apis:
|
||||
web:
|
||||
url: "http://localhost:3000"
|
||||
|
||||
security:
|
||||
sessionSecret: "secret-please-change"
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"request": "~2.34.0",
|
||||
"sandboxed-module": "~0.3.0",
|
||||
"sinon": "~1.5.2",
|
||||
"uid-safe": "^1.0.1"
|
||||
"uid-safe": "^1.0.1",
|
||||
"timekeeper": "0.0.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
chai = require("chai")
|
||||
expect = chai.expect
|
||||
chai.should()
|
||||
|
||||
RealTimeClient = require "./helpers/RealTimeClient"
|
||||
MockWebClient = require "./helpers/MockWebClient"
|
||||
|
||||
describe "joinProject", ->
|
||||
before (done) ->
|
||||
@user_id = "mock-user-id"
|
||||
@project_id = "mock-project-id"
|
||||
privileges = {}
|
||||
privileges[@user_id] = "owner"
|
||||
MockWebClient.createMockProject(@project_id, privileges, {
|
||||
name: "Test Project"
|
||||
})
|
||||
MockWebClient.run (error) =>
|
||||
throw error if error?
|
||||
RealTimeClient.setSession {
|
||||
user: { _id: @user_id }
|
||||
}, (error) =>
|
||||
throw error if error?
|
||||
@client = RealTimeClient.connect()
|
||||
@client.emit "joinProject", {
|
||||
project_id: @project_id
|
||||
}, (error, @project, @privilegeLevel, @protocolVersion) =>
|
||||
throw error if error?
|
||||
done()
|
||||
|
||||
it "should get the project from web", ->
|
||||
MockWebClient.joinProject
|
||||
.calledWith(@project_id, @user_id)
|
||||
.should.equal true
|
||||
|
||||
it "should return the project", ->
|
||||
@project.should.deep.equal {
|
||||
name: "Test Project"
|
||||
}
|
||||
|
||||
it "should return the privilege level", ->
|
||||
@privilegeLevel.should.equal "owner"
|
||||
|
||||
it "should return the protocolVersion", ->
|
||||
@protocolVersion.should.equal 2
|
|
@ -6,6 +6,7 @@ RealTimeClient = require "./helpers/RealTimeClient"
|
|||
describe "Session", ->
|
||||
describe "with an established session", ->
|
||||
beforeEach (done) ->
|
||||
@user_id = "mock-user-id"
|
||||
RealTimeClient.setSession {
|
||||
user: { _id: @user_id }
|
||||
}, (error) =>
|
||||
|
@ -33,7 +34,7 @@ describe "Session", ->
|
|||
@client.on "disconnect", () ->
|
||||
done()
|
||||
|
||||
describe "with a user set on the session", ->
|
||||
describe "without a valid user set on the session", ->
|
||||
beforeEach (done) ->
|
||||
RealTimeClient.setSession {
|
||||
foo: "bar"
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
sinon = require "sinon"
|
||||
express = require "express"
|
||||
|
||||
module.exports = MockWebClient =
|
||||
projects: {}
|
||||
privileges: {}
|
||||
|
||||
createMockProject: (project_id, privileges, project) ->
|
||||
MockWebClient.privileges[project_id] = privileges
|
||||
MockWebClient.projects[project_id] = project
|
||||
|
||||
joinProject: (project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
|
||||
callback(
|
||||
null,
|
||||
MockWebClient.projects[project_id],
|
||||
MockWebClient.privileges[project_id][user_id]
|
||||
)
|
||||
|
||||
joinProjectRequest: (req, res, next) ->
|
||||
{project_id} = req.params
|
||||
{user_id} = req.query
|
||||
MockWebClient.joinProject project_id, user_id, (error, project, privilegeLevel) ->
|
||||
return next(error) if error?
|
||||
res.json {
|
||||
project: project
|
||||
privilegeLevel: privilegeLevel
|
||||
}
|
||||
|
||||
running: false
|
||||
run: (callback = (error) ->) ->
|
||||
if MockWebClient.running
|
||||
return callback()
|
||||
app = express()
|
||||
app.post "/project/:project_id/join", MockWebClient.joinProjectRequest
|
||||
app.listen 3000, (error) ->
|
||||
MockWebClient.running = true
|
||||
callback(error)
|
||||
|
||||
sinon.spy MockWebClient, "joinProject"
|
|
@ -0,0 +1,55 @@
|
|||
chai = require('chai')
|
||||
should = chai.should()
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../app/js/WebApiManager.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe 'WebApiManager', ->
|
||||
beforeEach ->
|
||||
@project_id = "project-id-123"
|
||||
@user_id = "user-id-123"
|
||||
@callback = sinon.stub()
|
||||
@WebApiManager = SandboxedModule.require modulePath, requires:
|
||||
"request": @request = {}
|
||||
"settings-sharelatex": @settings =
|
||||
apis:
|
||||
web:
|
||||
url: "http://web.example.com"
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
|
||||
describe "joinProject", ->
|
||||
describe "successfully", ->
|
||||
beforeEach ->
|
||||
@response = {
|
||||
project: { name: "Test project" }
|
||||
privilegeLevel: "owner"
|
||||
}
|
||||
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @response)
|
||||
@WebApiManager.joinProject @project_id, @user_id, @callback
|
||||
|
||||
it "should send a request to web to join the project", ->
|
||||
@request.post
|
||||
.calledWith({
|
||||
url: "#{@settings.apis.web.url}/project/#{@project_id}/join"
|
||||
qs:
|
||||
user_id: @user_id
|
||||
json: true
|
||||
jar: false
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should return the project and privilegeLevel", ->
|
||||
@callback
|
||||
.calledWith(null, @response.project, @response.privilegeLevel)
|
||||
.should.equal true
|
||||
|
||||
describe "with an error from web", ->
|
||||
beforeEach ->
|
||||
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 500}, null)
|
||||
@WebApiManager.joinProject @project_id, @user_id, @callback
|
||||
|
||||
it "should call the callback with an error", ->
|
||||
@callback
|
||||
.calledWith(new Error("non-success code from web: 500"))
|
||||
.should.equal true
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
chai = require('chai')
|
||||
should = chai.should()
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../app/js/WebsocketController.js"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
tk = require "timekeeper"
|
||||
|
||||
describe 'WebsocketController', ->
|
||||
beforeEach ->
|
||||
tk.freeze(new Date())
|
||||
@project_id = "project-id-123"
|
||||
@user = {
|
||||
_id: "user-id-123"
|
||||
first_name: "James"
|
||||
last_name: "Allen"
|
||||
email: "james@example.com"
|
||||
signUpDate: new Date("2014-01-01")
|
||||
loginCount: 42
|
||||
}
|
||||
@callback = sinon.stub()
|
||||
@client =
|
||||
set: sinon.stub()
|
||||
join: sinon.stub()
|
||||
@WebsocketController = SandboxedModule.require modulePath, requires:
|
||||
"./WebApiManager": @WebApiManager = {}
|
||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||
|
||||
afterEach ->
|
||||
tk.reset()
|
||||
|
||||
describe "joinProject", ->
|
||||
describe "when authorised", ->
|
||||
beforeEach ->
|
||||
@project = {
|
||||
name: "Test Project"
|
||||
owner: {
|
||||
_id: @owner_id = "mock-owner-id-123"
|
||||
}
|
||||
}
|
||||
@privilegeLevel = "owner"
|
||||
@WebApiManager.joinProject = sinon.stub().callsArgWith(2, null, @project, @privilegeLevel)
|
||||
@WebsocketController.joinProject @client, @user, @project_id, @callback
|
||||
|
||||
it "should load the project from web", ->
|
||||
@WebApiManager.joinProject
|
||||
.calledWith(@project_id, @user._id)
|
||||
.should.equal true
|
||||
|
||||
it "should set the user's id on the client", ->
|
||||
@client.set.calledWith("user_id", @user._id).should.equal true
|
||||
|
||||
it "should set the user's email on the client", ->
|
||||
@client.set.calledWith("email", @user.email).should.equal true
|
||||
|
||||
it "should set the user's first_name on the client", ->
|
||||
@client.set.calledWith("first_name", @user.first_name).should.equal true
|
||||
|
||||
it "should set the user's last_name on the client", ->
|
||||
@client.set.calledWith("last_name", @user.last_name).should.equal true
|
||||
|
||||
it "should set the user's sign up date on the client", ->
|
||||
@client.set.calledWith("signup_date", @user.signUpDate).should.equal true
|
||||
|
||||
it "should set the user's login_count on the client", ->
|
||||
@client.set.calledWith("login_count", @user.loginCount).should.equal true
|
||||
|
||||
it "should set the connected time on the client", ->
|
||||
@client.set.calledWith("connected_time", new Date()).should.equal true
|
||||
|
||||
it "should set the project_id on the client", ->
|
||||
@client.set.calledWith("project_id", @project_id).should.equal true
|
||||
|
||||
it "should set the project owner id on the client", ->
|
||||
@client.set.calledWith("owner_id", @owner_id).should.equal true
|
||||
|
||||
it "should call the callback with the project, privilegeLevel and protocolVersion", ->
|
||||
@callback
|
||||
.calledWith(null, @project, @privilegeLevel, @WebsocketController.PROTOCOL_VERSION)
|
||||
.should.equal true
|
||||
|
||||
describe "when not authorized", ->
|
||||
beforeEach ->
|
||||
@WebApiManager.joinProject = sinon.stub().callsArgWith(2, null, null, null)
|
||||
@WebsocketController.joinProject @client, @user, @project_id, @callback
|
||||
|
||||
it "should return an error", ->
|
||||
@callback
|
||||
.calledWith(new Error("not authorized"))
|
||||
.should.equal true
|
Loading…
Reference in a new issue