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*
clean_ci:
docker-compose down
docker-compose down -v
test: test_unit test_frontend test_acceptance
@ -221,7 +221,7 @@ test_acceptance_module: $(MODULE_MAKEFILES)
fi
test_clean:
docker-compose ${DOCKER_COMPOSE_FLAGS} down
docker-compose ${DOCKER_COMPOSE_FLAGS} down -v
ci:
MOCHA_ARGS="--reporter tap" \

View file

@ -57,26 +57,9 @@ module.exports = EditorHttpController =
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)->
return name? and name.length < 150 and name.length != 0
addDoc: (req, res, next) ->
project_id = req.params.Project_id
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/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.
# 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.

View file

@ -73,13 +73,28 @@ module.exports = HistoryController =
return next(error) if error?
res.sendStatus 204
restoreFile: (req, res, next) ->
restoreFileFromV2: (req, res, next) ->
{project_id} = req.params
{version, pathname} = req.body
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?
res.json {
type: entity.type,
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'
FileWriter = require '../../infrastructure/FileWriter'
FileSystemImportManager = require '../Uploads/FileSystemImportManager'
ProjectEntityHandler = require '../Project/ProjectEntityHandler'
ProjectLocator = require '../Project/ProjectLocator'
EditorController = require '../Editor/EditorController'
Errors = require '../Errors/Errors'
moment = require 'moment'
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) ->
return callback(error) if error?
basename = Path.basename(pathname)
@ -16,7 +27,9 @@ module.exports = RestoreManager =
dirname = ''
RestoreManager._findFolderOrRootFolderId project_id, dirname, (error, parent_folder_id) ->
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) ->) ->
# We're going to try to recover the file into the folder it was in previously,
@ -30,8 +43,8 @@ module.exports = RestoreManager =
else
return callback(null, null)
_addEntityWithUniqueName: (user_id, project_id, parent_folder_id, basename, fsPath, callback = (error) ->) ->
FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, (error, entity) ->
_addEntityWithUniqueName: (addEntityWithName, basename, callback = (error) ->) ->
addEntityWithName basename, (error, entity) ->
if error?
if error instanceof Errors.InvalidNameError
# likely a duplicate name, so try with a prefix
@ -42,7 +55,7 @@ module.exports = RestoreManager =
basename = "#{basename} (Restored on #{date})"
if extension != ''
basename = "#{basename}#{extension}"
FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, callback
addEntityWithName basename, callback
else
callback(error)
else

View file

@ -108,15 +108,6 @@ module.exports = ProjectEntityUpdateHandler = self =
logger.log project_id: project_id, "removing root doc"
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) ->)=>
self.addDocWithoutUpdatingHistory.withoutLock project_id, folder_id, docName, docLines, userId, (error, doc, folder_id, path) ->
return callback(error) if error?

View file

@ -10,18 +10,25 @@ module.exports = FileWriter =
callback = _.once(callback)
fsPath = "#{Settings.path.dumpFolder}/#{identifier}_#{uuid.v4()}"
writeStream = fs.createWriteStream(fsPath)
stream.pipe(writeStream)
stream.pause()
fs.mkdir Settings.path.dumpFolder, (error) ->
stream.resume()
if error? and error.code != 'EEXIST'
# Ignore error about already existing
return callback(error)
stream.on 'error', (err)->
logger.err {err, identifier, fsPath}, "[writeStreamToDisk] something went wrong with incoming stream"
callback(err)
writeStream.on 'error', (err)->
logger.err {err, identifier, fsPath}, "[writeStreamToDisk] something went wrong with writing to disk"
callback(err)
writeStream.on "finish", ->
logger.log {identifier, fsPath}, "[writeStreamToDisk] write stream finished"
callback null, fsPath
writeStream = fs.createWriteStream(fsPath)
stream.pipe(writeStream)
stream.on 'error', (err)->
logger.err {err, identifier, fsPath}, "[writeStreamToDisk] something went wrong with incoming stream"
callback(err)
writeStream.on 'error', (err)->
logger.err {err, identifier, fsPath}, "[writeStreamToDisk] something went wrong with writing to disk"
callback(err)
writeStream.on "finish", ->
logger.log {identifier, fsPath}, "[writeStreamToDisk] write stream finished"
callback null, fsPath
writeUrlToDisk: (identifier, url, callback = (error, fsPath) ->) ->
callback = _.once(callback)

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/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/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
webRouter.get '/Project/:Project_id/download/zip', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectDownloadsController.downloadProject
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(
ng-if="!!history.diff && !history.diff.loading && !history.diff.deleted && !history.diff.error && !history.diff.binary"
)

View file

