Merge pull request #1303 from sharelatex/project-import-refactor
Project import refactor GitOrigin-RevId: b4edc35cd4a14db0f4e6746d3da28cd94c351117
This commit is contained in:
committed by
sharelatex
parent
0c1e754736
commit
7112e65dc5
@@ -141,8 +141,11 @@ module.exports = MockV1Api =
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockV1Api:", error.message
|
||||
process.exit(1)
|
||||
|
||||
app.get '/api/v1/sharelatex/docs/:token/info', (req, res, next) =>
|
||||
res.json { allow: true, exported: false }
|
||||
|
||||
app.get '/api/v1/sharelatex/docs/:token/is_published', (req, res, next) =>
|
||||
res.json { allow: true }
|
||||
|
||||
app.get '/api/v1/sharelatex/users/:user_id/docs/:token/info', (req, res, next) =>
|
||||
res.json { exported: false }
|
||||
|
||||
MockV1Api.run()
|
||||
|
||||
@@ -29,12 +29,17 @@ describe "TokenAccessController", ->
|
||||
'../Project/ProjectController': @ProjectController = {}
|
||||
'../Authentication/AuthenticationController': @AuthenticationController = {}
|
||||
'./TokenAccessHandler': @TokenAccessHandler = {
|
||||
getV1DocInfo: sinon.stub().yields(null, {
|
||||
getV1DocPublishedInfo: sinon.stub().yields(null, {
|
||||
allow: true
|
||||
})
|
||||
getV1DocInfo: sinon.stub().yields(null, {
|
||||
exists: true
|
||||
exported: false
|
||||
})
|
||||
}
|
||||
'../../infrastructure/Features': @Features = {
|
||||
hasFeature: sinon.stub().returns(false)
|
||||
}
|
||||
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
|
||||
'settings-sharelatex': {
|
||||
overleaf:
|
||||
@@ -250,6 +255,7 @@ describe "TokenAccessController", ->
|
||||
@req.url = '/123abc'
|
||||
@res = new MockResponse()
|
||||
@res.redirect = sinon.stub()
|
||||
@res.render = sinon.stub()
|
||||
@next = sinon.stub()
|
||||
@req.params['read_and_write_token'] = '123abc'
|
||||
@TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
|
||||
@@ -257,8 +263,11 @@ describe "TokenAccessController", ->
|
||||
|
||||
describe 'when project was not exported from v1', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.checkV1ProjectExported = sinon.stub()
|
||||
.callsArgWith(1, null, false)
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
})
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should redirect to v1', (done) ->
|
||||
@@ -269,14 +278,100 @@ describe "TokenAccessController", ->
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when project was not exported from v1 but forcing import to v2', ->
|
||||
beforeEach ->
|
||||
@Features.hasFeature.returns(true)
|
||||
|
||||
describe 'with project name', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: true
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page with name', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: '123abc'
|
||||
name: 'A title'
|
||||
hasOwner: true
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'with project owner', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: true
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: '123abc',
|
||||
hasOwner: true
|
||||
name: 'A title'
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'without project owner', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: false
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: '123abc',
|
||||
hasOwner: false
|
||||
name: 'A title'
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'with anonymous user', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should render anonymous import status page', (done) ->
|
||||
expect(@res.render.callCount).to.equal 1
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{ loginRedirect: '/123abc' }
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when project was exported from v1', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.checkV1ProjectExported = sinon.stub()
|
||||
.callsArgWith(1, null, false)
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: true
|
||||
})
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should call next with a not-found error', (done) ->
|
||||
expect(@next.callCount).to.equal 0
|
||||
expect(@next.callCount).to.equal 1
|
||||
done()
|
||||
|
||||
describe 'when token access is off, but user has higher access anyway', ->
|
||||
@@ -426,10 +521,8 @@ describe "TokenAccessController", ->
|
||||
@next = sinon.stub()
|
||||
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
|
||||
.callsArgWith(1, null, @project, true)
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
@TokenAccessHandler.getV1DocPublishedInfo = sinon.stub().yields(null, {
|
||||
allow: false
|
||||
exists: true
|
||||
exported: false
|
||||
published_path: 'doc-url'
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
@@ -565,6 +658,83 @@ describe "TokenAccessController", ->
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when project was not exported from v1 but forcing import to v2', ->
|
||||
beforeEach ->
|
||||
@Features.hasFeature.returns(true)
|
||||
@req = new MockRequest()
|
||||
@res = new MockResponse()
|
||||
@res.render = sinon.stub()
|
||||
@next = sinon.stub()
|
||||
@req.params['read_only_token'] = 'abcd'
|
||||
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
|
||||
.callsArgWith(1, null, null, false)
|
||||
|
||||
describe 'with project name', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: true
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page with name', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: 'abcd'
|
||||
name: 'A title'
|
||||
hasOwner: true
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'with project owner', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: true
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: 'abcd',
|
||||
hasOwner: true
|
||||
name: 'A title'
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'without project owner', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: false
|
||||
has_owner: false
|
||||
name: 'A title'
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should render v2-import page', (done) ->
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{
|
||||
projectId: 'abcd',
|
||||
hasOwner: false
|
||||
name: 'A title'
|
||||
}
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when project was exported from v1', ->
|
||||
beforeEach ->
|
||||
@req = new MockRequest()
|
||||
@@ -811,46 +981,53 @@ describe "TokenAccessController", ->
|
||||
@res.redirect = sinon.stub()
|
||||
@next = sinon.stub()
|
||||
@req.params['read_only_token'] = @readOnlyToken
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
|
||||
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
|
||||
.callsArgWith(1, null, null)
|
||||
@TokenAccessHandler.checkV1ProjectExported = sinon.stub()
|
||||
.callsArgWith(1, null, false)
|
||||
@TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
|
||||
.callsArgWith(2, null)
|
||||
@ProjectController.loadEditor = sinon.stub()
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should try to find a project with this token', (done) ->
|
||||
expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
|
||||
.to.equal 1
|
||||
expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
|
||||
.to.equal true
|
||||
done()
|
||||
describe 'when project does not exist', ->
|
||||
beforeEach ->
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should not give the user session read-only access', (done) ->
|
||||
expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
|
||||
.to.equal 0
|
||||
done()
|
||||
it 'should try to find a project with this token', (done) ->
|
||||
expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
|
||||
.to.equal 1
|
||||
expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
|
||||
.to.equal true
|
||||
done()
|
||||
|
||||
it 'should not pass control to loadEditor', (done) ->
|
||||
expect(@ProjectController.loadEditor.callCount).to.equal 0
|
||||
expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
|
||||
done()
|
||||
it 'should not give the user session read-only access', (done) ->
|
||||
expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
|
||||
.to.equal 0
|
||||
done()
|
||||
|
||||
it 'should not add the user to the project with read-only access', (done) ->
|
||||
expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
|
||||
.to.equal 0
|
||||
done()
|
||||
it 'should not add the user to the project with read-only access', (done) ->
|
||||
expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
|
||||
.to.equal 0
|
||||
done()
|
||||
|
||||
describe 'when project was exported to v2', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
allow: true
|
||||
exists: true
|
||||
exported: true
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should call next with not found error', (done) ->
|
||||
expect(@next.callCount).to.equal 1
|
||||
expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
|
||||
done()
|
||||
|
||||
describe 'when project was not exported to v2', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
exists: true
|
||||
exported: false
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should redirect to v1', (done) ->
|
||||
expect(@res.redirect.callCount).to.equal 1
|
||||
expect(@res.redirect.calledWith(
|
||||
@@ -859,3 +1036,44 @@ describe "TokenAccessController", ->
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'anonymous user', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
|
||||
|
||||
describe 'when project was not exported to v2', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
exists: true
|
||||
exported: false
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should redirect to v1', (done) ->
|
||||
expect(@res.redirect.callCount).to.equal 1
|
||||
expect(@res.redirect.calledWith(
|
||||
302,
|
||||
"/sign_in_to_v1?return_to=/read/#{@readOnlyToken}"
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
describe 'force-import-to-v2 flag is on', ->
|
||||
beforeEach ->
|
||||
@res.render = sinon.stub()
|
||||
@Features.hasFeature.returns(true)
|
||||
|
||||
describe 'when project was not exported to v2', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
|
||||
exists: true
|
||||
exported: false
|
||||
})
|
||||
@TokenAccessController.readOnlyToken @req, @res, @next
|
||||
|
||||
it 'should render anonymous import status page', (done) ->
|
||||
expect(@res.render.callCount).to.equal 1
|
||||
expect(@res.render.calledWith(
|
||||
'project/v2-import',
|
||||
{ loginRedirect: "/read/#{@readOnlyToken}" }
|
||||
)).to.equal true
|
||||
done()
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ describe "TokenAccessHandler", ->
|
||||
'../../models/Project': {Project: @Project = {}}
|
||||
'settings-sharelatex': @settings = {}
|
||||
'../Collaborators/CollaboratorsHandler': @CollaboratorsHandler = {}
|
||||
'../User/UserGetter': @UserGetter = {}
|
||||
'../V1/V1Api': @V1Api = {
|
||||
request: sinon.stub()
|
||||
}
|
||||
@@ -491,18 +492,53 @@ describe "TokenAccessHandler", ->
|
||||
expect(@project.tokens.readAndWrite).to.equal 'rw'
|
||||
expect(@project.tokens.readOnly).to.equal 'ro'
|
||||
|
||||
describe 'getV1DocInfo', ->
|
||||
describe 'getDocPublishedInfo', ->
|
||||
beforeEach ->
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe 'when v1 api not set', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo @token, @callback
|
||||
@TokenAccessHandler.getV1DocPublishedInfo @token, @callback
|
||||
|
||||
it 'should not check access and return default info', ->
|
||||
expect(@V1Api.request.called).to.equal false
|
||||
expect(@callback.calledWith null, {
|
||||
allow: true
|
||||
}).to.equal true
|
||||
|
||||
describe 'when v1 api is set', ->
|
||||
beforeEach ->
|
||||
@settings.apis = { v1: 'v1' }
|
||||
|
||||
describe 'on V1Api.request success', ->
|
||||
beforeEach ->
|
||||
@V1Api.request = sinon.stub().callsArgWith(1, null, null, 'mock-data')
|
||||
@TokenAccessHandler.getV1DocPublishedInfo @token, @callback
|
||||
|
||||
it 'should return response body', ->
|
||||
expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/docs/#{@token}/is_published" }).to.equal true
|
||||
expect(@callback.calledWith null, 'mock-data').to.equal true
|
||||
|
||||
describe 'on V1Api.request error', ->
|
||||
beforeEach ->
|
||||
@V1Api.request = sinon.stub().callsArgWith(1, 'error')
|
||||
@TokenAccessHandler.getV1DocPublishedInfo @token, @callback
|
||||
|
||||
it 'should callback with error', ->
|
||||
expect(@callback.calledWith 'error').to.equal true
|
||||
|
||||
describe 'getV1DocInfo', ->
|
||||
beforeEach ->
|
||||
@v2UserId = 123
|
||||
@callback = sinon.stub()
|
||||
|
||||
describe 'when v1 api not set', ->
|
||||
beforeEach ->
|
||||
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
|
||||
|
||||
it 'should not check access and return default info', ->
|
||||
expect(@V1Api.request.called).to.equal false
|
||||
expect(@callback.calledWith null, {
|
||||
exists: true
|
||||
exported: false
|
||||
}).to.equal true
|
||||
@@ -511,19 +547,45 @@ describe "TokenAccessHandler", ->
|
||||
beforeEach ->
|
||||
@settings.apis = { v1: 'v1' }
|
||||
|
||||
describe 'on success', ->
|
||||
describe 'on UserGetter.getUser success', ->
|
||||
beforeEach ->
|
||||
@UserGetter.getUser = sinon.stub().yields(null, {
|
||||
overleaf: { id: 1 }
|
||||
})
|
||||
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
|
||||
|
||||
it 'should get user', ->
|
||||
expect(@UserGetter.getUser.calledWith(@v2UserId)).to.equal true
|
||||
|
||||
describe 'on UserGetter.getUser error', ->
|
||||
beforeEach ->
|
||||
@error = new Error('failed to get user')
|
||||
@UserGetter.getUser = sinon.stub().yields(@error)
|
||||
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
|
||||
|
||||
it 'should callback with error', ->
|
||||
expect(@callback.calledWith @error).to.equal true
|
||||
|
||||
describe 'on V1Api.request success', ->
|
||||
beforeEach ->
|
||||
@v1UserId = 1
|
||||
@UserGetter.getUser = sinon.stub().yields(null, {
|
||||
overleaf: { id: @v1UserId }
|
||||
})
|
||||
@V1Api.request = sinon.stub().callsArgWith(1, null, null, 'mock-data')
|
||||
@TokenAccessHandler.getV1DocInfo @token, @callback
|
||||
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
|
||||
|
||||
it 'should return response body', ->
|
||||
expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/docs/#{@token}/info" }).to.equal true
|
||||
expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/users/#{@v1UserId}/docs/#{@token}/info" }).to.equal true
|
||||
expect(@callback.calledWith null, 'mock-data').to.equal true
|
||||
|
||||
describe 'on error', ->
|
||||
describe 'on V1Api.request error', ->
|
||||
beforeEach ->
|
||||
@UserGetter.getUser = sinon.stub().yields(null, {
|
||||
overleaf: { id: 1 }
|
||||
})
|
||||
@V1Api.request = sinon.stub().callsArgWith(1, 'error')
|
||||
@TokenAccessHandler.getV1DocInfo @token, @callback
|
||||
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
|
||||
|
||||
it 'should callback with error', ->
|
||||
expect(@callback.calledWith 'error').to.equal true
|
||||
|
||||
Reference in New Issue
Block a user