Adding ability to upload files on s3.
To enable file upload on S3 you need to enable the configuration setting in c_configuration table for s3. The S3 credentials (access_key, secret_key, bucket_name) in a m_external_services table. m_client table is normalised into m_client and m_image table. The relation between m_client and m_image is one-to-one for now. To persist the storage type(s3 or filesystem) a column had been added to m_document. MISOFX-69 Adding db migrations for s3 support MIFOSX-69 Adding method to return boolean value of S3 config MIFOSX-69 - Priti/Harsha - Refactoring Code, extracted interface for Documents upload MIFOSX-69 Refactored to add S3 Support for file upload. Modified saveDocs to throw Document Management Exception instead of IO exception MIFOSX-69 Adding S3 support for file upload MIFOSX-69 - Adding S3 support for Image upload MIFOSX-69 Adding factory to get instance of data store as per s3 configuration MIFOSX-69 Changing table s3_details to generic tabe for external services, adding column to store the storage type in m_documents table MIFOSX-69 Fetching S3 details from database MIFOSX-69 Adding document storage type while creating a document MIFOSX-69 Normalising client table to create an image table Refactoring ClientImage code to save client image in m_image table Refactored code to get document as file MIFOSX-69 Retreiving documents from S3 MIFOSX-69 Refactored ClientData to store ImageData MIFOSX-69 Retrieving images from S3, Modifying Base64 to encode from byte array instead of file in CLientImagesApiResource MIFOSX-69 Refactored code to delete client image MIFOSX-69 Deleting Images from S3 Refactored DocumentStore to return StoreType from string value MIFOSX-69 Refactored to move document deletion responsibility to storageType MIFOSX-69 Giving more appropriate name to getInstance methods of DocumentStoreFactory as per the instance creation criteria MIFOSX-69 Deleting Documents from S3 MIFOSX-69 Removing unnecessary s3_param from migration MIFOSX-69 Adding ability to update document and update doc store type MIFOSX-69 Renaming method to follow java conventions MIFOSX-69 Adding filename to error log for s3 file retrieval Adding data migrations and changing the version for migration file
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS m_external_services (
|
||||
name VARCHAR(150) NOT NULL,
|
||||
value VARCHAR(250),
|
||||
UNIQUE(name)
|
||||
);
|
||||
|
||||
INSERT INTO m_external_services (name) VALUES ('s3_bucket_name');
|
||||
INSERT INTO m_external_services (name) VALUES ('s3_access_key');
|
||||
INSERT INTO m_external_services (name) VALUES ('s3_secret_key');
|
||||
|
||||
ALTER TABLE m_document ADD COLUMN storage_type varchar(50);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `m_image`(
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`client_id` BIGINT NOT NULL,
|
||||
`key` varchar(500),
|
||||
`storage_type` varchar(50),
|
||||
FOREIGN KEY (`client_id`) REFERENCES m_client(`id`),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE(client_id)
|
||||
);
|
||||
|
||||
UPDATE m_document set storage_type='file_system';
|
||||
|
||||
insert into m_image (`client_id`,`key`,`storage_type`) select id,`image_key`,'file_system' from m_client where `image_key` IS NOT NULL;
|
||||
+1
@@ -8,4 +8,5 @@ package org.mifosplatform.infrastructure.configuration.domain;
|
||||
public interface ConfigurationDomainService {
|
||||
|
||||
boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
|
||||
boolean isAmazonS3Enabled();
|
||||
}
|
||||
+5
@@ -39,4 +39,9 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
|
||||
return thisTask.hasMakerCheckerEnabled() && property.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmazonS3Enabled(){
|
||||
return this.globalConfigurationRepository.findOneByName("amazon-S3").isEnabled();
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.mifosplatform.infrastructure.configuration.domain;
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_external_services", uniqueConstraints = { @UniqueConstraint(columnNames = ("name"))})
|
||||
public class ExternalServiceProperty extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "value", nullable = false)
|
||||
private String value;
|
||||
|
||||
public ExternalServiceProperty(final String name, final String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ExternalServiceProperty() {
|
||||
}
|
||||
|
||||
public String getValue(){
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package org.mifosplatform.infrastructure.configuration.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface ExternalServiceRepository extends JpaRepository<ExternalServiceProperty, Long>,
|
||||
JpaSpecificationExecutor<ExternalServiceProperty> {
|
||||
|
||||
ExternalServiceProperty findOneByName(String name);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.mifosplatform.infrastructure.configuration.domain;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ExternalServicesDomainService {
|
||||
private ExternalServiceRepository externalServiceRepository;
|
||||
|
||||
@Autowired
|
||||
public ExternalServicesDomainService(final ExternalServiceRepository externalServiceRepository) {
|
||||
this.externalServiceRepository = externalServiceRepository;
|
||||
}
|
||||
|
||||
public String getValue(String name){
|
||||
return externalServiceRepository.findOneByName(name).getValue();
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.data.ImageData;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class DocumentStore {
|
||||
protected DocumentStoreType type;
|
||||
|
||||
protected Integer maxFileSize = ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB;
|
||||
protected Integer maxImageSize = ApiConstants.MAX_IMAGE_UPLOAD_SIZE_IN_MB;
|
||||
|
||||
public abstract String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException;
|
||||
public abstract void deleteDocument(String documentName, String documentPath) throws DocumentManagementException;
|
||||
public abstract FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException;
|
||||
|
||||
public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException;
|
||||
public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException;
|
||||
public abstract void deleteImage(final Long resourceId, final String location);
|
||||
public abstract ImageData retrieveImage(ImageData imageData);
|
||||
|
||||
public abstract DocumentStoreType getType();
|
||||
|
||||
protected void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) {
|
||||
/**
|
||||
* Using Content-Length gives me size of the entire request, which is
|
||||
* good enough for now for a fast fail as the length of the rest of the
|
||||
* content i.e name and description while compared to the uploaded file
|
||||
* size is negligible
|
||||
**/
|
||||
if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) {
|
||||
throw new DocumentManagementException(name, fileSize,
|
||||
maxFileSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentStoreType getDocumentStoreType(String documentStoreType){
|
||||
if (documentStoreType.equals(DocumentStoreType.FILE_SYSTEM.getValue())) {
|
||||
return DocumentStoreType.FILE_SYSTEM;
|
||||
} else {
|
||||
return DocumentStoreType.S3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DocumentStoreFactory {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
public DocumentStoreFactory(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public DocumentStore getInstanceFromConfiguration(){
|
||||
ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class);
|
||||
if(configurationDomainServiceJpa.isAmazonS3Enabled()){
|
||||
return createS3DocumentStore();
|
||||
}
|
||||
else {
|
||||
return new FileSystemDocumentStore();
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentStore getInstanceFromStorageType(DocumentStoreType documentStoreType){
|
||||
if(documentStoreType == DocumentStoreType.FILE_SYSTEM){
|
||||
return new FileSystemDocumentStore();
|
||||
}
|
||||
else {
|
||||
return createS3DocumentStore();
|
||||
}
|
||||
}
|
||||
private DocumentStore createS3DocumentStore() {
|
||||
AmazonS3Client s3Client = applicationContext.getBean("s3ClientFactory", S3ClientFactory.class).getS3Client();
|
||||
ExternalServicesDomainService externalServicesDomainService = applicationContext.getBean("externalServicesDomainService", ExternalServicesDomainService.class);
|
||||
return new S3DocumentStore(externalServicesDomainService.getValue("s3_bucket_name"), s3Client);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
public enum DocumentStoreType {
|
||||
FILE_SYSTEM("file_system"), S3("s3");
|
||||
|
||||
private String value;
|
||||
|
||||
DocumentStoreType(String value){
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.portfolio.client.data.ImageData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class FileSystemDocumentStore extends DocumentStore {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(FileSystemDocumentStore.class);
|
||||
|
||||
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
||||
|
||||
|
||||
@Override
|
||||
public String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException {
|
||||
String documentName = documentCommand.getFileName();
|
||||
String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId());
|
||||
|
||||
validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize);
|
||||
makeDirectories(uploadDocumentLocation);
|
||||
|
||||
String fileLocation = uploadDocumentLocation + File.separator + documentName;
|
||||
|
||||
writeFileToFileSystem(uploadedInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException {
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
|
||||
validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize);
|
||||
makeDirectories(uploadImageLocation);
|
||||
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||
|
||||
writeFileToFileSystem(uploadedInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException {
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
|
||||
makeDirectories(uploadImageLocation);
|
||||
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension();
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||
byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString());
|
||||
out.write(imgBytes);
|
||||
out.flush();
|
||||
out.close();
|
||||
}catch (IOException ioe){
|
||||
throw new DocumentManagementException(ioe.getMessage());
|
||||
}
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteImage(final Long resourceId, final String location) {
|
||||
final boolean fileDeleted = deleteFile(location);
|
||||
if (!fileDeleted) {
|
||||
// no need to throw an Error, simply log a warning
|
||||
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException{
|
||||
final boolean fileDeleted = deleteFile(documentPath);
|
||||
if (!fileDeleted) { throw new DocumentManagementException(documentName); }
|
||||
}
|
||||
|
||||
private boolean deleteFile(String documentPath) {
|
||||
final File fileToBeDeleted = new File(documentPath);
|
||||
return fileToBeDeleted.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentStoreType getType() {
|
||||
return DocumentStoreType.FILE_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileData retrieveDocument(DocumentData documentData){
|
||||
File file = new File(documentData.fileLocation());
|
||||
return new FileData(file, documentData.fileName(), documentData.contentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData retrieveImage(ImageData imageData) {
|
||||
File file = new File(imageData.imageKey());
|
||||
imageData.setContent(file);
|
||||
return imageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the directory path for storing the new document
|
||||
*
|
||||
* @param entityType
|
||||
* @param entityId
|
||||
* @return
|
||||
*/
|
||||
private String generateFileParentDirectory(String entityType, Long entityId) {
|
||||
return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
||||
+ File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator
|
||||
+ RandomStringGenerator.generateRandomString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate directory path for storing new Image
|
||||
*/
|
||||
private String generateClientImageParentDirectory(final Long resourceId) {
|
||||
return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
||||
+ File.separator + "images" + File.separator + "clients" + File.separator + resourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively create the directory if it does not exist *
|
||||
*/
|
||||
private void makeDirectories(String uploadDocumentLocation) {
|
||||
if (!new File(uploadDocumentLocation).isDirectory()) {
|
||||
new File(uploadDocumentLocation).mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeFileToFileSystem(InputStream uploadedInputStream, String fileLocation) throws DocumentManagementException {
|
||||
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[1024];
|
||||
|
||||
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, read);
|
||||
}
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException ioException) {
|
||||
throw new DocumentManagementException("IO Exception caught while writing to FileSystem" + ioException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
||||
import org.mifosplatform.infrastructure.core.exception.ImageUploadException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(FileUtils.class);
|
||||
|
||||
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
||||
|
||||
public static enum IMAGE_MIME_TYPE {
|
||||
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_MIME_TYPE(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
|
||||
public static enum IMAGE_FILE_EXTENSION {
|
||||
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_FILE_EXTENSION(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
|
||||
public static enum IMAGE_DATA_URI_SUFFIX {
|
||||
GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG(
|
||||
"data:" + IMAGE_MIME_TYPE.PNG.getValue() + ";base64,");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_DATA_URI_SUFFIX(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
|
||||
public static Random random = new Random();
|
||||
|
||||
/**
|
||||
* Generate a random String
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String generateRandomString() {
|
||||
String characters = "abcdefghijklmnopqrstuvwxyz123456789";
|
||||
int length = generateRandomNumber();
|
||||
char[] text = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
text[i] = characters.charAt(random.nextInt(characters.length()));
|
||||
}
|
||||
return new String(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random number between 5 to 16
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static int generateRandomNumber() {
|
||||
Random randomGenerator = new Random();
|
||||
return randomGenerator.nextInt(11) + 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the directory path for storing the new document
|
||||
*
|
||||
* @param entityType
|
||||
* @param entityId
|
||||
* @return
|
||||
*/
|
||||
public static String generateFileParentDirectory(String entityType, Long entityId) {
|
||||
return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
||||
+ File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator
|
||||
+ FileUtils.generateRandomString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate directory path for storing new Image
|
||||
*/
|
||||
public static String generateClientImageParentDirectory(final Long resourceId) {
|
||||
return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim()
|
||||
+ File.separator + "images" + File.separator + "clients" + File.separator + resourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uploadedInputStream
|
||||
* @param uploadedFileLocation
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String saveToFileSystem(InputStream uploadedInputStream, String uploadedFileLocation, String fileName) throws IOException {
|
||||
String fileLocation = uploadedFileLocation + File.separator + fileName;
|
||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[1024];
|
||||
|
||||
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, read);
|
||||
}
|
||||
out.flush();
|
||||
out.close();
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param base64EncodedImage
|
||||
* @param uploadedFileLocation
|
||||
* @param fileName
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String saveToFileSystem(Base64EncodedImage base64EncodedImage, String uploadedFileLocation, String fileName)
|
||||
throws IOException {
|
||||
String fileLocation = uploadedFileLocation + File.separator + fileName + base64EncodedImage.getFileExtension();
|
||||
OutputStream out = new FileOutputStream(new File(fileLocation));
|
||||
byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString());
|
||||
out.write(imgBytes);
|
||||
out.flush();
|
||||
out.close();
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileSize
|
||||
* @param name
|
||||
*/
|
||||
public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) {
|
||||
/**
|
||||
* Using Content-Length gives me size of the entire request, which is
|
||||
* good enough for now for a fast fail as the length of the rest of the
|
||||
* content i.e name and description while compared to the uploaded file
|
||||
* size is negligible
|
||||
**/
|
||||
if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) { throw new DocumentManagementException(name, fileSize,
|
||||
maxFileSize); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that passed in Mime type maps to known image mime types
|
||||
*
|
||||
* @param mimeType
|
||||
*/
|
||||
public static void validateImageMimeType(String mimeType) {
|
||||
if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType
|
||||
.equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { throw new ImageUploadException(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Image from a Data URL
|
||||
*
|
||||
* @param mimeType
|
||||
*/
|
||||
public static Base64EncodedImage extractImageFromDataURL(String dataURL) {
|
||||
String fileExtension = "";
|
||||
String base64EncodedString = null;
|
||||
if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.GIF.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.GIF.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.GIF.getValue();
|
||||
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.PNG.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.PNG.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.PNG.getValue();
|
||||
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.JPEG.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.JPEG.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.JPEG.getValue();
|
||||
} else {
|
||||
throw new ImageDataURLNotValidException();
|
||||
}
|
||||
|
||||
return new Base64EncodedImage(base64EncodedString, fileExtension);
|
||||
}
|
||||
|
||||
public static void validateClientImageNotEmpty(String imageFileName) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
if (imageFileName == null) {
|
||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.clientImage.cannot.be.blank");
|
||||
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter image cannot be blank.");
|
||||
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
|
||||
"image");
|
||||
dataValidationErrors.add(error);
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteClientImage(final Long resourceId, final String location) {
|
||||
File fileToBeDeleted = new File(location);
|
||||
boolean fileDeleted = fileToBeDeleted.delete();
|
||||
if (!fileDeleted) {
|
||||
// no need to throw an Error, simply log a warning
|
||||
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
||||
import org.mifosplatform.infrastructure.core.exception.ImageUploadException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
public static enum IMAGE_MIME_TYPE {
|
||||
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_MIME_TYPE(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum IMAGE_FILE_EXTENSION {
|
||||
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_FILE_EXTENSION(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum IMAGE_DATA_URI_SUFFIX {
|
||||
GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG(
|
||||
"data:" + IMAGE_MIME_TYPE.PNG.getValue() + ";base64,");
|
||||
|
||||
private final String value;
|
||||
|
||||
private IMAGE_DATA_URI_SUFFIX(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validates that passed in Mime type maps to known image mime types
|
||||
*
|
||||
* @param mimeType
|
||||
*/
|
||||
public static void validateImageMimeType(String mimeType) {
|
||||
if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType
|
||||
.equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) {
|
||||
throw new ImageUploadException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts Image from a Data URL
|
||||
*
|
||||
* @param mimeType
|
||||
*/
|
||||
public static Base64EncodedImage extractImageFromDataURL(String dataURL) {
|
||||
String fileExtension = "";
|
||||
String base64EncodedString = null;
|
||||
if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.GIF.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.GIF.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.GIF.getValue();
|
||||
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.PNG.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.PNG.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.PNG.getValue();
|
||||
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.JPEG.getValue())) {
|
||||
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.JPEG.getValue(), "");
|
||||
fileExtension = IMAGE_FILE_EXTENSION.JPEG.getValue();
|
||||
} else {
|
||||
throw new ImageDataURLNotValidException();
|
||||
}
|
||||
|
||||
return new Base64EncodedImage(base64EncodedString, fileExtension);
|
||||
}
|
||||
|
||||
public static void validateClientImageNotEmpty(String imageFileName) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
if (imageFileName == null) {
|
||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.clientImage.cannot.be.blank");
|
||||
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter image cannot be blank.");
|
||||
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
|
||||
"image");
|
||||
dataValidationErrors.add(error);
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
||||
dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomStringGenerator {
|
||||
public static Random random = new Random();
|
||||
|
||||
/**
|
||||
* Generate a random String
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String generateRandomString() {
|
||||
String characters = "abcdefghijklmnopqrstuvwxyz123456789";
|
||||
int length = generateRandomNumber();
|
||||
char[] text = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
text[i] = characters.charAt(random.nextInt(characters.length()));
|
||||
}
|
||||
return new String(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random number between 5 to 16
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static int generateRandomNumber() {
|
||||
Random randomGenerator = new Random();
|
||||
return randomGenerator.nextInt(11) + 5;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class S3ClientFactory {
|
||||
|
||||
private ExternalServicesDomainService ExternalServicesDomainService;
|
||||
|
||||
@Autowired
|
||||
public S3ClientFactory(final ExternalServicesDomainService ExternalServicesDomainService) {
|
||||
this.ExternalServicesDomainService = ExternalServicesDomainService;
|
||||
}
|
||||
|
||||
public AmazonS3Client getS3Client() {
|
||||
String s3_access_key = ExternalServicesDomainService.getValue("s3_access_key");
|
||||
String s3_secret_key = ExternalServicesDomainService.getValue("s3_secret_key");
|
||||
return new AmazonS3Client(new BasicAWSCredentials(s3_access_key,s3_secret_key));
|
||||
}
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
|
||||
import com.amazonaws.AmazonClientException;
|
||||
import com.amazonaws.AmazonServiceException;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.data.ImageData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class S3DocumentStore extends DocumentStore {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(S3DocumentStore.class);
|
||||
|
||||
private final String s3BucketName;
|
||||
private AmazonS3 s3Client;
|
||||
|
||||
public S3DocumentStore(String s3BucketName, AmazonS3 s3Client) {
|
||||
this.s3BucketName = s3BucketName;
|
||||
this.s3Client = s3Client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveDocument(InputStream toUpload, DocumentCommand documentCommand) throws DocumentManagementException {
|
||||
String documentName = documentCommand.getFileName();
|
||||
validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize);
|
||||
|
||||
String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId());
|
||||
String uploadDocFullPath = uploadDocFolder + File.separator + documentName;
|
||||
|
||||
uploadDocument(toUpload, uploadDocFullPath);
|
||||
return uploadDocFullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException {
|
||||
try{
|
||||
deleteObjectFromS3(documentPath);
|
||||
}catch (AmazonServiceException ase){
|
||||
deleteObjectAmazonServiceExceptionMessage(ase);
|
||||
throw new DocumentManagementException(documentName);
|
||||
}catch (AmazonClientException ace){
|
||||
deleteObjectAmazonClientExceptionMessage(ace);
|
||||
throw new DocumentManagementException(documentName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException {
|
||||
validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize);
|
||||
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||
|
||||
uploadDocument(toUploadInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException {
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension();
|
||||
InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString()));
|
||||
|
||||
uploadDocument(toUploadInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteImage(Long resourceId, String location) {
|
||||
try {
|
||||
deleteObjectFromS3(location);
|
||||
} catch (AmazonServiceException ase) {
|
||||
deleteObjectAmazonServiceExceptionMessage(ase);
|
||||
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||
} catch (AmazonClientException ace) {
|
||||
deleteObjectAmazonClientExceptionMessage(ace);
|
||||
logger.warn("Unable to delete image associated with clients with Id " + resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteObjectAmazonClientExceptionMessage(AmazonClientException ace) {
|
||||
String message = "Caught an AmazonClientException." +
|
||||
"Error Message: " + ace.getMessage();
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
private void deleteObjectAmazonServiceExceptionMessage(AmazonServiceException ase) {
|
||||
String message = "Caught an AmazonServiceException." +
|
||||
"Error Message: " + ase.getMessage() +
|
||||
"HTTP Status Code: " + ase.getStatusCode() +
|
||||
"AWS Error Code: " + ase.getErrorCode() +
|
||||
"Error Type: " + ase.getErrorType() +
|
||||
"Request ID: " + ase.getRequestId();
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
private void deleteObjectFromS3(String location) {
|
||||
s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentStoreType getType() {
|
||||
return DocumentStoreType.S3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException {
|
||||
FileData fileData = null;
|
||||
String fileName = documentData.fileName();
|
||||
try {
|
||||
logger.info("Downloading an object");
|
||||
S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, documentData.fileLocation()));
|
||||
fileData = new FileData(s3object.getObjectContent(), fileName, documentData.contentType());
|
||||
} catch (AmazonServiceException ase) {
|
||||
getObjectAmazonServiceExceptionMessage(ase,fileName);
|
||||
throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId());
|
||||
} catch (AmazonClientException ace) {
|
||||
getObjectAmazonClientExceptionMessage(ace,fileName);
|
||||
throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId());
|
||||
}
|
||||
return fileData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData retrieveImage(ImageData imageData) {
|
||||
S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.imageKey()));
|
||||
imageData.setContent(s3object.getObjectContent());
|
||||
return imageData;
|
||||
}
|
||||
|
||||
String generateFileParentDirectory(String entityType, Long entityId) {
|
||||
return "documents" + File.separator + entityType + File.separator + entityId + File.separator + RandomStringGenerator.generateRandomString();
|
||||
}
|
||||
|
||||
String generateClientImageParentDirectory(Long resourceId) {
|
||||
return "images" + File.separator + "clients" + File.separator + resourceId;
|
||||
}
|
||||
|
||||
private void uploadDocument(InputStream inputStream, String s3UploadLocation) throws DocumentManagementException {
|
||||
try {
|
||||
logger.info("Uploading a new object to S3 from a file to " + s3UploadLocation);
|
||||
s3Client.putObject(new PutObjectRequest(this.s3BucketName, s3UploadLocation, inputStream, new ObjectMetadata()));
|
||||
|
||||
} catch (AmazonServiceException ase) {
|
||||
String message = "Caught an AmazonServiceException, which " + "means your request made it " + "to Amazon S3, but was rejected with an error response" +
|
||||
" for some reason. \n Error Message: " + ase.getMessage() +
|
||||
"HTTP Status Code: " + ase.getStatusCode() +
|
||||
"AWS Error Code: " + ase.getErrorCode() +
|
||||
"Error Type: " + ase.getErrorType() +
|
||||
"Request ID: " + ase.getRequestId();
|
||||
|
||||
logger.error(message);
|
||||
throw new DocumentManagementException(message);
|
||||
} catch (AmazonClientException ace) {
|
||||
String message = "Caught an AmazonClientException, which " +
|
||||
"means the client encountered " +
|
||||
"an internal error while trying to " +
|
||||
"communicate with S3, " +
|
||||
"such as not being able to access the network." +
|
||||
"Error Message: " + ace.getMessage();
|
||||
logger.error(message);
|
||||
throw new DocumentManagementException(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void getObjectAmazonClientExceptionMessage(AmazonClientException ace, String filename) {
|
||||
String message = "Caught an AmazonClientException, which means the client encountered " +
|
||||
"an internal error while trying to communicate with S3, " +
|
||||
"such as not being able to access the network." +
|
||||
"Error Message: " + ace.getMessage() +
|
||||
"FileName: " + filename;
|
||||
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
private void getObjectAmazonServiceExceptionMessage(AmazonServiceException ase, String filename) {
|
||||
String message = "Caught an AmazonServiceException, which means your request made it " +
|
||||
"to Amazon S3, but was rejected with an error response" +
|
||||
" for some reason." +
|
||||
"Error Message: " + ase.getMessage() +
|
||||
"HTTP Status Code: " + ase.getStatusCode() +
|
||||
"AWS Error Code: " + ase.getErrorCode() +
|
||||
"Error Type: " + ase.getErrorType() +
|
||||
"Request ID: " + ase.getRequestId() +
|
||||
"FileName: " + filename;
|
||||
logger.error(message);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -29,7 +29,7 @@ import javax.ws.rs.core.StreamingOutput;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.FileSystemDocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.ReportData;
|
||||
@@ -243,7 +243,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
|
||||
"Pentaho is not enabled"); }
|
||||
|
||||
final String reportPath = FileUtils.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt";
|
||||
final String reportPath = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt";
|
||||
logger.info("Report path: " + reportPath);
|
||||
|
||||
// load report definition
|
||||
@@ -357,7 +357,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
|
||||
@Override
|
||||
public String retrieveReportPDF(final String reportName, final String type, final Map<String, String> queryParams) {
|
||||
|
||||
String fileLocation = FileUtils.MIFOSX_BASE_DIR + File.separator + "";
|
||||
String fileLocation = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + "";
|
||||
if (!new File(fileLocation).isDirectory()) {
|
||||
new File(fileLocation).mkdirs();
|
||||
}
|
||||
|
||||
+21
-38
@@ -5,36 +5,16 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
||||
import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentReadPlatformService;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.service.DocumentWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
@@ -42,9 +22,17 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Path("{entityType}/{entityId}/documents")
|
||||
@Component
|
||||
@@ -64,8 +52,8 @@ public class DocumentManagementApiResource {
|
||||
|
||||
@Autowired
|
||||
public DocumentManagementApiResource(final PlatformSecurityContext context,
|
||||
final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer<DocumentData> toApiJsonSerializer) {
|
||||
final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer<DocumentData> toApiJsonSerializer) {
|
||||
this.context = context;
|
||||
this.documentReadPlatformService = documentReadPlatformService;
|
||||
this.documentWritePlatformService = documentWritePlatformService;
|
||||
@@ -95,8 +83,6 @@ public class DocumentManagementApiResource {
|
||||
@FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart,
|
||||
@FormDataParam("name") final String name, @FormDataParam("description") final String description) {
|
||||
|
||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
||||
|
||||
/**
|
||||
* TODO: also need to have a backup and stop reading from stream after
|
||||
* max size is reached to protect against malicious clients
|
||||
@@ -124,8 +110,6 @@ public class DocumentManagementApiResource {
|
||||
@FormDataParam("file") final FormDataBodyPart bodyPart, @FormDataParam("name") final String name,
|
||||
@FormDataParam("description") final String description) {
|
||||
|
||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
||||
|
||||
final Set<String> modifiedParams = new HashSet<String>();
|
||||
modifiedParams.add("name");
|
||||
modifiedParams.add("description");
|
||||
@@ -182,11 +166,10 @@ public class DocumentManagementApiResource {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.SystemEntityType);
|
||||
|
||||
final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId);
|
||||
final File file = new File(documentData.fileLocation());
|
||||
final ResponseBuilder response = Response.ok(file);
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + documentData.fileName() + "\"");
|
||||
response.header("Content-Type", documentData.contentType());
|
||||
final FileData fileData = this.documentReadPlatformService.retrieveDocumentAsFile(entityType, entityId, documentId);
|
||||
final ResponseBuilder response = Response.ok(fileData.file());
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + fileData.name() + "\"");
|
||||
response.header("Content-Type", fileData.contentType());
|
||||
|
||||
return response.build();
|
||||
}
|
||||
|
||||
+10
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.command;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -24,6 +26,7 @@ public class DocumentCommand {
|
||||
private String location;
|
||||
|
||||
private final Set<String> modifiedParameters;
|
||||
private DocumentStoreType storageType;
|
||||
|
||||
public DocumentCommand(final Set<String> modifiedParameters, final Long id, final String parentEntityType, final Long parentEntityId,
|
||||
final String name, final String fileName, final Long size, final String type, final String description, final String location) {
|
||||
@@ -119,4 +122,11 @@ public class DocumentCommand {
|
||||
return this.modifiedParameters.contains("location");
|
||||
}
|
||||
|
||||
public void setStorageType(DocumentStoreType storageType) {
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
public String getStorageType(){
|
||||
return storageType.getValue();
|
||||
}
|
||||
}
|
||||
+23
-1
@@ -5,6 +5,9 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
|
||||
/**
|
||||
* Immutable data object represent document being managed on platform.
|
||||
*/
|
||||
@@ -26,8 +29,10 @@ public class DocumentData {
|
||||
private final String description;
|
||||
private String location;
|
||||
|
||||
private String storageType;
|
||||
|
||||
public DocumentData(final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
||||
final Long size, final String type, final String description, final String location) {
|
||||
final Long size, final String type, final String description, final String location, String storageType) {
|
||||
this.id = id;
|
||||
this.parentEntityType = parentEntityType;
|
||||
this.parentEntityId = parentEntityId;
|
||||
@@ -37,6 +42,7 @@ public class DocumentData {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.location = location;
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
public String contentType() {
|
||||
@@ -55,4 +61,20 @@ public class DocumentData {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public DocumentStoreType storeType(){
|
||||
return DocumentStore.getDocumentStoreType(storageType);
|
||||
}
|
||||
|
||||
public String getParentEntityType(){
|
||||
return this.parentEntityType;
|
||||
}
|
||||
|
||||
public Long getParentEntityId(){
|
||||
return this.parentEntityId;
|
||||
}
|
||||
|
||||
public Long getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileData {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileData.class);
|
||||
|
||||
private final File file;
|
||||
private final String fileName;
|
||||
private final String contentType;
|
||||
private final InputStream inputStream;
|
||||
|
||||
public FileData(File file, String fileName, String contentType) {
|
||||
this.file = file;
|
||||
this.fileName = fileName;
|
||||
this.contentType = contentType;
|
||||
this.inputStream = null;
|
||||
}
|
||||
|
||||
public FileData(InputStream inputStream, String fileName, String contentType) {
|
||||
this.file = null;
|
||||
this.inputStream = inputStream;
|
||||
this.fileName = fileName;
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String contentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public InputStream file(){
|
||||
try{
|
||||
if(inputStream == null){
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
else {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e){
|
||||
logger.error(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-9
@@ -5,14 +5,16 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.domain;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_document")
|
||||
public class Document extends AbstractPersistable<Long> {
|
||||
@@ -41,15 +43,19 @@ public class Document extends AbstractPersistable<Long> {
|
||||
@Column(name = "location", length = 500)
|
||||
private String location;
|
||||
|
||||
public Document() {}
|
||||
@Column(name = "storage_type", length = 50)
|
||||
private String storageType;
|
||||
|
||||
public Document() {
|
||||
}
|
||||
|
||||
public static Document createNew(final String parentEntityType, final Long parentEntityId, final String name, final String fileName,
|
||||
final Long size, final String type, final String description, final String location) {
|
||||
return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location);
|
||||
final Long size, final String type, final String description, final String location, final DocumentStoreType storageType) {
|
||||
return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location, storageType);
|
||||
}
|
||||
|
||||
private Document(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size,
|
||||
final String type, final String description, final String location) {
|
||||
final String type, final String description, final String location, final DocumentStoreType storageType) {
|
||||
this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null);
|
||||
this.parentEntityId = parentEntityId;
|
||||
this.name = StringUtils.defaultIfEmpty(name, null);
|
||||
@@ -58,6 +64,7 @@ public class Document extends AbstractPersistable<Long> {
|
||||
this.type = StringUtils.defaultIfEmpty(type, null);
|
||||
this.description = StringUtils.defaultIfEmpty(description, null);
|
||||
this.location = StringUtils.defaultIfEmpty(location, null);
|
||||
this.storageType = storageType.getValue();
|
||||
}
|
||||
|
||||
public void update(final DocumentCommand command) {
|
||||
@@ -79,7 +86,7 @@ public class Document extends AbstractPersistable<Long> {
|
||||
if (command.isSizeChanged()) {
|
||||
this.size = command.getSize();
|
||||
}
|
||||
|
||||
this.storageType = command.getStorageType();
|
||||
}
|
||||
|
||||
public String getParentEntityType() {
|
||||
@@ -146,4 +153,7 @@ public class Document extends AbstractPersistable<Long> {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public DocumentStoreType storageType(){
|
||||
return DocumentStore.getDocumentStoreType(this.storageType);
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -5,13 +5,17 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface DocumentReadPlatformService {
|
||||
|
||||
Collection<DocumentData> retrieveAllDocuments(String entityType, Long entityId);
|
||||
|
||||
FileData retrieveDocumentAsFile(String entityType, Long entityId, Long documentId);
|
||||
|
||||
DocumentData retrieveDocument(String entityType, Long entityId, Long documentId);
|
||||
|
||||
}
|
||||
+41
-16
@@ -5,13 +5,12 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -20,16 +19,23 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
@Service
|
||||
public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final DocumentStoreFactory documentStoreFactory;
|
||||
|
||||
@Autowired
|
||||
public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
|
||||
public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
|
||||
final DocumentStoreFactory documentStoreFactory) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.documentStoreFactory = documentStoreFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,17 +52,22 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentData retrieveDocument(final String entityType, final Long entityId, final Long documentId) {
|
||||
|
||||
public FileData retrieveDocumentAsFile(final String entityType, final Long entityId, final Long documentId) {
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
DocumentData documentData = getDocumentData(entityType, entityId, documentId);
|
||||
DocumentStore instanceForRead = documentStoreFactory.getInstanceFromStorageType(documentData.storeType());
|
||||
return instanceForRead.retrieveDocument(documentData);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new DocumentNotFoundException(entityType, entityId, documentId);
|
||||
} catch (final DocumentNotFoundException d) {
|
||||
throw d;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO verify if the entities are valid and a
|
||||
// user has data
|
||||
// scope for the particular entities
|
||||
final DocumentMapper mapper = new DocumentMapper(false);
|
||||
final String sql = "select " + mapper.schema() + " and d.id=? ";
|
||||
return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { entityType, entityId, documentId });
|
||||
@Override
|
||||
public DocumentData retrieveDocument(String entityType, Long entityId, Long documentId) {
|
||||
try {
|
||||
return getDocumentData(entityType, entityId, documentId);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new DocumentNotFoundException(entityType, entityId, documentId);
|
||||
}
|
||||
@@ -73,7 +84,8 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ
|
||||
public String schema() {
|
||||
return "d.id as id, d.parent_entity_type as parentEntityType, d.parent_entity_id as parentEntityId, d.name as name, "
|
||||
+ " d.file_name as fileName, d.size as fileSize, d.type as fileType, "
|
||||
+ " d.description as description, d.location as location"
|
||||
+ " d.description as description, d.location as location,"
|
||||
+ " d.storage_type as storageType"
|
||||
+ " from m_document d where d.parent_entity_type=? and d.parent_entity_id=?";
|
||||
}
|
||||
|
||||
@@ -92,8 +104,21 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ
|
||||
if (!this.hideLocation) {
|
||||
location = rs.getString("location");
|
||||
}
|
||||
final String storageType = rs.getString("storageType");
|
||||
|
||||
return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location);
|
||||
return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location, storageType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DocumentData getDocumentData(String entityType, Long entityId, Long documentId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
// TODO verify if the entities are valid and a
|
||||
// user has data
|
||||
// scope for the particular entities
|
||||
final DocumentMapper mapper = new DocumentMapper(false);
|
||||
final String sql = "select " + mapper.schema() + " and d.id=? ";
|
||||
return this.jdbcTemplate.queryForObject(sql, mapper, new Object[]{entityType, entityId, documentId});
|
||||
}
|
||||
}
|
||||
+24
-41
@@ -5,13 +5,11 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommandValidator;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.Document;
|
||||
@@ -27,6 +25,8 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service
|
||||
public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWritePlatformService {
|
||||
|
||||
@@ -34,11 +34,14 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final DocumentRepository documentRepository;
|
||||
private final DocumentStoreFactory documentStoreFactory;
|
||||
|
||||
@Autowired
|
||||
public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository) {
|
||||
public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository,
|
||||
final DocumentStoreFactory documentStoreFactory) {
|
||||
this.context = context;
|
||||
this.documentRepository = documentRepository;
|
||||
this.documentStoreFactory = documentStoreFactory;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -53,19 +56,13 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
validator.validateForCreate();
|
||||
|
||||
final String fileUploadLocation = FileUtils.generateFileParentDirectory(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId());
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration();
|
||||
|
||||
/** Recursively create the directory if it does not exist **/
|
||||
if (!new File(fileUploadLocation).isDirectory()) {
|
||||
new File(fileUploadLocation).mkdirs();
|
||||
}
|
||||
|
||||
final String fileLocation = FileUtils.saveToFileSystem(inputStream, fileUploadLocation, documentCommand.getFileName());
|
||||
final String fileLocation = documentStore.saveDocument(inputStream, documentCommand);
|
||||
|
||||
final Document document = Document.createNew(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(),
|
||||
documentCommand.getName(), documentCommand.getFileName(), documentCommand.getSize(), documentCommand.getType(),
|
||||
documentCommand.getDescription(), fileLocation);
|
||||
documentCommand.getDescription(), fileLocation, documentStore.getType());
|
||||
|
||||
this.documentRepository.save(document);
|
||||
|
||||
@@ -74,8 +71,8 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
logger.error(dve.getMessage(), dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
} catch (final IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
} catch (final DocumentManagementException dme) {
|
||||
logger.error(dme.getMessage(), dme);
|
||||
throw new DocumentManagementException(documentCommand.getName());
|
||||
}
|
||||
}
|
||||
@@ -92,30 +89,21 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
// TODO check if entity id is valid and within data scope for the
|
||||
// user
|
||||
final Document documentForUpdate = this.documentRepository.findOne(documentCommand.getId());
|
||||
DocumentStoreType documentStoreType = documentForUpdate.storageType();
|
||||
if (documentForUpdate == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId(), documentCommand.getId()); }
|
||||
oldLocation = documentForUpdate.getLocation();
|
||||
// if a new file is also passed in
|
||||
if (inputStream != null && documentCommand.isFileNameChanged()) {
|
||||
final String fileUploadLocation = FileUtils.generateFileParentDirectory(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId());
|
||||
|
||||
/** Recursively create the directory if it does not exist **/
|
||||
if (!new File(fileUploadLocation).isDirectory()) {
|
||||
new File(fileUploadLocation).mkdirs();
|
||||
}
|
||||
|
||||
// TODO provide switch to toggle between file system appender
|
||||
// and Amazon S3 appender
|
||||
final String fileLocation = FileUtils.saveToFileSystem(inputStream, fileUploadLocation, documentCommand.getFileName());
|
||||
documentCommand.setLocation(fileLocation);
|
||||
DocumentStore instanceFromConfiguration = this.documentStoreFactory.getInstanceFromConfiguration();
|
||||
documentCommand.setLocation(instanceFromConfiguration.saveDocument(inputStream, documentCommand));
|
||||
documentCommand.setStorageType(instanceFromConfiguration.getType());
|
||||
}
|
||||
|
||||
documentForUpdate.update(documentCommand);
|
||||
|
||||
if (inputStream != null && documentCommand.isFileNameChanged()) {
|
||||
// delete previous file
|
||||
deleteFile(documentCommand.getName(), oldLocation);
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(documentStoreType);
|
||||
documentStore.deleteDocument(documentCommand.getName(), oldLocation);
|
||||
}
|
||||
|
||||
this.documentRepository.saveAndFlush(documentForUpdate);
|
||||
@@ -125,8 +113,8 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
logger.error(dve.getMessage(), dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
} catch (final IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
} catch (final DocumentManagementException dme) {
|
||||
logger.error(dme.getMessage(), dme);
|
||||
throw new DocumentManagementException(documentCommand.getName());
|
||||
}
|
||||
}
|
||||
@@ -139,20 +127,15 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
validateParentEntityType(documentCommand);
|
||||
// TODO: Check document is present under this entity Id
|
||||
final Document document = this.documentRepository.findOne(documentCommand.getId());
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(document.storageType());
|
||||
if (document == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId(), documentCommand.getId()); }
|
||||
|
||||
this.documentRepository.delete(document);
|
||||
deleteFile(document.getName(), document.getLocation());
|
||||
documentStore.deleteDocument(document.getName(), document.getLocation());
|
||||
return new CommandProcessingResult(document.getId());
|
||||
}
|
||||
|
||||
private void deleteFile(final String documentName, final String location) {
|
||||
final File fileToBeDeleted = new File(location);
|
||||
final boolean fileDeleted = fileToBeDeleted.delete();
|
||||
if (!fileDeleted) { throw new DocumentManagementException(documentName); }
|
||||
}
|
||||
|
||||
private void validateParentEntityType(final DocumentCommand documentCommand) {
|
||||
if (!checkValidEntityType(documentCommand.getParentEntityType())) { throw new InvalidEntityTypeForDocumentManagementException(
|
||||
documentCommand.getParentEntityType()); }
|
||||
|
||||
+27
-51
@@ -5,44 +5,31 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_DATA_URI_SUFFIX;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_FILE_EXTENSION;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_MIME_TYPE;
|
||||
import org.mifosplatform.infrastructure.core.service.ImageUtils;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.exception.ImageNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.service.ClientWritePlatformService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Path("/clients/{clientId}/images")
|
||||
@Component
|
||||
@@ -53,10 +40,11 @@ public class ClientImagesApiResource {
|
||||
private final ClientReadPlatformService clientReadPlatformService;
|
||||
private final ClientWritePlatformService clientWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
||||
private final static Logger logger = LoggerFactory.getLogger(ClientImagesApiResource.class);
|
||||
|
||||
@Autowired
|
||||
public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
|
||||
final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||
final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||
this.context = context;
|
||||
this.clientReadPlatformService = readPlatformService;
|
||||
this.clientWritePlatformService = clientWritePlatformService;
|
||||
@@ -76,12 +64,11 @@ public class ClientImagesApiResource {
|
||||
// TODO: vishwas might need more advances validation (like reading magic
|
||||
// number) for handling malicious clients
|
||||
// and clients not setting mime type
|
||||
FileUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
||||
FileUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, fileDetails.getFileName(), ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
||||
ImageUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
||||
ImageUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
||||
|
||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
||||
inputStream);
|
||||
inputStream, fileSize);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
@@ -94,7 +81,7 @@ public class ClientImagesApiResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
|
||||
|
||||
final Base64EncodedImage base64EncodedImage = FileUtils.extractImageFromDataURL(jsonRequestBody);
|
||||
final Base64EncodedImage base64EncodedImage = ImageUtils.extractImageFromDataURL(jsonRequestBody);
|
||||
|
||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
||||
|
||||
@@ -116,15 +103,14 @@ public class ClientImagesApiResource {
|
||||
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
||||
|
||||
// TODO: Need a better way of determining image type
|
||||
String imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
||||
if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
||||
String imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
||||
if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
||||
}
|
||||
|
||||
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeFromFile(clientData.imageKey());
|
||||
|
||||
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(clientData.image());
|
||||
return clientImageAsBase64Text;
|
||||
}
|
||||
|
||||
@@ -138,22 +124,12 @@ public class ClientImagesApiResource {
|
||||
|
||||
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
||||
|
||||
File image = new File(clientData.imageKey());
|
||||
String imageName = image.getName();
|
||||
ResponseBuilder response = Response.ok(image);
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + imageName + "\"");
|
||||
ResponseBuilder response = Response.ok(clientData.image());
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + clientData.imageName() + "\"");
|
||||
|
||||
// TODO: Need a better way of determining image type
|
||||
|
||||
// determine image type
|
||||
String contentType = IMAGE_MIME_TYPE.JPEG.getValue();
|
||||
if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
contentType = IMAGE_MIME_TYPE.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
contentType = IMAGE_MIME_TYPE.PNG.getValue();
|
||||
}
|
||||
|
||||
response.header("Content-Type", contentType);
|
||||
response.header("Content-Type", clientData.imageContentType());
|
||||
return response.build();
|
||||
}
|
||||
|
||||
|
||||
+41
-14
@@ -5,9 +5,6 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
@@ -16,12 +13,21 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.organisation.office.data.OfficeData;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Immutable data object representing client data.
|
||||
*/
|
||||
final public class ClientData implements Comparable<ClientData> {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(ClientData.class);
|
||||
|
||||
|
||||
private final Long id;
|
||||
private final String accountNo;
|
||||
private final String externalId;
|
||||
@@ -40,7 +46,8 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
private final Long officeId;
|
||||
private final String officeName;
|
||||
|
||||
private final String imageKey;
|
||||
// private String imageData;
|
||||
private ImageData imageData;
|
||||
@SuppressWarnings("unused")
|
||||
private final Boolean imagePresent;
|
||||
|
||||
@@ -58,13 +65,13 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
|
||||
return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
|
||||
clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
|
||||
clientData.externalId, clientData.activationDate, clientData.imageKey, allowedOffices, clientData.groups);
|
||||
clientData.externalId, clientData.activationDate, clientData.imageData, allowedOffices, clientData.groups);
|
||||
}
|
||||
|
||||
public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) {
|
||||
return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id,
|
||||
clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName,
|
||||
clientData.externalId, clientData.activationDate, clientData.imageKey, clientData.officeOptions, parentGroups);
|
||||
clientData.externalId, clientData.activationDate, clientData.imageData, clientData.officeOptions, parentGroups);
|
||||
}
|
||||
|
||||
public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname,
|
||||
@@ -81,14 +88,14 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
|
||||
public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName,
|
||||
final Long id, final String firstname, final String middlename, final String lastname, final String fullname,
|
||||
final String displayName, final String externalId, final LocalDate activationDate, final String imageKey) {
|
||||
final String displayName, final String externalId, final LocalDate activationDate, final ImageData imageData) {
|
||||
return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey, null, null);
|
||||
externalId, activationDate, imageData, null, null);
|
||||
}
|
||||
|
||||
private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id,
|
||||
final String firstname, final String middlename, final String lastname, final String fullname, final String displayName,
|
||||
final String externalId, final LocalDate activationDate, final String imageKey, final Collection<OfficeData> allowedOffices,
|
||||
final String externalId, final LocalDate activationDate, final ImageData imageKey, final Collection<OfficeData> allowedOffices,
|
||||
final Collection<GroupGeneralData> groups) {
|
||||
this.accountNo = accountNo;
|
||||
this.status = status;
|
||||
@@ -107,7 +114,7 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
this.displayName = StringUtils.defaultIfEmpty(displayName, null);
|
||||
this.externalId = StringUtils.defaultIfEmpty(externalId, null);
|
||||
this.activationDate = activationDate;
|
||||
this.imageKey = imageKey;
|
||||
this.imageData = imageKey;
|
||||
if (imageKey != null) {
|
||||
this.imagePresent = Boolean.TRUE;
|
||||
} else {
|
||||
@@ -138,17 +145,16 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
}
|
||||
|
||||
public String imageKey() {
|
||||
return this.imageKey;
|
||||
return this.imageData.imageKey();
|
||||
}
|
||||
|
||||
public boolean imageKeyDoesNotExist() {
|
||||
return !imageKeyExists();
|
||||
return imageData.equals(null) && !imageKeyExists();
|
||||
}
|
||||
|
||||
private boolean imageKeyExists() {
|
||||
return StringUtils.isNotBlank(this.imageKey);
|
||||
return StringUtils.isNotBlank(this.imageData.imageKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ClientData obj) {
|
||||
if (obj == null) { return -1; }
|
||||
@@ -194,4 +200,25 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
public LocalDate getActivationDate() {
|
||||
return this.activationDate;
|
||||
}
|
||||
|
||||
public void setImageData(ImageData imageData){
|
||||
this.imageData = imageData;
|
||||
}
|
||||
|
||||
public String imageName() {
|
||||
return imageData.name();
|
||||
}
|
||||
|
||||
public String imageContentType() {
|
||||
return imageData.contentType();
|
||||
}
|
||||
|
||||
public byte[] image(){
|
||||
try{
|
||||
return imageData.getContent();
|
||||
}catch (IOException ioe){
|
||||
logger.error("Error in fetching client image");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.mifosplatform.portfolio.client.data;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
import org.mifosplatform.infrastructure.core.service.ImageUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ImageData {
|
||||
|
||||
private final Long clientId;
|
||||
private final String key;
|
||||
// private final DocumentStoreType storeType;
|
||||
private final String storeType;
|
||||
private File file;
|
||||
private String contentType;
|
||||
private String name;
|
||||
private InputStream inputStream;
|
||||
|
||||
|
||||
public ImageData(Long clientId, String key, String storeType) {
|
||||
this.clientId = clientId;
|
||||
this.key = key;
|
||||
this.storeType = storeType;
|
||||
}
|
||||
|
||||
public String imageKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public byte[] getContent() throws IOException {
|
||||
if(inputStream == null){
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
return IOUtils.toByteArray(fileInputStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IOUtils.toByteArray(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private String setImageContentType() {
|
||||
String fileName = file.getName();
|
||||
String contentType = ImageUtils.IMAGE_MIME_TYPE.JPEG.getValue();
|
||||
if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
contentType = ImageUtils.IMAGE_MIME_TYPE.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
contentType = ImageUtils.IMAGE_MIME_TYPE.PNG.getValue();
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String contentType(){
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public DocumentStoreType storeType() {
|
||||
return DocumentStore.getDocumentStoreType(storeType);
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return this.file.getName();
|
||||
}
|
||||
|
||||
|
||||
public void setContent(InputStream objectContent) {
|
||||
this.inputStream = objectContent;
|
||||
}
|
||||
|
||||
public void setContent(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
+9
-30
@@ -5,26 +5,6 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
@@ -39,6 +19,9 @@ import org.mifosplatform.portfolio.client.api.ClientApiConstants;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") })
|
||||
public final class Client extends AbstractPersistable<Long> {
|
||||
@@ -79,9 +62,6 @@ public final class Client extends AbstractPersistable<Long> {
|
||||
@Column(name = "external_id", length = 100, nullable = true, unique = true)
|
||||
private String externalId;
|
||||
|
||||
@Column(name = "image_key", length = 500, nullable = true)
|
||||
private String imageKey;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
|
||||
private Set<Group> groups;
|
||||
@@ -385,13 +365,12 @@ public final class Client extends AbstractPersistable<Long> {
|
||||
return this.office.identifiedBy(officeId);
|
||||
}
|
||||
|
||||
public String imageKey() {
|
||||
return imageKey;
|
||||
}
|
||||
|
||||
public void updateImageKey(final String imageKey) {
|
||||
this.imageKey = imageKey;
|
||||
}
|
||||
// public String imageKey() {
|
||||
// }
|
||||
//
|
||||
// public void updateImageKey(final String imageKey) {
|
||||
// this.image.setKey(imageKey);
|
||||
// }
|
||||
|
||||
public Long officeId() {
|
||||
return this.office.getId();
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.domain;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreType;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_image")
|
||||
public final class Image extends AbstractPersistable<Long> {
|
||||
|
||||
// @Column(name = "`client_id`")
|
||||
@OneToOne()
|
||||
@JoinColumn(name = "`client_id`")
|
||||
private Client client;
|
||||
|
||||
@Column(name = "`key`", length = 500)
|
||||
private String key;
|
||||
|
||||
@Column(name = "`storage_type`", length = 50)
|
||||
private String storeType;
|
||||
|
||||
public Image(Client client, String image_key, DocumentStoreType storeType) {
|
||||
this.client = client;
|
||||
this.key = image_key;
|
||||
this.storeType = storeType.getValue();
|
||||
}
|
||||
|
||||
public Image() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Image(Client client) {
|
||||
this.client = client;
|
||||
this.key = null;
|
||||
this.storeType = null;
|
||||
}
|
||||
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String updateKey(String key) {
|
||||
setKey(key);
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public void updateStorageType(String documentStoreType) {
|
||||
this.storeType = documentStoreType;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface ImageRepository extends JpaRepository<Image, Long>, JpaSpecificationExecutor<Client> {
|
||||
Image findOneByClient(Client client);
|
||||
}
|
||||
+1
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.service;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.Page;
|
||||
|
||||
+33
-10
@@ -16,6 +16,7 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory;
|
||||
import org.mifosplatform.infrastructure.core.service.Page;
|
||||
import org.mifosplatform.infrastructure.core.service.PaginationHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
@@ -25,6 +26,7 @@ import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData;
|
||||
import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.data.ImageData;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
@@ -38,12 +40,14 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service(value = "clientReadPlatformService")
|
||||
@Service
|
||||
public class ClientReadPlatformServiceImpl implements ClientReadPlatformService {
|
||||
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
private final OfficeReadPlatformService officeReadPlatformService;
|
||||
private final DocumentStoreFactory documentStoreFactory;
|
||||
|
||||
// data mappers
|
||||
private final PaginationHelper<ClientData> paginationHelper = new PaginationHelper<ClientData>();
|
||||
@@ -54,9 +58,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
@Autowired
|
||||
public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
|
||||
final OfficeReadPlatformService officeReadPlatformService) {
|
||||
final OfficeReadPlatformService officeReadPlatformService, final DocumentStoreFactory documentStoreFactory) {
|
||||
this.context = context;
|
||||
this.officeReadPlatformService = officeReadPlatformService;
|
||||
this.documentStoreFactory = documentStoreFactory;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@@ -92,7 +97,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
if (searchParameters.isOrderByRequested()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
|
||||
|
||||
if (searchParameters.isSortOrderProvided()) {
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
}
|
||||
@@ -109,7 +114,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(),
|
||||
new Object[] { hierarchySearchString }, this.clientMapper);
|
||||
}
|
||||
|
||||
|
||||
private String buildSqlStringFromClientCriteria(final SearchParameters searchParameters) {
|
||||
|
||||
final String sqlSearch = searchParameters.getSqlSearch();
|
||||
@@ -159,7 +164,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
@Override
|
||||
public ClientData retrieveOne(final Long clientId) {
|
||||
|
||||
try {
|
||||
AppUser currentUser = context.authenticatedUser();
|
||||
String hierarchy = currentUser.getOffice().getHierarchy();
|
||||
@@ -173,7 +177,15 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
Collection<GroupGeneralData> parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper,
|
||||
new Object[] { clientId });
|
||||
|
||||
String clientImageSql = "select * from m_image where client_id =" + clientId;
|
||||
|
||||
try {
|
||||
ImageData imageData = this.jdbcTemplate.queryForObject(clientImageSql, new ImageMapper());
|
||||
clientData.setImageData(documentStoreFactory.getInstanceFromStorageType(imageData.storeType()).retrieveImage(imageData));
|
||||
} catch (EmptyResultDataAccessException e) {}
|
||||
return ClientData.setParentGroups(clientData, parentGroups);
|
||||
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
throw new ClientNotFoundException(clientId);
|
||||
}
|
||||
@@ -206,8 +218,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String hierarchy = currentUser.getOffice().getHierarchy();
|
||||
final String hierarchySearchString = hierarchy + "%";
|
||||
|
||||
final String sql = "select " + this.membersOfGroupMapper.schema()
|
||||
+ " where o.hierarchy like ? and pgc.group_id = ?";
|
||||
final String sql = "select " + this.membersOfGroupMapper.schema() + " where o.hierarchy like ? and pgc.group_id = ?";
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, new Object[] { hierarchySearchString, groupId });
|
||||
}
|
||||
@@ -255,7 +266,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String officeName = rs.getString("officeName");
|
||||
|
||||
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey);
|
||||
externalId, activationDate, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,11 +310,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String displayName = rs.getString("displayName");
|
||||
final String externalId = rs.getString("externalId");
|
||||
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
|
||||
final String imageKey = rs.getString("imageKey");
|
||||
// final String imageKey = rs.getString("imageKey");
|
||||
final String officeName = rs.getString("officeName");
|
||||
|
||||
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey);
|
||||
externalId, activationDate, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -325,6 +336,18 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ImageMapper implements RowMapper<ImageData> {
|
||||
|
||||
@Override
|
||||
public ImageData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
final Long clientId = rs.getLong("client_id");
|
||||
final String imageKey = rs.getString("key");
|
||||
final String storeType = rs.getString("storage_type");
|
||||
return new ImageData(clientId, imageKey, storeType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class ClientLookupMapper implements RowMapper<ClientData> {
|
||||
|
||||
private final String schema;
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ public interface ClientWritePlatformService {
|
||||
CommandProcessingResult deleteClient(Long clientId);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream);
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream, Long fileSize);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage);
|
||||
|
||||
+48
-44
@@ -5,13 +5,6 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -21,7 +14,8 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStore;
|
||||
import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
@@ -29,10 +23,7 @@ import org.mifosplatform.organisation.office.domain.OfficeRepository;
|
||||
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
|
||||
import org.mifosplatform.portfolio.client.api.ClientApiConstants;
|
||||
import org.mifosplatform.portfolio.client.data.ClientDataValidator;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
|
||||
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.client.domain.*;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
@@ -47,6 +38,11 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWritePlatformService {
|
||||
|
||||
@@ -59,12 +55,14 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
private final GroupRepository groupRepository;
|
||||
private final ClientDataValidator fromApiJsonDeserializer;
|
||||
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
|
||||
private final DocumentStoreFactory documentStoreFactory;
|
||||
private final ImageRepository imageRepository;
|
||||
|
||||
@Autowired
|
||||
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository,
|
||||
final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory,
|
||||
final GroupRepository groupRepository) {
|
||||
final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository,
|
||||
final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory,
|
||||
final GroupRepository groupRepository, DocumentStoreFactory documentStoreFactory, ImageRepository imageRepository) {
|
||||
this.context = context;
|
||||
this.clientRepository = clientRepository;
|
||||
this.officeRepository = officeRepository;
|
||||
@@ -72,6 +70,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
|
||||
this.groupRepository = groupRepository;
|
||||
this.documentStoreFactory = documentStoreFactory;
|
||||
this.imageRepository = imageRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -223,16 +223,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream) {
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream, Long fileSize) {
|
||||
try {
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
String imageUploadLocation = setupForClientImageUpdate(clientId, client);
|
||||
deletePreviousClientImage(clientId, client);
|
||||
|
||||
String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName);
|
||||
|
||||
return updateClientImage(clientId, client, imageLocation);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration();
|
||||
String imageLocation = documentStore.saveImage(inputStream, clientId, imageName, fileSize);
|
||||
return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue());
|
||||
} catch (DocumentManagementException dme) {
|
||||
logger.error(dme.getMessage(), dme);
|
||||
throw new DocumentManagementException(imageName);
|
||||
}
|
||||
}
|
||||
@@ -243,48 +243,52 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
Image image = imageRepository.findOneByClient(client);
|
||||
String imageKey = image.getKey();
|
||||
// delete image from the file system
|
||||
if (StringUtils.isNotEmpty(client.imageKey())) {
|
||||
FileUtils.deleteClientImage(clientId, client.imageKey());
|
||||
if (StringUtils.isNotEmpty((imageKey))) {
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration();
|
||||
documentStore.deleteImage(clientId, imageKey);
|
||||
this.imageRepository.delete(image);
|
||||
}
|
||||
return updateClientImage(clientId, client, null);
|
||||
|
||||
return new CommandProcessingResult(clientId) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) {
|
||||
try {
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final String imageUploadLocation = setupForClientImageUpdate(clientId, client);
|
||||
deletePreviousClientImage(clientId, client);
|
||||
|
||||
final String imageLocation = FileUtils.saveToFileSystem(encodedImage, imageUploadLocation, "image");
|
||||
DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration();
|
||||
final String imageLocation = documentStore.saveImage(encodedImage, clientId, "image");
|
||||
|
||||
return updateClientImage(clientId, client, imageLocation);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue());
|
||||
} catch (DocumentManagementException dme) {
|
||||
logger.error(dme.getMessage(), dme);
|
||||
throw new DocumentManagementException("image");
|
||||
}
|
||||
}
|
||||
|
||||
private String setupForClientImageUpdate(final Long clientId, final Client client) {
|
||||
private void deletePreviousClientImage(final Long clientId, final Client client) {
|
||||
if (client == null) { throw new ClientNotFoundException(clientId); }
|
||||
|
||||
final String imageUploadLocation = FileUtils.generateClientImageParentDirectory(clientId);
|
||||
// delete previous image from the file system
|
||||
if (StringUtils.isNotEmpty(client.imageKey())) {
|
||||
FileUtils.deleteClientImage(clientId, client.imageKey());
|
||||
Image image = imageRepository.findOneByClient(client);
|
||||
if (image != null){
|
||||
this.documentStoreFactory.getInstanceFromConfiguration().deleteImage(clientId, image.getKey());
|
||||
}
|
||||
|
||||
/** Recursively create the directory if it does not exist **/
|
||||
if (!new File(imageUploadLocation).isDirectory()) {
|
||||
new File(imageUploadLocation).mkdirs();
|
||||
}
|
||||
return imageUploadLocation;
|
||||
}
|
||||
|
||||
private CommandProcessingResult updateClientImage(final Long clientId, final Client client, final String imageLocation) {
|
||||
client.updateImageKey(imageLocation);
|
||||
this.clientRepository.save(client);
|
||||
private CommandProcessingResult updateClientImage(final Long clientId, final Client client, final String imageLocation, final String documentStoreType) {
|
||||
Image image = imageRepository.findOneByClient(client);
|
||||
if(image == null){
|
||||
image = new Image(client);
|
||||
}
|
||||
image.updateKey(imageLocation);
|
||||
image.updateStorageType(documentStoreType);
|
||||
|
||||
this.imageRepository.save(image);
|
||||
return new CommandProcessingResult(clientId);
|
||||
}
|
||||
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.mifosplatform.infrastructure.configuration.domain;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.useradministration.domain.Permission;
|
||||
import org.mifosplatform.useradministration.domain.PermissionRepository;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ConfigurationDomainServiceJpaTest {
|
||||
|
||||
private final String S3 = "amazon-S3";
|
||||
private PermissionRepository permissionRepositoryMock = mock(PermissionRepository.class);;
|
||||
private GlobalConfigurationRepository globalConfigurationRepositoryMock = mock(GlobalConfigurationRepository.class);
|
||||
|
||||
@Test
|
||||
public void shouldReturnTrueIfAmazonS3ConfigurationIsEnabled(){
|
||||
when(globalConfigurationRepositoryMock.findOneByName(S3)).thenReturn(new GlobalConfigurationProperty(S3, true));
|
||||
ConfigurationDomainServiceJpa configurationDomainServiceJpa = new ConfigurationDomainServiceJpa(permissionRepositoryMock, globalConfigurationRepositoryMock);
|
||||
|
||||
boolean actualIsS3Enabled = configurationDomainServiceJpa.isAmazonS3Enabled();
|
||||
|
||||
assertTrue(actualIsS3Enabled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnFalseIfAmazonS3ConfigurationIsDisabled(){
|
||||
when(globalConfigurationRepositoryMock.findOneByName(S3)).thenReturn(new GlobalConfigurationProperty(S3, false));
|
||||
ConfigurationDomainServiceJpa configurationDomainServiceJpa = new ConfigurationDomainServiceJpa(permissionRepositoryMock, globalConfigurationRepositoryMock);
|
||||
|
||||
boolean actualIsS3Enabled = configurationDomainServiceJpa.isAmazonS3Enabled();
|
||||
|
||||
assertFalse(actualIsS3Enabled);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DocumentStoreFactoryTest {
|
||||
|
||||
@Test
|
||||
public void shouldReturnS3DocumentStoreObjectIfS3Enabled(){
|
||||
S3ClientFactory s3ClientFactoryMock = mock(S3ClientFactory.class);
|
||||
when(s3ClientFactoryMock.getS3Client()).thenReturn(null);
|
||||
|
||||
ExternalServicesDomainService externalServicesDomainServiceMock = mock(ExternalServicesDomainService.class);
|
||||
when(externalServicesDomainServiceMock.getValue("s3_bucket_name")).thenReturn("bucket_name");
|
||||
|
||||
ConfigurationDomainService configurationDomainServiceMock = mock(ConfigurationDomainService.class);
|
||||
when(configurationDomainServiceMock.isAmazonS3Enabled()).thenReturn(true);
|
||||
|
||||
ApplicationContext applicationContextMock = mock(ApplicationContext.class);
|
||||
|
||||
when(applicationContextMock.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainServiceMock);
|
||||
when(applicationContextMock.getBean("s3ClientFactory", S3ClientFactory.class)).thenReturn(s3ClientFactoryMock);
|
||||
when(applicationContextMock.getBean("externalServicesDomainService", ExternalServicesDomainService.class)).thenReturn(externalServicesDomainServiceMock);
|
||||
|
||||
|
||||
DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContextMock);
|
||||
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),S3DocumentStore.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnFileSystemDocumentStoreObjectIfS3IsDisabled(){
|
||||
ConfigurationDomainService configurationDomainService = mock(ConfigurationDomainService.class);
|
||||
when(configurationDomainService.isAmazonS3Enabled()).thenReturn(false);
|
||||
|
||||
ApplicationContext applicationContext = mock(ApplicationContext.class);
|
||||
when(applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainService);
|
||||
|
||||
DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContext);
|
||||
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),FileSystemDocumentStore.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FileDocumentStoreTest {
|
||||
|
||||
@Test
|
||||
public void shouldRetrieveDocument(){
|
||||
FileSystemDocumentStore fileSystemDocumentStore = new FileSystemDocumentStore();
|
||||
DocumentData documentDataMock = mock(DocumentData.class);
|
||||
when(documentDataMock.fileName()).thenReturn("some_file");
|
||||
when(documentDataMock.fileName()).thenReturn("content_type");
|
||||
when(documentDataMock.fileLocation()).thenReturn("abcd");
|
||||
|
||||
FileData fileData = fileSystemDocumentStore.retrieveDocument(documentDataMock);
|
||||
|
||||
assertEquals(fileData.name(), documentDataMock.fileName());
|
||||
assertEquals(fileData.contentType(), documentDataMock.contentType());
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import com.amazonaws.AmazonClientException;
|
||||
import com.amazonaws.AmazonServiceException;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class S3DocumentStoreTest {
|
||||
|
||||
InputStream inputStream;
|
||||
AmazonS3Client s3ClientMock;
|
||||
DocumentCommand documentCommand;
|
||||
DocumentData documentDataMock;
|
||||
|
||||
@Test
|
||||
public void shouldGenerateParentDirectoryFromEntityDetails(){
|
||||
S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null);
|
||||
String actualParentDirectory = s3DocumentStore.generateFileParentDirectory("entity_type", 1234L);
|
||||
assertTrue(actualParentDirectory.startsWith("documents/entity_type/1234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGenerateClientImageDirectoryFromResourceId(){
|
||||
S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null);
|
||||
String actualParentDirectory = s3DocumentStore.generateClientImageParentDirectory(1234L);
|
||||
assertTrue(actualParentDirectory.startsWith("images/clients/1234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSaveADocumentToS3() throws IOException {
|
||||
initUpload();
|
||||
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenReturn(new PutObjectResult());
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
|
||||
|
||||
s3DocumentStore.saveDocument(inputStream, documentCommand);
|
||||
|
||||
verify(s3ClientMock, times(1)).putObject(Matchers.<PutObjectRequest>anyObject());
|
||||
}
|
||||
|
||||
@Test(expected = DocumentManagementException.class)
|
||||
public void shouldCatchAmazonServiceExceptionWhenUnableToUploadDocumentToS3() throws IOException {
|
||||
initUpload();
|
||||
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenThrow(new AmazonServiceException("serviceException"));
|
||||
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
|
||||
|
||||
s3DocumentStore.saveDocument(inputStream, documentCommand);
|
||||
}
|
||||
|
||||
@Test(expected = DocumentManagementException.class)
|
||||
public void shouldCatchAmazonClientExceptionWhenUnableToUploadDocumentToS3() throws IOException {
|
||||
initUpload();
|
||||
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenThrow(new AmazonClientException("clientException"));
|
||||
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
|
||||
|
||||
s3DocumentStore.saveDocument(inputStream, documentCommand);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRetrieveDocumentFromS3() throws IOException {
|
||||
initRetrieve();
|
||||
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenReturn(new S3Object());
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
|
||||
|
||||
FileData fileData = s3DocumentStore.retrieveDocument(documentDataMock);
|
||||
|
||||
verify(s3ClientMock, times(1)).getObject(Matchers.<GetObjectRequest>anyObject());
|
||||
assertEquals(fileData.name(), "name");
|
||||
assertEquals(fileData.contentType(), "type");
|
||||
}
|
||||
|
||||
@Test(expected = DocumentNotFoundException.class)
|
||||
public void shouldCatchAmazonServiceExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException {
|
||||
initRetrieve();
|
||||
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenThrow(new AmazonServiceException("serviceException"));
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
|
||||
|
||||
s3DocumentStore.retrieveDocument(documentDataMock);
|
||||
}
|
||||
|
||||
@Test(expected = DocumentNotFoundException.class)
|
||||
public void shouldCatchAmazonClientExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException {
|
||||
initRetrieve();
|
||||
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenThrow(new AmazonClientException("clientException"));
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
|
||||
|
||||
s3DocumentStore.retrieveDocument(documentDataMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteImageFromS3() throws IOException {
|
||||
AmazonS3Client s3ClientMock = mock(AmazonS3Client.class);
|
||||
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
|
||||
|
||||
s3DocumentStore.deleteImage(123L,"imagekey");
|
||||
|
||||
verify(s3ClientMock, times(1)).deleteObject(Matchers.<DeleteObjectRequest>anyObject());
|
||||
}
|
||||
|
||||
private void initRetrieve() {
|
||||
s3ClientMock = mock(AmazonS3Client.class);
|
||||
|
||||
documentDataMock = mock(DocumentData.class);
|
||||
when(documentDataMock.fileLocation()).thenReturn("path");
|
||||
when(documentDataMock.fileName()).thenReturn("name");
|
||||
when(documentDataMock.contentType()).thenReturn("type");
|
||||
}
|
||||
|
||||
|
||||
private void initUpload(){
|
||||
inputStream = S3DocumentStoreTest.class.getResourceAsStream("DummyString");
|
||||
s3ClientMock = mock(AmazonS3Client.class);
|
||||
documentCommand = new DocumentCommand(null, -1l, "parentEntityType", 1l, null, "someFileName", 3l, null, null, null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user