Fix restore of docs from old deleted docs

This commit is contained in:
James Allen 2018-03-16 12:21:07 +00:00
parent b4fa47d664
commit 1e8439a2c6
16 changed files with 242 additions and 243 deletions

View file

@ -180,7 +180,7 @@ clean_css:
rm -f public/stylesheets/*.css* rm -f public/stylesheets/*.css*
clean_ci: clean_ci:
docker-compose down docker-compose down -v
test: test_unit test_frontend test_acceptance test: test_unit test_frontend test_acceptance
@ -221,7 +221,7 @@ test_acceptance_module: $(MODULE_MAKEFILES)
fi fi
test_clean: test_clean:
docker-compose ${DOCKER_COMPOSE_FLAGS} down docker-compose ${DOCKER_COMPOSE_FLAGS} down -v
ci: ci:
MOCHA_ARGS="--reporter tap" \ MOCHA_ARGS="--reporter tap" \

View file

@ -57,26 +57,9 @@ module.exports = EditorHttpController =
privilegeLevel privilegeLevel
) )
restoreDoc: (req, res, next) ->
project_id = req.params.Project_id
doc_id = req.params.doc_id
name = req.body.name
if !name?
return res.sendStatus 400 # Malformed request
logger.log project_id: project_id, doc_id: doc_id, "restoring doc"
ProjectEntityUpdateHandler.restoreDoc project_id, doc_id, name, (err, doc, folder_id) =>
return next(error) if error?
EditorRealTimeController.emitToRoom(project_id, 'reciveNewDoc', folder_id, doc)
res.json {
doc_id: doc._id
}
_nameIsAcceptableLength: (name)-> _nameIsAcceptableLength: (name)->
return name? and name.length < 150 and name.length != 0 return name? and name.length < 150 and name.length != 0
addDoc: (req, res, next) -> addDoc: (req, res, next) ->
project_id = req.params.Project_id project_id = req.params.Project_id
name = req.body.name name = req.body.name

View file

@ -14,8 +14,6 @@ module.exports =
webRouter.delete '/project/:Project_id/doc/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteDoc webRouter.delete '/project/:Project_id/doc/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteDoc
webRouter.delete '/project/:Project_id/folder/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteFolder webRouter.delete '/project/:Project_id/folder/:entity_id', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.deleteFolder
webRouter.post '/project/:Project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, EditorHttpController.restoreDoc
# Called by the real-time API to load up the current project state. # Called by the real-time API to load up the current project state.
# This is a post request because it's more than just a getting of data. We take actions # This is a post request because it's more than just a getting of data. We take actions
# whenever a user joins a project, like updating the deleted status. # whenever a user joins a project, like updating the deleted status.

View file

@ -73,13 +73,28 @@ module.exports = HistoryController =
return next(error) if error? return next(error) if error?
res.sendStatus 204 res.sendStatus 204
restoreFile: (req, res, next) -> restoreFileFromV2: (req, res, next) ->
{project_id} = req.params {project_id} = req.params
{version, pathname} = req.body {version, pathname} = req.body
user_id = AuthenticationController.getLoggedInUserId req user_id = AuthenticationController.getLoggedInUserId req
RestoreManager.restoreFile user_id, project_id, version, pathname, (error, entity) -> logger.log {project_id, version, pathname}, "restoring file from v2"
RestoreManager.restoreFileFromV2 user_id, project_id, version, pathname, (error, entity) ->
return next(error) if error? return next(error) if error?
res.json { res.json {
type: entity.type, type: entity.type,
id: entity._id id: entity._id
} }
restoreDocFromDeletedDoc: (req, res, next) ->
{project_id, doc_id} = req.params
{name} = req.body
user_id = AuthenticationController.getLoggedInUserId(req)
if !name?
return res.sendStatus 400 # Malformed request
logger.log {project_id, doc_id, user_id}, "restoring doc from v1 deleted doc"
RestoreManager.restoreDocFromDeletedDoc user_id, project_id, doc_id, name, (err, doc) =>
return next(error) if error?
res.json {
doc_id: doc._id
}

View file

@ -2,12 +2,23 @@ Settings = require 'settings-sharelatex'
Path = require 'path' Path = require 'path'
FileWriter = require '../../infrastructure/FileWriter' FileWriter = require '../../infrastructure/FileWriter'
FileSystemImportManager = require '../Uploads/FileSystemImportManager' FileSystemImportManager = require '../Uploads/FileSystemImportManager'
ProjectEntityHandler = require '../Project/ProjectEntityHandler'
ProjectLocator = require '../Project/ProjectLocator' ProjectLocator = require '../Project/ProjectLocator'
EditorController = require '../Editor/EditorController'
Errors = require '../Errors/Errors' Errors = require '../Errors/Errors'
moment = require 'moment' moment = require 'moment'
module.exports = RestoreManager = module.exports = RestoreManager =
restoreFile: (user_id, project_id, version, pathname, callback = (error, entity) ->) -> restoreDocFromDeletedDoc: (user_id, project_id, doc_id, name, callback = (error, doc, folder_id) ->) ->
# getDoc will return the deleted doc's lines, but we don't actually remove
# the deleted doc, just create a new one from its lines.
ProjectEntityHandler.getDoc project_id, doc_id, include_deleted: true, (error, lines) ->
return callback(error) if error?
addDocWithName = (name, callback) ->
EditorController.addDoc project_id, null, name, lines, 'restore', user_id, callback
RestoreManager._addEntityWithUniqueName addDocWithName, name, callback
restoreFileFromV2: (user_id, project_id, version, pathname, callback = (error, entity) ->) ->
RestoreManager._writeFileVersionToDisk project_id, version, pathname, (error, fsPath) -> RestoreManager._writeFileVersionToDisk project_id, version, pathname, (error, fsPath) ->
return callback(error) if error? return callback(error) if error?
basename = Path.basename(pathname) basename = Path.basename(pathname)
@ -16,7 +27,9 @@ module.exports = RestoreManager =
dirname = '' dirname = ''
RestoreManager._findFolderOrRootFolderId project_id, dirname, (error, parent_folder_id) -> RestoreManager._findFolderOrRootFolderId project_id, dirname, (error, parent_folder_id) ->
return callback(error) if error? return callback(error) if error?
RestoreManager._addEntityWithUniqueName user_id, project_id, parent_folder_id, basename, fsPath, callback addEntityWithName = (name, callback) ->
FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, name, fsPath, false, callback
RestoreManager._addEntityWithUniqueName addEntityWithName, basename, callback
_findFolderOrRootFolderId: (project_id, dirname, callback = (error, folder_id) ->) -> _findFolderOrRootFolderId: (project_id, dirname, callback = (error, folder_id) ->) ->
# We're going to try to recover the file into the folder it was in previously, # We're going to try to recover the file into the folder it was in previously,
@ -30,8 +43,8 @@ module.exports = RestoreManager =
else else
return callback(null, null) return callback(null, null)
_addEntityWithUniqueName: (user_id, project_id, parent_folder_id, basename, fsPath, callback = (error) ->) -> _addEntityWithUniqueName: (addEntityWithName, basename, callback = (error) ->) ->
FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, (error, entity) -> addEntityWithName basename, (error, entity) ->
if error? if error?
if error instanceof Errors.InvalidNameError if error instanceof Errors.InvalidNameError
# likely a duplicate name, so try with a prefix # likely a duplicate name, so try with a prefix
@ -42,7 +55,7 @@ module.exports = RestoreManager =
basename = "#{basename} (Restored on #{date})" basename = "#{basename} (Restored on #{date})"
if extension != '' if extension != ''
basename = "#{basename}#{extension}" basename = "#{basename}#{extension}"
FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, callback addEntityWithName basename, callback
else else
callback(error) callback(error)
else else

View file

@ -108,15 +108,6 @@ module.exports = ProjectEntityUpdateHandler = self =
logger.log project_id: project_id, "removing root doc" logger.log project_id: project_id, "removing root doc"
Project.update {_id:project_id}, {$unset: {rootDoc_id: true}}, {}, callback Project.update {_id:project_id}, {$unset: {rootDoc_id: true}}, {}, callback
restoreDoc: (project_id, doc_id, name, callback = (error, doc, folder_id) ->) ->
if not SafePath.isCleanFilename name
return callback new Errors.InvalidNameError("invalid element name")
# getDoc will return the deleted doc's lines, but we don't actually remove
# the deleted doc, just create a new one from its lines.
ProjectEntityHandler.getDoc project_id, doc_id, include_deleted: true, (error, lines) ->
return callback(error) if error?
self.addDoc project_id, null, name, lines, callback
addDoc: wrapWithLock (project_id, folder_id, docName, docLines, userId, callback = (error, doc, folder_id) ->)=> addDoc: wrapWithLock (project_id, folder_id, docName, docLines, userId, callback = (error, doc, folder_id) ->)=>
self.addDocWithoutUpdatingHistory.withoutLock project_id, folder_id, docName, docLines, userId, (error, doc, folder_id, path) -> self.addDocWithoutUpdatingHistory.withoutLock project_id, folder_id, docName, docLines, userId, (error, doc, folder_id, path) ->
return callback(error) if error? return callback(error) if error?

View file

@ -10,6 +10,13 @@ module.exports = FileWriter =
callback = _.once(callback) callback = _.once(callback)
fsPath = "#{Settings.path.dumpFolder}/#{identifier}_#{uuid.v4()}" fsPath = "#{Settings.path.dumpFolder}/#{identifier}_#{uuid.v4()}"
stream.pause()
fs.mkdir Settings.path.dumpFolder, (error) ->
stream.resume()
if error? and error.code != 'EEXIST'
# Ignore error about already existing
return callback(error)
writeStream = fs.createWriteStream(fsPath) writeStream = fs.createWriteStream(fsPath)
stream.pipe(writeStream) stream.pipe(writeStream)

View file

@ -201,9 +201,11 @@ module.exports = class Router
webRouter.get "/project/:Project_id/doc/:doc_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi webRouter.get "/project/:Project_id/doc/:doc_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
webRouter.get "/project/:Project_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApiAndInjectUserDetails webRouter.get "/project/:Project_id/diff", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApiAndInjectUserDetails
webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
webRouter.post "/project/:project_id/restore_file", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreFile webRouter.post '/project/:project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreDocFromDeletedDoc
webRouter.post "/project/:project_id/restore_file", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreFileFromV2
privateApiRouter.post "/project/:Project_id/history/resync", AuthenticationController.httpAuth, HistoryController.resyncProjectHistory privateApiRouter.post "/project/:Project_id/history/resync", AuthenticationController.httpAuth, HistoryController.resyncProjectHistory
webRouter.get '/Project/:Project_id/download/zip', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectDownloadsController.downloadProject webRouter.get '/Project/:Project_id/download/zip', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectDownloadsController.downloadProject
webRouter.get '/project/download/zip', AuthorizationMiddlewear.ensureUserCanReadMultipleProjects, ProjectDownloadsController.downloadMultipleProjects webRouter.get '/project/download/zip', AuthorizationMiddlewear.ensureUserCanReadMultipleProjects, ProjectDownloadsController.downloadMultipleProjects

View file

@ -1,4 +1,4 @@
.diff-panel.full-size(ng-if="history.isV2", ng-controller="HistoryDiffController") .diff-panel.full-size(ng-if="!history.isV2", ng-controller="HistoryDiffController")
.diff( .diff(
ng-if="!!history.diff && !history.diff.loading && !history.diff.deleted && !history.diff.error && !history.diff.binary" ng-if="!!history.diff && !history.diff.loading && !history.diff.deleted && !history.diff.error && !history.diff.binary"
) )

View file

@ -18,6 +18,44 @@ describe "RestoringFiles", ->
throw error if error? throw error if error?
done() done()
describe "restoring a deleted doc", ->
beforeEach (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
@doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'main.tex'
@owner.request {
method: "DELETE",
url: "/project/#{@project_id}/doc/#{@doc._id}",
}, (error, response, body) =>
throw error if error?
expect(response.statusCode).to.equal 204
@owner.request {
method: "POST",
url: "/project/#{@project_id}/doc/#{@doc._id}/restore"
json:
name: "main.tex"
}, (error, response, body) =>
throw error if error?
expect(response.statusCode).to.equal 200
expect(body.doc_id).to.exist
@restored_doc_id = body.doc_id
done()
it 'should have restored the doc', (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
restored_doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'main.tex'
expect(restored_doc._id.toString()).to.equal @restored_doc_id
expect(@doc._id).to.not.equal @restored_doc_id
# console.log @doc_id, @restored_doc_id, MockDocstoreApi.docs[@project_id]
expect(MockDocstoreApi.docs[@project_id][@restored_doc_id].lines).to.deep.equal(
MockDocstoreApi.docs[@project_id][@doc._id].lines
)
done()
describe "restoring from v2 history", ->
describe "restoring a text file", -> describe "restoring a text file", ->
beforeEach (done) -> beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "foo.tex", "hello world, this is foo.tex!") MockProjectHistoryApi.addOldFile(@project_id, 42, "foo.tex", "hello world, this is foo.tex!")
@ -29,7 +67,7 @@ describe "RestoringFiles", ->
version: 42 version: 42
}, (error, response, body) -> }, (error, response, body) ->
throw error if error? throw error if error?
expect(response.statusCode).to.equal 204 expect(response.statusCode).to.equal 200
done() done()
it "should have created a doc", (done) -> it "should have created a doc", (done) ->
@ -54,7 +92,7 @@ describe "RestoringFiles", ->
version: 42 version: 42
}, (error, response, body) -> }, (error, response, body) ->
throw error if error? throw error if error?
expect(response.statusCode).to.equal 204 expect(response.statusCode).to.equal 200
done() done()
it "should have created a file", (done) -> it "should have created a file", (done) ->
@ -84,7 +122,7 @@ describe "RestoringFiles", ->
version: 42 version: 42
}, (error, response, body) -> }, (error, response, body) ->
throw error if error? throw error if error?
expect(response.statusCode).to.equal 204 expect(response.statusCode).to.equal 200
done() done()
it "should have created the doc in the named folder", (done) -> it "should have created the doc in the named folder", (done) ->
@ -111,7 +149,7 @@ describe "RestoringFiles", ->
version: 42 version: 42
}, (error, response, body) -> }, (error, response, body) ->
throw error if error? throw error if error?
expect(response.statusCode).to.equal 204 expect(response.statusCode).to.equal 200
done() done()
it "should have created the doc in the root folder", (done) -> it "should have created the doc in the root folder", (done) ->
@ -137,7 +175,7 @@ describe "RestoringFiles", ->
version: 42 version: 42
}, (error, response, body) -> }, (error, response, body) ->
throw error if error? throw error if error?
expect(response.statusCode).to.equal 204 expect(response.statusCode).to.equal 200
done() done()
it "should have created the doc in the root folder", (done) -> it "should have created the doc in the root folder", (done) ->

View file

@ -36,7 +36,7 @@ module.exports = MockDocUpdaterApi =
res.sendStatus 200 res.sendStatus 200
app.delete "/project/:project_id/doc/:doc_id", (req, res, next) => app.delete "/project/:project_id/doc/:doc_id", (req, res, next) =>
res.send 204 res.sendStatus 204
app.listen 3003, (error) -> app.listen 3003, (error) ->
throw error if error? throw error if error?

View file

@ -23,15 +23,23 @@ module.exports = MockDocStoreApi =
docs = (doc for doc_id, doc of @docs[req.params.project_id]) docs = (doc for doc_id, doc of @docs[req.params.project_id])
res.send JSON.stringify docs res.send JSON.stringify docs
app.get "/project/:project_id/doc/:doc_id", (req, res, next) =>
{project_id, doc_id} = req.params
doc = @docs[project_id][doc_id]
if doc.deleted and !req.query.include_deleted
res.sendStatus 404
else
res.send JSON.stringify doc
app.delete "/project/:project_id/doc/:doc_id", (req, res, next) => app.delete "/project/:project_id/doc/:doc_id", (req, res, next) =>
{project_id, doc_id} = req.params {project_id, doc_id} = req.params
if !@docs[project_id]? if !@docs[project_id]?
res.send 404 res.sendStatus 404
else if !@docs[project_id][doc_id]? else if !@docs[project_id][doc_id]?
res.send 404 res.sendStatus 404
else else
@docs[project_id][doc_id] = undefined @docs[project_id][doc_id].deleted = true
res.send 204 res.sendStatus 204
app.listen 3016, (error) -> app.listen 3016, (error) ->
throw error if error? throw error if error?

View file

@ -164,35 +164,6 @@ describe "EditorHttpController", ->
it "should return false in the callback", -> it "should return false in the callback", ->
@callback.calledWith(null, null, false).should.equal true @callback.calledWith(null, null, false).should.equal true
describe "restoreDoc", ->
beforeEach ->
@req.params =
Project_id: @project_id
doc_id: @doc_id
@req.body =
name: @name = "doc-name"
@ProjectEntityUpdateHandler.restoreDoc = sinon.stub().callsArgWith(3, null,
@doc = { "mock": "doc", _id: @new_doc_id = "new-doc-id" }
@folder_id = "mock-folder-id"
)
@EditorRealTimeController.emitToRoom = sinon.stub()
@EditorHttpController.restoreDoc @req, @res
it "should restore the doc", ->
@ProjectEntityUpdateHandler.restoreDoc
.calledWith(@project_id, @doc_id, @name)
.should.equal true
it "should the real-time clients about the new doc", ->
@EditorRealTimeController.emitToRoom
.calledWith(@project_id, 'reciveNewDoc', @folder_id, @doc)
.should.equal true
it "should return the new doc id", ->
@res.json
.calledWith(doc_id: @new_doc_id)
.should.equal true
describe "addDoc", -> describe "addDoc", ->
beforeEach -> beforeEach ->
@doc = { "mock": "doc" } @doc = { "mock": "doc" }

View file

@ -22,6 +22,7 @@ describe "HistoryController", ->
"./HistoryManager": @HistoryManager = {} "./HistoryManager": @HistoryManager = {}
"../Project/ProjectDetailsHandler": @ProjectDetailsHandler = {} "../Project/ProjectDetailsHandler": @ProjectDetailsHandler = {}
"../Project/ProjectEntityUpdateHandler": @ProjectEntityUpdateHandler = {} "../Project/ProjectEntityUpdateHandler": @ProjectEntityUpdateHandler = {}
"./RestoreManager": @RestoreManager = {}
@settings.apis = @settings.apis =
trackchanges: trackchanges:
enabled: false enabled: false

View file

@ -15,6 +15,8 @@ describe 'RestoreManager', ->
'../Uploads/FileSystemImportManager': @FileSystemImportManager = {} '../Uploads/FileSystemImportManager': @FileSystemImportManager = {}
'../Project/ProjectLocator': @ProjectLocator = {} '../Project/ProjectLocator': @ProjectLocator = {}
'../Errors/Errors': Errors '../Errors/Errors': Errors
'../Project/ProjectEntityHandler': @ProjectEntityHandler = {}
'../Editor/EditorController': @EditorController = {}
'logger-sharelatex': @logger = {log: sinon.stub(), err: sinon.stub()} 'logger-sharelatex': @logger = {log: sinon.stub(), err: sinon.stub()}
@user_id = 'mock-user-id' @user_id = 'mock-user-id'
@project_id = 'mock-project-id' @project_id = 'mock-project-id'
@ -25,16 +27,16 @@ describe 'RestoreManager', ->
afterEach -> afterEach ->
tk.reset() tk.reset()
describe 'restoreFile', -> describe 'restoreFileFromV2', ->
beforeEach -> beforeEach ->
@RestoreManager._writeFileVersionToDisk = sinon.stub().yields(null, @fsPath = "/tmp/path/on/disk") @RestoreManager._writeFileVersionToDisk = sinon.stub().yields(null, @fsPath = "/tmp/path/on/disk")
@RestoreManager._findFolderOrRootFolderId = sinon.stub().yields(null, @folder_id = 'mock-folder-id') @RestoreManager._findFolderOrRootFolderId = sinon.stub().yields(null, @folder_id = 'mock-folder-id')
@RestoreManager._addEntityWithUniqueName = sinon.stub().yields(null, @entity = 'mock-entity') @FileSystemImportManager.addEntity = sinon.stub().yields(null, @entity = 'mock-entity')
describe "with a file not in a folder", -> describe "with a file not in a folder", ->
beforeEach -> beforeEach ->
@pathname = 'foo.tex' @pathname = 'foo.tex'
@RestoreManager.restoreFile @user_id, @project_id, @version, @pathname, @callback @RestoreManager.restoreFileFromV2 @user_id, @project_id, @version, @pathname, @callback
it 'should write the file version to disk', -> it 'should write the file version to disk', ->
@RestoreManager._writeFileVersionToDisk @RestoreManager._writeFileVersionToDisk
@ -47,18 +49,17 @@ describe 'RestoreManager', ->
.should.equal true .should.equal true
it 'should add the entity', -> it 'should add the entity', ->
@RestoreManager._addEntityWithUniqueName @FileSystemImportManager.addEntity
.calledWith(@user_id, @project_id, @folder_id, 'foo.tex', @fsPath) .calledWith(@user_id, @project_id, @folder_id, 'foo.tex', @fsPath, false)
.should.equal true .should.equal true
it 'should call the callback with the entity', -> it 'should call the callback with the entity', ->
@callback.calledWith(null, @entity).should.equal true @callback.calledWith(null, @entity).should.equal true
describe "with a file in a folder", -> describe "with a file in a folder", ->
beforeEach -> beforeEach ->
@pathname = 'foo/bar.tex' @pathname = 'foo/bar.tex'
@RestoreManager.restoreFile @user_id, @project_id, @version, @pathname, @callback @RestoreManager.restoreFileFromV2 @user_id, @project_id, @version, @pathname, @callback
it 'should find the folder', -> it 'should find the folder', ->
@RestoreManager._findFolderOrRootFolderId @RestoreManager._findFolderOrRootFolderId
@ -66,8 +67,8 @@ describe 'RestoreManager', ->
.should.equal true .should.equal true
it 'should add the entity by its basename', -> it 'should add the entity by its basename', ->
@RestoreManager._addEntityWithUniqueName @FileSystemImportManager.addEntity
.calledWith(@user_id, @project_id, @folder_id, 'bar.tex', @fsPath) .calledWith(@user_id, @project_id, @folder_id, 'bar.tex', @fsPath, false)
.should.equal true .should.equal true
describe '_findFolderOrRootFolderId', -> describe '_findFolderOrRootFolderId', ->
@ -94,40 +95,32 @@ describe 'RestoreManager', ->
describe '_addEntityWithUniqueName', -> describe '_addEntityWithUniqueName', ->
beforeEach -> beforeEach ->
@parent_folder_id = 'mock-folder-id' @addEntityWithName = sinon.stub()
@fsPath = '/tmp/file/on/disk'
@name = 'foo.tex' @name = 'foo.tex'
describe 'with a valid name', -> describe 'with a valid name', ->
beforeEach -> beforeEach ->
@FileSystemImportManager.addEntity = sinon.stub().yields(null, @entity = 'mock-entity') @addEntityWithName.yields(null, @entity = 'mock-entity')
@RestoreManager._addEntityWithUniqueName @user_id, @project_id, @parent_folder_id, @name, @fsPath, @callback @RestoreManager._addEntityWithUniqueName @addEntityWithName, @name, @callback
it 'should add the entity', -> it 'should add the entity', ->
@FileSystemImportManager.addEntity @addEntityWithName.calledWith(@name).should.equal true
.calledWith(@user_id, @project_id, @parent_folder_id, @name, @fsPath, false)
.should.equal true
it 'should return the entity', -> it 'should return the entity', ->
@callback.calledWith(null, @entity).should.equal true @callback.calledWith(null, @entity).should.equal true
describe "with an invalid name", -> describe "with an invalid name", ->
beforeEach -> beforeEach ->
@FileSystemImportManager.addEntity = sinon.stub() @addEntityWithName.onFirstCall().yields(new Errors.InvalidNameError())
@FileSystemImportManager.addEntity.onFirstCall().yields(new Errors.InvalidNameError()) @addEntityWithName.onSecondCall().yields(null, @entity = 'mock-entity')
@FileSystemImportManager.addEntity.onSecondCall().yields(null, @entity = 'mock-entity') @RestoreManager._addEntityWithUniqueName @addEntityWithName, @name, @callback
@RestoreManager._addEntityWithUniqueName @user_id, @project_id, @parent_folder_id, @name, @fsPath, @callback
it 'should try to add the entity with its original name', -> it 'should try to add the entity with its original name', ->
@FileSystemImportManager.addEntity @addEntityWithName.calledWith('foo.tex').should.equal true
.calledWith(@user_id, @project_id, @parent_folder_id, 'foo.tex', @fsPath, false)
.should.equal true
it 'should try to add the entity with a unique name', -> it 'should try to add the entity with a unique name', ->
date = moment(new Date()).format('Do MMM YY H:mm:ss') date = moment(new Date()).format('Do MMM YY H:mm:ss')
@FileSystemImportManager.addEntity @addEntityWithName.calledWith("foo (Restored on #{date}).tex").should.equal true
.calledWith(@user_id, @project_id, @parent_folder_id, "foo (Restored on #{date}).tex", @fsPath, false)
.should.equal true
it 'should return the entity', -> it 'should return the entity', ->
@callback.calledWith(null, @entity).should.equal true @callback.calledWith(null, @entity).should.equal true

View file

@ -246,27 +246,6 @@ describe 'ProjectEntityUpdateHandler', ->
.calledWith({_id : project_id}, {$unset : {rootDoc_id: true}}) .calledWith({_id : project_id}, {$unset : {rootDoc_id: true}})
.should.equal true .should.equal true
describe "restoreDoc", ->
beforeEach ->
@doc = { "mock": "doc" }
@ProjectEntityHandler.getDoc = sinon.stub().yields(null, @docLines)
@ProjectEntityUpdateHandler.addDoc = sinon.stub().yields(null, @doc, folder_id)
@ProjectEntityUpdateHandler.restoreDoc project_id, doc_id, @docName, @callback
it 'should get the doc lines', ->
@ProjectEntityHandler.getDoc
.calledWith(project_id, doc_id, include_deleted: true)
.should.equal true
it "should add a new doc with these doc lines", ->
@ProjectEntityUpdateHandler.addDoc
.calledWith(project_id, null, @docName, @docLines)
.should.equal true
it "should call the callback with the new folder and doc", ->
@callback.calledWith(null, @doc, folder_id).should.equal true
describe 'addDoc', -> describe 'addDoc', ->
beforeEach -> beforeEach ->
@path = "/path/to/doc" @path = "/path/to/doc"