@ -18,137 +18,175 @@ describe "RestoringFiles", ->
throw error if error?
done()
describe "restoring a text file", ->
describe "restoring a deleted doc", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "foo.tex", "hello world, this is foo.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "foo.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 204
done()
it "should have created a doc", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'foo.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo.tex!"
]
done()
@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()
describe "restoring a binary file", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "image.png", "Mock image.png content")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "image.png"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 204
done()
it "should have created a file", (done) ->
it 'should have restored the doc', (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
file = _.find project.rootFolder[0].fileRefs, (file) ->
file.name == 'image.png'
file = MockFileStoreApi.files[@project_id][file._id]
expect(file.content).to.equal "Mock image.png content"
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 to a directory that exists", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "foldername/foo2.tex", "hello world, this is foo-2.tex!")
@owner.request.post {
uri: "project/#{@project_id}/folder",
json:
name: 'foldername'
}, (error, response, body) =>
throw error if error?
expect(response.statusCode).to.equal 200
describe "restoring from v2 history", ->
describe "restoring a text file", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "foo.tex", "hello world, this is foo.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "foldername/foo2.tex"
pathname: "foo.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 204
expect(response.statusCode).to.equal 200
done()
it "should have created the doc in the named folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
folder = _.find project.rootFolder[0].folders, (folder) ->
folder.name == 'foldername'
doc = _.find folder.docs, (doc) ->
doc.name == 'foo2.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo-2.tex!"
]
done()
it "should have created a doc", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'foo.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo.tex!"
]
done()
describe "restoring to a directory that no longer exists", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "nothere/foo3.tex", "hello world, this is foo-3.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "nothere/foo3.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 204
done()
describe "restoring a binary file", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "image.png", "Mock image.png content")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "image.png"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 200
done()
it "should have created the doc in the root folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'foo3.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo-3.tex!"
]
done()
it "should have created a file", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
file = _.find project.rootFolder[0].fileRefs, (file) ->
file.name == 'image.png'
file = MockFileStoreApi.files[@project_id][file._id]
expect(file.content).to.equal "Mock image.png content"
done()
describe "restoring to a filename that already exists", ->
it "should have created the file with a timestamp appended", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "main.tex", "hello world, this is main.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "main.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 204
done()
describe "restoring to a directory that exists", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "foldername/foo2.tex", "hello world, this is foo-2.tex!")
@owner.request.post {
uri: "project/#{@project_id}/folder",
json:
name: 'foldername'
}, (error, response, body) =>
throw error if error?
expect(response.statusCode).to.equal 200
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "foldername/foo2.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 200
done()
it "should have created the doc in the root folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name.match(/main \(Restored on/)
expect(doc).to.exist
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is main.tex!"
]
done()
it "should have created the doc in the named folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
folder = _.find project.rootFolder[0].folders, (folder) ->
folder.name == 'foldername'
doc = _.find folder.docs, (doc) ->
doc.name == 'foo2.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo-2.tex!"
]
done()
describe "restoring to a directory that no longer exists", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "nothere/foo3.tex", "hello world, this is foo-3.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "nothere/foo3.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 200
done()
it "should have created the doc in the root folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name == 'foo3.tex'
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is foo-3.tex!"
]
done()
describe "restoring to a filename that already exists", ->
it "should have created the file with a timestamp appended", ->
beforeEach (done) ->
MockProjectHistoryApi.addOldFile(@project_id, 42, "main.tex", "hello world, this is main.tex!")
@owner.request {
method: "POST",
url: "/project/#{@project_id}/restore_file",
json:
pathname: "main.tex"
version: 42
}, (error, response, body) ->
throw error if error?
expect(response.statusCode).to.equal 200
done()
it "should have created the doc in the root folder", (done) ->
@owner.getProject @project_id, (error, project) =>
throw error if error?
doc = _.find project.rootFolder[0].docs, (doc) ->
doc.name.match(/main \(Restored on/)
expect(doc).to.exist
doc = MockDocstoreApi.docs[@project_id][doc._id]
expect(doc.lines).to.deep.equal [
"hello world, this is main.tex!"
]
done()

View file

@ -36,7 +36,7 @@ module.exports = MockDocUpdaterApi =
res.sendStatus 200
app.delete "/project/:project_id/doc/:doc_id", (req, res, next) =>
res.send 204
res.sendStatus 204
app.listen 3003, (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])
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) =>
{project_id, doc_id} = req.params
if !@docs[project_id]?
res.send 404
res.sendStatus 404
else if !@docs[project_id][doc_id]?
res.send 404
res.sendStatus 404
else
@docs[project_id][doc_id] = undefined
res.send 204
@docs[project_id][doc_id].deleted = true
res.sendStatus 204
app.listen 3016, (error) ->
throw error if error?

View file

@ -164,35 +164,6 @@ describe "EditorHttpController", ->
it "should return false in the callback", ->
@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", ->
beforeEach ->
@doc = { "mock": "doc" }

View file

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

View file

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

View file

@ -246,27 +246,6 @@ describe 'ProjectEntityUpdateHandler', ->
.calledWith({_id : project_id}, {$unset : {rootDoc_id: 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', ->
beforeEach ->
@path = "/path/to/doc"