mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Fix restore of docs from old deleted docs
This commit is contained in:
parent
b4fa47d664
commit
1e8439a2c6
16 changed files with 242 additions and 243 deletions
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -22,6 +22,7 @@ describe "HistoryController", ->
|
|||
"./HistoryManager": @HistoryManager = {}
|
||||
"../Project/ProjectDetailsHandler": @ProjectDetailsHandler = {}
|
||||
"../Project/ProjectEntityUpdateHandler": @ProjectEntityUpdateHandler = {}
|
||||
"./RestoreManager": @RestoreManager = {}
|
||||
@settings.apis =
|
||||
trackchanges:
|
||||
enabled: false
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue