diff --git a/README.md b/README.md index df72375..8f1e702 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# File-Uploader Plugin (Latest 4.0.0) +# File-Uploader Plugin (Latest 4.0.1) [![Maintainability](https://api.codeclimate.com/v1/badges/13bfee73c29ecd2ea4b2/maintainability)](https://codeclimate.com/github/causecode/grails-file-uploader/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/13bfee73c29ecd2ea4b2/test_coverage)](https://codeclimate.com/github/causecode/grails-file-uploader/test_coverage) -File-Uploader Plugin >= 4.0.0 supports Grails 3.3.x (Tested with 3.3.5) +File-Uploader Plugin >= 4.0.1 supports Grails 3.3.x (Tested with 3.3.5) File-Uploader Plugin <= 3.1.x supports Grails 3.2.x (Tested upto 3.2.5) # For Grails 2.x refer branch [here](https://github.com/causecode/grails-file-uploader/tree/grails-2.x-master) diff --git a/build.gradle b/build.gradle index 41c4f6c..d844bb6 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { } } -version "4.0.0" +version "4.0.1" group "com.causecode.plugins" apply plugin: "idea" diff --git a/changelog.md b/changelog.md index 857d23d..3ae3bad 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,11 @@ # ChangeLog -## Version 4.0.0 [Unreleased] +## Version 4.0.1 [Unreleased] + +### Added +- Field to store the container name in UFile domain. + +## Version 4.0.0 [19-12-2018] ### Changed - Upgraded the plugin to support grails 3.3.5 diff --git a/grails-app/conf/application.groovy b/grails-app/conf/application.groovy index 08af37b..c822150 100644 --- a/grails-app/conf/application.groovy +++ b/grails-app/conf/application.groovy @@ -53,6 +53,7 @@ fileuploader { maxSize = 1024 * 1024 * 2 // 2 MB allowedExtensions = ["jpg", "jpeg", "gif", "png", "txt"] path = '/tmp' + container = 'test-container' } } diff --git a/grails-app/domain/com/causecode/fileuploader/UFile.groovy b/grails-app/domain/com/causecode/fileuploader/UFile.groovy index 631986d..ae09dc4 100644 --- a/grails-app/domain/com/causecode/fileuploader/UFile.groovy +++ b/grails-app/domain/com/causecode/fileuploader/UFile.groovy @@ -37,6 +37,7 @@ class UFile implements Serializable { String fileGroup String name String path + String containerName // Name of the cloud container. //Contains calculated hash value of file content String checksum @@ -65,6 +66,7 @@ class UFile implements Serializable { dateCreated bindable: false lastUpdated bindable: false envName bindable: false + containerName size: 3..63, nullable: true } static mapping = { @@ -103,8 +105,8 @@ class UFile implements Serializable { new File(path).exists() } - String getContainer() { - containerName(Holders.flatConfig["fileuploader.groups.${fileGroup}.container"]) + String getContainerFromConfig() { + containerNameFromConfig(Holders.flatConfig["fileuploader.groups.${fileGroup}.container"]) } String getFullName() { @@ -124,7 +126,7 @@ class UFile implements Serializable { * @param containerName Name of the Amazon file container or Google bucket. * @return Modified container name as described above. */ - static String containerName(String containerName) { + static String containerNameFromConfig(String containerName) { if (!containerName) { return } diff --git a/grails-app/services/com/causecode/fileuploader/FileUploaderService.groovy b/grails-app/services/com/causecode/fileuploader/FileUploaderService.groovy index 9cc12fe..164542e 100644 --- a/grails-app/services/com/causecode/fileuploader/FileUploaderService.groovy +++ b/grails-app/services/com/causecode/fileuploader/FileUploaderService.groovy @@ -52,6 +52,8 @@ class FileUploaderService { FileGroup fileGroupInstance = new FileGroup(group) ChecksumValidator checksumValidator = new ChecksumValidator(fileGroupInstance) + String containerName + if (checksumValidator.shouldCalculateChecksum()) { UFile uFileInstance = UFile.findByChecksumAndChecksumAlgorithm(checksumValidator.getChecksum(file), checksumValidator.algorithm) @@ -100,6 +102,7 @@ class FileUploaderService { throw new StorageConfigurationException('Provider not defined in the Config. Please define one.') } + containerName = getContainerNameFromConfig(fileGroupInstance) expireOn = isPublicGroup(group) ? null : new Date(new Date().time + expirationPeriod * 1000) path = uploadFileToCloud(fileData, fileGroupInstance, tempFile) } else { @@ -110,7 +113,8 @@ class FileUploaderService { UFile ufile = new UFile( [name : fileData.fileName, size: fileData.fileSize, path: path, type: type, - extension: fileData.fileExtension, expiresOn: expireOn, fileGroup: group, provider: cdnProvider]) + extension: fileData.fileExtension, expiresOn: expireOn, fileGroup: group, provider: cdnProvider, + containerName: containerName]) if (checksumValidator.shouldCalculateChecksum()) { ufile.checksum = checksumValidator.getChecksum(file) @@ -121,6 +125,23 @@ class FileUploaderService { return ufile } + /** + * This method checks if the configuration {@link FileGroup} contains the {@link String} container name. + * + * @param fileGroup {@link FileGroup} + * @return {@link String} - containerName + * @throws StorageConfigurationException - When container name is not defined. + */ + private static String getContainerNameFromConfig(FileGroup fileGroup) throws StorageConfigurationException { + String containerName = fileGroup.containerName + + if (!containerName) { + throw new StorageConfigurationException('Container name not defined in the Config. Please define one.') + } + + return containerName + } + /** * Method is used to upload file to cloud provider. Then it gets the path of uploaded file * @params fileData , fileGroupInstance, tempFile @@ -177,7 +198,7 @@ class FileUploaderService { CDNFileUploader fileUploaderInstance try { fileUploaderInstance = providerService.getProviderInstance(ufileInstance.provider.name()) - fileUploaderInstance.deleteFile(ufileInstance.container, ufileInstance.fullName) + fileUploaderInstance.deleteFile(ufileInstance.containerFromConfig, ufileInstance.fullName) } finally { fileUploaderInstance?.close() } @@ -375,7 +396,7 @@ class FileUploaderService { Boolean makePublic = isPublicGroup(uFileInstance.fileGroup) long expirationPeriod = getExpirationPeriod(uFileInstance.fileGroup) - amazonFileUploaderInstance.updatePreviousFileMetaData(uFileInstance.container, + amazonFileUploaderInstance.updatePreviousFileMetaData(uFileInstance.containerFromConfig, uFileInstance.fullName, makePublic, expirationPeriod) } @@ -452,13 +473,13 @@ class FileUploaderService { CDNFileUploader fileUploaderInstance try { fileUploaderInstance = providerService.getProviderInstance(toCDNProvider.name()) - fileUploaderInstance.uploadFile(uFile.container, downloadedFile, fileName, makePublic, + fileUploaderInstance.uploadFile(uFile.containerFromConfig, downloadedFile, fileName, makePublic, expirationPeriod) if (makePublic) { - savedUrlPath = fileUploaderInstance.getPermanentURL(uFile.container, fileName) + savedUrlPath = fileUploaderInstance.getPermanentURL(uFile.containerFromConfig, fileName) } else { - savedUrlPath = fileUploaderInstance.getTemporaryURL(uFile.container, fileName, + savedUrlPath = fileUploaderInstance.getTemporaryURL(uFile.containerFromConfig, fileName, expirationPeriod) } } finally { diff --git a/src/main/groovy/com/causecode/fileuploader/FileGroup.groovy b/src/main/groovy/com/causecode/fileuploader/FileGroup.groovy index b44ded1..882d938 100644 --- a/src/main/groovy/com/causecode/fileuploader/FileGroup.groovy +++ b/src/main/groovy/com/causecode/fileuploader/FileGroup.groovy @@ -201,9 +201,9 @@ class FileGroup { return fileNameBuilder.toString() } - // Method which fetches containerName from application.groovy file and returns it. + // Method which fetches container from application.groovy file and returns it. String getContainerName() { - return UFile.containerName(this.groupConfig.container ?: this.config.container) + return UFile.containerNameFromConfig(this.groupConfig.container ?: this.config.container) } // Method that fetched CDNProvider from the config and returns it. diff --git a/src/main/groovy/com/causecode/fileuploader/ufile/renewer/DefaultTemporaryUrlRenewer.groovy b/src/main/groovy/com/causecode/fileuploader/ufile/renewer/DefaultTemporaryUrlRenewer.groovy index 9455c6f..ff6953a 100644 --- a/src/main/groovy/com/causecode/fileuploader/ufile/renewer/DefaultTemporaryUrlRenewer.groovy +++ b/src/main/groovy/com/causecode/fileuploader/ufile/renewer/DefaultTemporaryUrlRenewer.groovy @@ -105,7 +105,7 @@ class DefaultTemporaryUrlRenewer implements TemporaryUrlRenewer { private boolean updateExpirationPeriodAndUrl(UFile uFile) { long expirationPeriod = getExpirationPeriod(uFile.fileGroup) - uFile.path = cdnFileUploader.getTemporaryURL(uFile.container, uFile.fullName, expirationPeriod) + uFile.path = cdnFileUploader.getTemporaryURL(uFile.containerFromConfig, uFile.fullName, expirationPeriod) uFile.expiresOn = new Date(new Date().time + expirationPeriod * 1000) if (NucleusUtils.save(uFile, true)) { diff --git a/src/test/groovy/com/causecode/fileuploader/BaseFileUploaderServiceSpecSetup.groovy b/src/test/groovy/com/causecode/fileuploader/BaseFileUploaderServiceSpecSetup.groovy index 03b3a50..613f0cc 100644 --- a/src/test/groovy/com/causecode/fileuploader/BaseFileUploaderServiceSpecSetup.groovy +++ b/src/test/groovy/com/causecode/fileuploader/BaseFileUploaderServiceSpecSetup.groovy @@ -98,6 +98,7 @@ class BaseFileUploaderServiceSpecSetup extends Specification implements BaseTest void mockFileGroupConstructor(String storageTypes) { new FileGroup(_) >> { String group -> fileGroupMock.groupName = group + fileGroupMock.containerName >> 'test-container' fileGroupMock.groupConfig >> [storageTypes: storageTypes] return fileGroupMock diff --git a/src/test/groovy/com/causecode/fileuploader/FileUploaderServiceSpec.groovy b/src/test/groovy/com/causecode/fileuploader/FileUploaderServiceSpec.groovy index 5b0f7c6..a6531a7 100644 --- a/src/test/groovy/com/causecode/fileuploader/FileUploaderServiceSpec.groovy +++ b/src/test/groovy/com/causecode/fileuploader/FileUploaderServiceSpec.groovy @@ -17,7 +17,6 @@ import grails.util.Holders import groovy.json.JsonBuilder import org.apache.commons.fileupload.disk.DiskFileItem import org.apache.commons.validator.UrlValidator -import org.grails.plugins.codecs.HTMLCodec import org.springframework.context.MessageSource import org.springframework.context.i18n.LocaleContextHolder import org.springframework.web.multipart.MultipartFile @@ -34,7 +33,7 @@ import javax.servlet.http.Part // Suppressed Methods counts since this class contains more than 30 methods. @ConfineMetaClassChanges([FileUploaderService, File]) @Build([UFile, UFileMoveHistory]) -@SuppressWarnings('MethodCount') +@SuppressWarnings(['MethodCount', 'FileLengthRule']) class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implements ServiceUnitTest, BuildDataTest { @@ -92,6 +91,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement new FileGroup(_) >> fileGroupMock fileGroupMock.cdnProvider >> provider + fileGroupMock.containerName >> 'test-bucket' fileGroupMock.groupConfig >> [storageTypes: 'CDN'] when: 'The saveFile method is called' @@ -100,7 +100,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement then: 'UFile instance should be successfully saved' ufileInstancefile.provider == provider ufileInstancefile.extension == 'txt' - ufileInstancefile.container == 'causecode-test' + ufileInstancefile.containerFromConfig == 'causecode-test' ufileInstancefile.fileGroup == fileGroup file.delete() @@ -125,6 +125,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement and: 'Mocked FileGroup class method call' new FileGroup(_) >> fileGroupMock fileGroupMock.cdnProvider >> CDNProvider.GOOGLE + fileGroupMock.containerName >> 'test-container' fileGroupMock.groupConfig >> [storageTypes: 'CDN'] mockGetFileNameAndExtensions() @@ -544,6 +545,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement mockGetFileNameAndExtensions() mockUploadFileMethod(true) mockGetProviderInstance('google') + 4 * fileGroupMock.containerName >> 'test-container' 5 * fileGroupMock.cdnProvider >> { return } >> { @@ -565,6 +567,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement then: 'Method should return instance of UFile' result.fileGroup == 'testGoogle' result.type == UFileType.CDN_PUBLIC + result.containerName == 'test-container' when: 'saveFile method is hit and file belongs to StandardMultiartFile' mockUploadFileMethod(true) @@ -608,6 +611,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement then: 'Method should return saved UFile instance' result.id != null + assert result.containerName == null when: 'saveFile method is called and error occurs while saving file' result = service.saveFile('testLocal', commonsMultipartFileInstance, 'test') @@ -787,6 +791,7 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement and: 'Mocked FileGroup Instance' new FileGroup(_) >> fileGroupMock fileGroupMock.cdnProvider >> CDNProvider.GOOGLE + fileGroupMock.containerName >> 'test-container' fileGroupMock.groupConfig >> [storageTypes: 'CDN', checksum: [calculate: true, algorithm: Algorithm.SHA1]] and: 'The saveFile method has been already called once for given file' @@ -838,5 +843,32 @@ class FileUploaderServiceSpec extends BaseFileUploaderServiceSpecSetup implement Exception exception = thrown(IllegalArgumentException) exception.message == "No enum constant ${Algorithm.canonicalName}.ABCD" } -} + void "test saveFile method when container name is not defined in the config"() { + given: 'Instances of CommonsMultipartFile to upload' + File fileInstance = getFileInstance('/tmp/test.txt') + + DiskFileItem fileItem = getDiskFileItemInstance(fileInstance) + CommonsMultipartFile commonsMultipartFileInstance = new CommonsMultipartFile(fileItem) + + and: 'Mocked methods of FileGroup to return empty container name' + mockFileGroupConstructor('CDN') + fileGroupMock.containerName >> { + return null + } + + fileGroupMock.cdnProvider >> { + return CDNProvider.GOOGLE + } + + and: 'Mocked getFileNameAndExtensions method of fileGroupMock' + mockGetFileNameAndExtensions() + + when: 'saveFile is called and container name is not defined' + service.saveFile('testGoogle', commonsMultipartFileInstance, 'test') + + then: 'Method should throw StorageConfigurationException and message should match' + StorageConfigurationException exception = thrown(StorageConfigurationException) + exception.message == 'Container name not defined in the Config. Please define one.' + } +} diff --git a/src/test/groovy/com/causecode/fileuploader/UFileSpec.groovy b/src/test/groovy/com/causecode/fileuploader/UFileSpec.groovy index 61c3fb6..a60e839 100644 --- a/src/test/groovy/com/causecode/fileuploader/UFileSpec.groovy +++ b/src/test/groovy/com/causecode/fileuploader/UFileSpec.groovy @@ -119,8 +119,8 @@ class UFileSpec extends Specification implements BaseTestSetup, DomainUnitTest