Просмотр исходного кода

Implement restore end point with acceptance tests

James Allen 8 лет назад
Родитель
Сommit
3832850be6

+ 35 - 10
services/web/app/coffee/Features/History/RestoreManager.coffee

@@ -3,6 +3,8 @@ Path = require 'path'
 FileWriter = require '../../infrastructure/FileWriter'
 FileSystemImportManager = require '../Uploads/FileSystemImportManager'
 ProjectLocator = require '../Project/ProjectLocator'
+Errors = require '../Errors/Errors'
+moment = require 'moment'
 
 module.exports = RestoreManager =
 	restoreFile: (user_id, project_id, version, pathname, callback = (error) ->) ->
@@ -12,18 +14,41 @@ module.exports = RestoreManager =
 			dirname = Path.dirname(pathname)
 			if dirname == '.' # no directory
 				dirname = ''
-			ProjectLocator.findElementByPath {project_id, path: dirname}, (error, element, type) ->
+			RestoreManager._findFolderOrRootFolderId project_id, dirname, (error, parent_folder_id) ->
 				return callback(error) if error?
-				# We're going to try to recover the file into the folder it was in previously,
-				# but this is historical, so the folder may not exist anymore. Fallback to the 
-				# root folder if not (parent_folder_id == null will default to this)
-				if type == 'folder' and element?
-					parent_folder_id = element._id
+				RestoreManager._addEntityWithUniqueName user_id, project_id, parent_folder_id, basename, fsPath, 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,
+		# but this is historical, so the folder may not exist anymore. Fallback to the 
+		# root folder if not (folder_id == null)
+		ProjectLocator.findElementByPath {project_id, path: dirname}, (error, element, type) ->
+			if error? and not error instanceof Errors.NotFoundError
+				return callback(error)
+			if type == 'folder' and element?
+				return callback(null, element._id)
+			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) ->
+			if error?
+				console.log "ERROR", error, error instanceof Errors.InvalidNameError
+				if error instanceof Errors.InvalidNameError
+					# likely a duplicate name, so try with a prefix
+					date = moment(new Date()).format('Do MMM YY H:mm:ss')
+					# Move extension to the end so the file type is preserved
+					extension = Path.extname(basename)
+					basename = Path.basename(basename, extension)
+					basename = "#{basename} (Restored on #{date})"
+					if extension != ''
+						basename = "#{basename}#{extension}"
+					FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, callback
 				else
-					parent_folder_id = null
-				# TODO if we get a name conflict error from here, then retry with a timestamp appended
-				FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, callback
+					callback(error)
+			else
+				callback()
 
 	_writeFileVersionToDisk: (project_id, version, pathname, callback = (error, fsPath) ->) ->
-		url = "#{Settings.apis.project_history.url}/project/#{project_id}/version/#{version}/#{pathname}"
+		url = "#{Settings.apis.project_history.url}/project/#{project_id}/version/#{version}/#{encodeURIComponent(pathname)}"
 		FileWriter.writeUrlToDisk project_id, url, callback

+ 1 - 1
services/web/test/acceptance/coffee/LinkedFilesTests.coffee

@@ -21,7 +21,7 @@ describe "LinkedFiles", ->
 	before (done) ->
 		LinkedUrlProxy.listen 6543, (error) =>
 			return done(error) if error?
-			@owner = new User() 
+			@owner = new User()
 			@owner.login done
 
 	describe "creating a URL based linked file", ->

+ 107 - 4
services/web/test/acceptance/coffee/RestoringFilesTest.coffee

@@ -1,11 +1,13 @@
 async = require "async"
 expect = require("chai").expect
+_ = require 'underscore'
 
 ProjectGetter = require "../../../app/js/Features/Project/ProjectGetter.js"
 
 User = require "./helpers/User"
 MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
 MockDocstoreApi = require "./helpers/MockDocstoreApi"
+MockFileStoreApi = require "./helpers/MockFileStoreApi"
 
 describe "RestoringFiles", ->
 	before (done) ->
@@ -30,7 +32,7 @@ describe "RestoringFiles", ->
 				expect(response.statusCode).to.equal 204
 				done()
 
-		it "should have created a doc", ->
+		it "should have created a doc", (done) ->
 			@owner.getProject @project_id, (error, project) =>
 				throw error if error?
 				doc = _.find project.rootFolder[0].docs, (doc) ->
@@ -42,10 +44,111 @@ describe "RestoringFiles", ->
 				done()
 
 	describe "restoring a binary file", ->
-		it "should have created a 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) ->
+			@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 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 204
+					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", ->
-		it "should have created the file in the root folder"
+		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()
+
+		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"
+		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()
+
+		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()
+

+ 0 - 1
services/web/test/acceptance/coffee/helpers/MockProjectHistoryApi.coffee

@@ -16,7 +16,6 @@ module.exports = MockProjectHistoryApi =
 		app.get "/project/:project_id/version/:version/:pathname", (req, res, next) =>
 			{project_id, version, pathname} = req.params
 			key = "#{project_id}:#{version}:#{pathname}"
-			console.log key, @oldFiles, @oldFiles[key]
 			if @oldFiles[key]?
 				res.send @oldFiles[key]
 			else