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

Skip top-level directory if it exists in zip upload

James Allen 10 лет назад
Родитель
Сommit
155831c3e7

+ 16 - 0
services/web/app/coffee/Features/Uploads/ArchiveManager.coffee

@@ -1,6 +1,8 @@
 child   = require "child_process"
 logger  = require "logger-sharelatex"
 metrics = require "../../infrastructure/Metrics"
+fs      = require "fs"
+Path    = require "path"
 
 module.exports = ArchiveManager =
 	extractZipArchive: (source, destination, _callback = (err) ->) ->
@@ -34,4 +36,18 @@ module.exports = ArchiveManager =
 				error = new Error(error)
 				logger.error err:error, source: source, destination: destination, "error unzipping file"
 			callback(error)
+	
+	findTopLevelDirectory: (directory, callback = (error, topLevelDir) ->) ->
+		fs.readdir directory, (error, files) ->
+			return callback(error) if error?
+			if files.length == 1
+				childPath = Path.join(directory, files[0])
+				fs.stat childPath, (error, stat) ->
+					return callback(error) if error?
+					if stat.isDirectory()
+						return callback(null, childPath)
+					else
+						return callback(null, directory)
+			else
+				return callback(null, directory)
 

+ 4 - 2
services/web/app/coffee/Features/Uploads/ProjectUploadManager.coffee

@@ -19,9 +19,11 @@ module.exports = ProjectUploadHandler =
 		destination = @_getDestinationDirectory path
 		ArchiveManager.extractZipArchive path, destination, (error) ->
 			return callback(error) if error?
-			FileSystemImportManager.addFolderContents owner_id, project_id, folder_id, destination, false, (error) ->
+			ArchiveManager.findTopLevelDirectory destination, (error, topLevelDestination) ->
 				return callback(error) if error?
-				rimraf(destination, callback)
+				FileSystemImportManager.addFolderContents owner_id, project_id, folder_id, topLevelDestination, false, (error) ->
+					return callback(error) if error?
+					rimraf(destination, callback)
 
 	_getDestinationDirectory: (source) ->
 		return path.join(path.dirname(source), "#{path.basename(source, ".zip")}-#{Date.now()}")

+ 54 - 0
services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee

@@ -22,6 +22,7 @@ describe "ArchiveManager", ->
 			"child_process": @child
 			"logger-sharelatex": @logger
 			"../../infrastructure/Metrics": @metrics
+			"fs": @fs = {}
 	
 	describe "extractZipArchive", ->
 		beforeEach ->
@@ -69,4 +70,57 @@ describe "ArchiveManager", ->
 
 			it "should log out the error", ->
 				@logger.error.called.should.equal true
+	
+	describe "findTopLevelDirectory", ->
+		beforeEach ->
+			@fs.readdir = sinon.stub()
+			@fs.stat = sinon.stub()
+			@directory = "test/directory"
+
+		describe "with multiple files", ->
+			beforeEach ->
+				@fs.readdir.callsArgWith(1, null, ["multiple", "files"])
+				@ArchiveManager.findTopLevelDirectory(@directory, @callback)
+			
+			it "should find the files in the directory", ->
+				@fs.readdir
+					.calledWith(@directory)
+					.should.equal true
+			
+			it "should return the original directory", ->
+				@callback
+					.calledWith(null, @directory)
+					.should.equal true
+		
+		describe "with a single file (not folder)", ->
+			beforeEach ->
+				@fs.readdir.callsArgWith(1, null, ["foo.tex"])
+				@fs.stat.callsArgWith(1, null, { isDirectory: () -> false })
+				@ArchiveManager.findTopLevelDirectory(@directory, @callback)
+			
+			it "should check if the file is a directory", ->
+				@fs.stat
+					.calledWith(@directory + "/foo.tex")
+					.should.equal true
+			
+			it "should return the original directory", ->
+				@callback
+					.calledWith(null, @directory)
+					.should.equal true
+		
+		describe "with a single top-level folder", ->
+			beforeEach ->
+				@fs.readdir.callsArgWith(1, null, ["folder"])
+				@fs.stat.callsArgWith(1, null, { isDirectory: () -> true })
+				@ArchiveManager.findTopLevelDirectory(@directory, @callback)
+			
+			it "should check if the file is a directory", ->
+				@fs.stat
+					.calledWith(@directory + "/folder")
+					.should.equal true
+			
+			it "should return the child directory", ->
+				@callback
+					.calledWith(null, @directory + "/folder")
+					.should.equal true
 

+ 5 - 1
services/web/test/UnitTests/coffee/Uploads/ProjectUploadManagerTests.coffee

@@ -64,6 +64,7 @@ describe "ProjectUploadManager", ->
 			@destination = "/path/to/zile/file-extracted"
 			@ProjectUploadManager._getDestinationDirectory = sinon.stub().returns @destination
 			@ArchiveManager.extractZipArchive = sinon.stub().callsArg(2)
+			@ArchiveManager.findTopLevelDirectory = sinon.stub().callsArgWith(1, null, @topLevelDestination = "/path/to/zip/file-extracted/nested")
 			@FileSystemImportManager.addFolderContents = sinon.stub().callsArg(5)
 
 			@ProjectUploadManager.insertZipArchiveIntoFolder @owner_id, @project_id, @folder_id, @source, @callback
@@ -74,8 +75,11 @@ describe "ProjectUploadManager", ->
 		it "should extract the archive", ->
 			@ArchiveManager.extractZipArchive.calledWith(@source, @destination).should.equal true
 
+		it "should find the top level directory", ->
+			@ArchiveManager.findTopLevelDirectory.calledWith(@destination).should.equal true
+
 		it "should insert the extracted archive into the folder", ->
-			@FileSystemImportManager.addFolderContents.calledWith(@owner_id, @project_id, @folder_id, @destination, false)
+			@FileSystemImportManager.addFolderContents.calledWith(@owner_id, @project_id, @folder_id, @topLevelDestination, false)
 				.should.equal true
 
 		it "should return the callback", ->