Merge pull request #325 from vishwasbabu/harshac-MIFOSX69
Amazon S3 support and refactoring
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0);
|
||||
|
||||
/*New table for storing details of external services used*/
|
||||
CREATE TABLE `c_external_service` (
|
||||
`name` VARCHAR(150) NOT NULL,
|
||||
`value` VARCHAR(250) NULL DEFAULT NULL,
|
||||
UNIQUE INDEX `name` (`name`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO c_external_service (name) VALUES ('s3_bucket_name');
|
||||
INSERT INTO c_external_service (name) VALUES ('s3_access_key');
|
||||
INSERT INTO c_external_service (name) VALUES ('s3_secret_key');
|
||||
|
||||
|
||||
|
||||
/*Image tables stores details of all images*/
|
||||
CREATE TABLE IF NOT EXISTS `m_image`(
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`location` varchar(500),
|
||||
`storage_type_enum` SMALLINT(5),
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
|
||||
/*Client points to image table*/
|
||||
ALTER TABLE `m_client`
|
||||
ADD COLUMN `image_id` BIGINT(20) NULL DEFAULT NULL AFTER `display_name`,
|
||||
ADD CONSTRAINT `FK_m_client_m_image` FOREIGN KEY (`image_id`) REFERENCES `m_image` (`id`);
|
||||
|
||||
/*Move existing image locations to new table*/
|
||||
drop procedure if exists migrate_customer_image_data;
|
||||
|
||||
delimiter #
|
||||
create procedure migrate_customer_image_data()
|
||||
begin
|
||||
declare v_counter int unsigned default 0;
|
||||
declare num_of_clients INT DEFAULT 0;
|
||||
declare curr_client INT DEFAULT 0;
|
||||
declare curr_image INT DEFAULT 0;
|
||||
declare prev_image INT DEFAULT 0;
|
||||
SELECT COUNT(*) FROM m_client INTO num_of_clients;
|
||||
while v_counter < num_of_clients do
|
||||
INSERT INTO m_image(`location`,`storage_type_enum`) select image_key, 1 FROM m_client where image_key is not null LIMIT v_counter,1;
|
||||
select ifnull(max(`id`),0) from m_image INTO curr_image;
|
||||
select id FROM m_client where image_key is not null limit v_counter,1 INTO curr_client;
|
||||
IF (prev_image != curr_image) THEN UPDATE m_client set image_id = curr_image where id=curr_client;
|
||||
END IF;
|
||||
set prev_image=curr_image;
|
||||
set v_counter=v_counter+1;
|
||||
end while;
|
||||
delete from m_image where `location` is null;
|
||||
end#
|
||||
|
||||
delimiter ;
|
||||
call migrate_customer_image_data();
|
||||
|
||||
/*Now drop the procedure*/
|
||||
drop procedure if exists migrate_customer_image_data;
|
||||
|
||||
/*Delete image key from client*/
|
||||
ALTER TABLE `m_client` DROP COLUMN `image_key`;
|
||||
|
||||
|
||||
/*Add storage type for m_document table and update existing documents to file storage*/
|
||||
ALTER TABLE m_document ADD COLUMN storage_type_enum SMALLINT(5);
|
||||
UPDATE m_document set storage_type_enum=1;
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.infrastructure.configuration.data;
|
||||
|
||||
public class S3CredentialsData {
|
||||
|
||||
private final String bucketName;
|
||||
private final String accessKey;
|
||||
private final String secretKey;
|
||||
|
||||
public S3CredentialsData(String bucketName, String accessKey, String secretKey) {
|
||||
this.bucketName = bucketName;
|
||||
this.accessKey = accessKey;
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return this.bucketName;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
}
|
||||
+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 javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "c_external_service", uniqueConstraints = { @UniqueConstraint(columnNames = ("name")) })
|
||||
public class ExternalService extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "value", nullable = false)
|
||||
private String value;
|
||||
|
||||
public ExternalService(final String name, final String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
protected ExternalService() {}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
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<ExternalService, Long>, JpaSpecificationExecutor<ExternalService> {
|
||||
|
||||
ExternalService findOneByName(String name);
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.mifosplatform.infrastructure.configuration.service;
|
||||
|
||||
public class ExternalServicesConstants {
|
||||
|
||||
public static final String S3_BUCKET_NAME = "s3_bucket_name";
|
||||
public static final String S3_ACCESS_KEY = "s3_access_key";
|
||||
public static final String S3_SECRET_KEY = "s3_secret_key";
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.mifosplatform.infrastructure.configuration.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||
|
||||
public interface ExternalServicesReadPlatformService {
|
||||
|
||||
S3CredentialsData getS3Credentials();
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package org.mifosplatform.infrastructure.configuration.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ExternalServicesReadPlatformServiceImpl implements ExternalServicesReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public ExternalServicesReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
private static final class S3CredentialsDataExtractor implements ResultSetExtractor<S3CredentialsData> {
|
||||
|
||||
@Override
|
||||
public S3CredentialsData extractData(ResultSet rs) throws SQLException, DataAccessException {
|
||||
String accessKey = null;
|
||||
String bucketName = null;
|
||||
String secretKey = null;
|
||||
while (rs.next()) {
|
||||
if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_ACCESS_KEY)) {
|
||||
accessKey = rs.getString("value");
|
||||
} else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_BUCKET_NAME)) {
|
||||
bucketName = rs.getString("value");
|
||||
} else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_SECRET_KEY)) {
|
||||
secretKey = rs.getString("value");
|
||||
}
|
||||
}
|
||||
return new S3CredentialsData(bucketName, accessKey, secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3CredentialsData getS3Credentials() {
|
||||
final ResultSetExtractor<S3CredentialsData> resultSetExtractor = new S3CredentialsDataExtractor();
|
||||
final String sql = "SELECT es.name, es.value FROM c_external_service es where es.name in('s3_bucket_name','s3_access_key','s3_secret_key')";
|
||||
final S3CredentialsData s3CredentialsData = this.jdbcTemplate.query(sql, resultSetExtractor, new Object[] {});
|
||||
return s3CredentialsData;
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +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.api;
|
||||
|
||||
public class ApiConstants {
|
||||
|
||||
// TODO:Vishwas Need to move these settings to the Database
|
||||
public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5;
|
||||
|
||||
// TODO:Vishwas Need to move these settings to the Database
|
||||
public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1;
|
||||
}
|
||||
+2
-1
@@ -13,12 +13,13 @@ import org.springframework.security.authentication.dao.SaltSource;
|
||||
import org.springframework.security.authentication.encoding.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Service(value = "applicationPasswordEncoder")
|
||||
@Scope("singleton")
|
||||
public class DefaultPlatformPasswordEncoder implements PlatformPasswordEncoder {
|
||||
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final SaltSource saltSource;
|
||||
private final SaltSource saltSource;
|
||||
|
||||
@Autowired
|
||||
public DefaultPlatformPasswordEncoder(final PasswordEncoder passwordEncoder, final SaltSource saltSource) {
|
||||
|
||||
+3
-3
@@ -29,7 +29,6 @@ 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.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.ReportData;
|
||||
@@ -38,6 +37,7 @@ import org.mifosplatform.infrastructure.dataqueries.data.ReportParameterJoinData
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
|
||||
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
|
||||
import org.mifosplatform.infrastructure.dataqueries.exception.ReportNotFoundException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
|
||||
@@ -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 = FileSystemContentRepository.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 = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "";
|
||||
if (!new File(fileLocation).isDirectory()) {
|
||||
new File(fileLocation).mkdirs();
|
||||
}
|
||||
|
||||
+19
-39
@@ -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
|
||||
@@ -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");
|
||||
@@ -166,9 +150,6 @@ public class DocumentManagementApiResource {
|
||||
|
||||
final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId);
|
||||
|
||||
// we do not want to send document location as a part of the response
|
||||
documentData.setLocation(null);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, documentData, this.RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
@@ -182,11 +163,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.retrieveFileData(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();
|
||||
}
|
||||
|
||||
+37
-52
@@ -3,9 +3,8 @@
|
||||
* 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.api;
|
||||
package org.mifosplatform.infrastructure.documentmanagement.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
@@ -22,19 +21,16 @@ import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
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.documentmanagement.contentrepository.ContentRepositoryUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils.IMAGE_FILE_EXTENSION;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.service.ImageReadPlatformService;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.service.ImageWritePlatformService;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -44,22 +40,26 @@ import com.sun.jersey.core.header.FormDataContentDisposition;
|
||||
import com.sun.jersey.multipart.FormDataBodyPart;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
|
||||
@Path("/clients/{clientId}/images")
|
||||
/**
|
||||
* TODO Vishwas need to change the url to entity/entityId/images to make the
|
||||
* image management API's generic for other entities like staff etc
|
||||
**/
|
||||
@Path("{clients}/{clientId}/images")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class ClientImagesApiResource {
|
||||
public class ImagesApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientReadPlatformService clientReadPlatformService;
|
||||
private final ClientWritePlatformService clientWritePlatformService;
|
||||
private final ImageReadPlatformService imageReadPlatformService;
|
||||
private final ImageWritePlatformService imageWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
||||
|
||||
@Autowired
|
||||
public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
|
||||
final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||
public ImagesApiResource(final PlatformSecurityContext context, final ImageReadPlatformService readPlatformService,
|
||||
final ImageWritePlatformService imageWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||
this.context = context;
|
||||
this.clientReadPlatformService = readPlatformService;
|
||||
this.clientWritePlatformService = clientWritePlatformService;
|
||||
this.imageReadPlatformService = readPlatformService;
|
||||
this.imageWritePlatformService = imageWritePlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
}
|
||||
|
||||
@@ -76,12 +76,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);
|
||||
ContentRepositoryUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
||||
ContentRepositoryUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
||||
|
||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
||||
inputStream);
|
||||
final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
||||
inputStream, fileSize);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
@@ -94,9 +93,9 @@ 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 = ContentRepositoryUtils.extractImageFromDataURL(jsonRequestBody);
|
||||
|
||||
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
||||
final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
@@ -111,20 +110,17 @@ public class ClientImagesApiResource {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||
|
||||
final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
|
||||
|
||||
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
||||
final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(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 = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
||||
if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
||||
}
|
||||
|
||||
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeFromFile(clientData.imageKey());
|
||||
|
||||
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(imageData.getContent());
|
||||
return clientImageAsBase64Text;
|
||||
}
|
||||
|
||||
@@ -134,26 +130,15 @@ public class ClientImagesApiResource {
|
||||
public Response downloadClientImage(@PathParam("clientId") final Long clientId) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||
final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
|
||||
final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId);
|
||||
|
||||
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(imageData.getContent());
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + imageData.getEntityDisplayName() + IMAGE_FILE_EXTENSION.JPEG
|
||||
+ "\"");
|
||||
|
||||
// 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", imageData.contentType());
|
||||
return response.build();
|
||||
}
|
||||
|
||||
@@ -187,7 +172,7 @@ public class ClientImagesApiResource {
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String deleteClientImage(@PathParam("clientId") final Long clientId) {
|
||||
this.clientWritePlatformService.deleteClientImage(clientId);
|
||||
this.imageWritePlatformService.deleteClientImage(clientId);
|
||||
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId));
|
||||
}
|
||||
|
||||
+9
@@ -22,6 +22,7 @@ public class DocumentCommand {
|
||||
private Long size;
|
||||
private String type;
|
||||
private String location;
|
||||
private Integer storageType;
|
||||
|
||||
private final Set<String> modifiedParameters;
|
||||
|
||||
@@ -95,6 +96,14 @@ public class DocumentCommand {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public Integer getStorageType() {
|
||||
return this.storageType;
|
||||
}
|
||||
|
||||
public void setStorageType(Integer storageType) {
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
public boolean isNameChanged() {
|
||||
return this.modifiedParameters.contains("name");
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
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.data.ImageData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
|
||||
public interface ContentRepository {
|
||||
|
||||
public StorageType type = null;
|
||||
|
||||
// TODO:Vishwas Need to move these settings to the Database
|
||||
public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5;
|
||||
|
||||
// TODO:Vishwas Need to move these settings to the Database
|
||||
public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1;
|
||||
|
||||
public abstract String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand);
|
||||
|
||||
public abstract void deleteFile(String fileName, String documentPath);
|
||||
|
||||
public abstract FileData fetchFile(DocumentData documentData);
|
||||
|
||||
public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize);
|
||||
|
||||
public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName);
|
||||
|
||||
public abstract void deleteImage(final Long resourceId, final String location);
|
||||
|
||||
public abstract ImageData fetchImage(ImageData imageData);
|
||||
|
||||
public abstract StorageType getStorageType();
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||
|
||||
import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.configuration.service.ExternalServicesReadPlatformService;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ContentRepositoryFactory {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
private ExternalServicesReadPlatformService externalServicesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public ContentRepositoryFactory(final ApplicationContext applicationContext,
|
||||
final ExternalServicesReadPlatformService externalServicesReadPlatformService) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.externalServicesReadPlatformService = externalServicesReadPlatformService;
|
||||
}
|
||||
|
||||
public ContentRepository getRepository() {
|
||||
ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa",
|
||||
ConfigurationDomainService.class);
|
||||
if (configurationDomainServiceJpa.isAmazonS3Enabled()) { return createS3DocumentStore(); }
|
||||
return new FileSystemContentRepository();
|
||||
}
|
||||
|
||||
public ContentRepository getRepository(StorageType documentStoreType) {
|
||||
if (documentStoreType == StorageType.FILE_SYSTEM) { return new FileSystemContentRepository(); }
|
||||
return createS3DocumentStore();
|
||||
}
|
||||
|
||||
private ContentRepository createS3DocumentStore() {
|
||||
S3CredentialsData s3CredentialsData = externalServicesReadPlatformService.getS3Credentials();
|
||||
return new S3ContentRepository(s3CredentialsData.getBucketName(), s3CredentialsData.getSecretKey(),
|
||||
s3CredentialsData.getAccessKey());
|
||||
}
|
||||
}
|
||||
+39
-129
@@ -1,15 +1,5 @@
|
||||
/**
|
||||
* 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;
|
||||
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||
|
||||
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;
|
||||
@@ -20,17 +10,11 @@ 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 org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
public class ContentRepositoryUtils {
|
||||
|
||||
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 Random random = new Random();
|
||||
|
||||
public static enum IMAGE_MIME_TYPE {
|
||||
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
||||
@@ -44,7 +28,7 @@ public class FileUtils {
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static enum IMAGE_FILE_EXTENSION {
|
||||
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
||||
@@ -58,7 +42,7 @@ public class FileUtils {
|
||||
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(
|
||||
@@ -73,107 +57,6 @@ public class FileUtils {
|
||||
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); }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,6 +93,17 @@ public class FileUtils {
|
||||
return new Base64EncodedImage(base64EncodedString, fileExtension);
|
||||
}
|
||||
|
||||
public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name) {
|
||||
/**
|
||||
* 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)) > ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB)) { throw new ContentManagementException(
|
||||
name, fileSize, ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB); }
|
||||
}
|
||||
|
||||
public static void validateClientImageNotEmpty(String imageFileName) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
if (imageFileName == null) {
|
||||
@@ -223,12 +117,28 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* 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.documentmanagement.contentrepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
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.data.ImageData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
|
||||
public class FileSystemContentRepository implements ContentRepository {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(FileSystemContentRepository.class);
|
||||
|
||||
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
||||
|
||||
@Override
|
||||
public String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand) {
|
||||
String fileName = documentCommand.getFileName();
|
||||
String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId());
|
||||
|
||||
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName);
|
||||
makeDirectories(uploadDocumentLocation);
|
||||
|
||||
String fileLocation = uploadDocumentLocation + File.separator + fileName;
|
||||
|
||||
writeFileToFileSystem(fileName, uploadedInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) {
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
|
||||
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName);
|
||||
makeDirectories(uploadImageLocation);
|
||||
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||
|
||||
writeFileToFileSystem(imageName, uploadedInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) {
|
||||
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 ContentManagementException(imageName, 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 deleteFile(String fileName, String documentPath) {
|
||||
final boolean fileDeleted = deleteFile(documentPath);
|
||||
if (!fileDeleted) { throw new ContentManagementException(fileName, null); }
|
||||
}
|
||||
|
||||
private boolean deleteFile(String documentPath) {
|
||||
final File fileToBeDeleted = new File(documentPath);
|
||||
return fileToBeDeleted.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.FILE_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileData fetchFile(DocumentData documentData) {
|
||||
File file = new File(documentData.fileLocation());
|
||||
return new FileData(file, documentData.fileName(), documentData.contentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData fetchImage(ImageData imageData) {
|
||||
File file = new File(imageData.location());
|
||||
imageData.updateContent(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 FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator
|
||||
+ ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + File.separator + "documents" + File.separator
|
||||
+ entityType + File.separator + entityId + File.separator + ContentRepositoryUtils.generateRandomString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate directory path for storing new Image
|
||||
*/
|
||||
private String generateClientImageParentDirectory(final Long resourceId) {
|
||||
return FileSystemContentRepository.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(String fileName, InputStream uploadedInputStream, String fileLocation) {
|
||||
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 ContentManagementException(fileName, ioException.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.contentrepository;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
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.data.ImageData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.amazonaws.AmazonClientException;
|
||||
import com.amazonaws.AmazonServiceException;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.model.DeleteObjectRequest;
|
||||
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||
import com.amazonaws.services.s3.model.ObjectMetadata;
|
||||
import com.amazonaws.services.s3.model.PutObjectRequest;
|
||||
import com.amazonaws.services.s3.model.S3Object;
|
||||
import com.lowagie.text.pdf.codec.Base64;
|
||||
|
||||
public class S3ContentRepository implements ContentRepository {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(S3ContentRepository.class);
|
||||
|
||||
private final String s3BucketName;
|
||||
private AmazonS3 s3Client;
|
||||
|
||||
public S3ContentRepository(String bucketName, String secretKey, String accessKey) {
|
||||
this.s3BucketName = bucketName;
|
||||
this.s3Client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveFile(InputStream toUpload, DocumentCommand documentCommand) {
|
||||
String fileName = documentCommand.getFileName();
|
||||
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName);
|
||||
|
||||
String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId());
|
||||
String uploadDocFullPath = uploadDocFolder + File.separator + fileName;
|
||||
|
||||
uploadDocument(fileName, toUpload, uploadDocFullPath);
|
||||
return uploadDocFullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteFile(String documentName, String documentPath) {
|
||||
try {
|
||||
deleteObjectFromS3(documentPath);
|
||||
} catch (AmazonClientException ace) {
|
||||
throw new ContentManagementException(documentName, ace.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) {
|
||||
ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName);
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName;
|
||||
|
||||
uploadDocument(imageName, toUploadInputStream, fileLocation);
|
||||
return fileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) {
|
||||
String uploadImageLocation = generateClientImageParentDirectory(resourceId);
|
||||
String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension();
|
||||
InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString()));
|
||||
|
||||
uploadDocument(imageName, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
return StorageType.S3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileData fetchFile(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 (AmazonClientException ace) {
|
||||
logger.error(ace.getMessage());
|
||||
throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId());
|
||||
}
|
||||
return fileData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData fetchImage(ImageData imageData) {
|
||||
S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.location()));
|
||||
imageData.updateContent(s3object.getObjectContent());
|
||||
return imageData;
|
||||
}
|
||||
|
||||
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 String generateFileParentDirectory(String entityType, Long entityId) {
|
||||
return "documents" + File.separator + entityType + File.separator + entityId + File.separator
|
||||
+ ContentRepositoryUtils.generateRandomString();
|
||||
}
|
||||
|
||||
private String generateClientImageParentDirectory(Long resourceId) {
|
||||
return "images" + File.separator + "clients" + File.separator + resourceId;
|
||||
}
|
||||
|
||||
private void deleteObjectFromS3(String location) {
|
||||
s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location));
|
||||
}
|
||||
|
||||
private void uploadDocument(String filename, InputStream inputStream, String s3UploadLocation) throws ContentManagementException {
|
||||
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 (AmazonClientException ace) {
|
||||
String message = ace.getMessage();
|
||||
throw new ContentManagementException(filename, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-7
@@ -5,16 +5,15 @@
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
|
||||
/**
|
||||
* Immutable data object represent document being managed on platform.
|
||||
*/
|
||||
public class DocumentData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
@SuppressWarnings("unused")
|
||||
private final String parentEntityType;
|
||||
@SuppressWarnings("unused")
|
||||
private final Long parentEntityId;
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
@@ -24,10 +23,11 @@ public class DocumentData {
|
||||
private final String type;
|
||||
@SuppressWarnings("unused")
|
||||
private final String description;
|
||||
private String location;
|
||||
private final String location;
|
||||
private final Integer 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, Integer storageType) {
|
||||
this.id = id;
|
||||
this.parentEntityType = parentEntityType;
|
||||
this.parentEntityId = parentEntityId;
|
||||
@@ -37,6 +37,7 @@ public class DocumentData {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.location = location;
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
public String contentType() {
|
||||
@@ -51,8 +52,20 @@ public class DocumentData {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public void setLocation(final String location) {
|
||||
this.location = location;
|
||||
public StorageType storageType() {
|
||||
return StorageType.fromInt(storageType);
|
||||
}
|
||||
|
||||
public String getParentEntityType() {
|
||||
return this.parentEntityType;
|
||||
}
|
||||
|
||||
public Long getParentEntityId() {
|
||||
return this.parentEntityId;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
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); }
|
||||
return inputStream;
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.error(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
|
||||
public class ImageData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long imageId;
|
||||
private final String location;
|
||||
private final Integer storageType;
|
||||
private final String entityDisplayName;
|
||||
|
||||
private File file;
|
||||
private String contentType;
|
||||
private InputStream inputStream;
|
||||
|
||||
public ImageData(Long imageId, String location, Integer storageType, String entityDisplayName) {
|
||||
this.imageId = imageId;
|
||||
this.location = location;
|
||||
this.storageType = storageType;
|
||||
this.entityDisplayName = entityDisplayName;
|
||||
}
|
||||
|
||||
public byte[] getContent() {
|
||||
// TODO Vishwas Fix error handling
|
||||
try {
|
||||
if (inputStream == null) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
return IOUtils.toByteArray(fileInputStream);
|
||||
}
|
||||
|
||||
return IOUtils.toByteArray(inputStream);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String setImageContentType() {
|
||||
String contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.JPEG.getValue();
|
||||
|
||||
if (file != null) {
|
||||
String fileName = file.getName();
|
||||
|
||||
if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||
contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.GIF.getValue();
|
||||
} else if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||
contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.PNG.getValue();
|
||||
}
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public String contentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
public StorageType storageType() {
|
||||
return StorageType.fromInt(storageType);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.file.getName();
|
||||
}
|
||||
|
||||
public String location() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public void updateContent(InputStream objectContent) {
|
||||
this.inputStream = objectContent;
|
||||
}
|
||||
|
||||
public void updateContent(File file) {
|
||||
this.file = file;
|
||||
setImageContentType();
|
||||
}
|
||||
|
||||
public String getEntityDisplayName() {
|
||||
return this.entityDisplayName;
|
||||
}
|
||||
|
||||
}
|
||||
+10
-4
@@ -41,15 +41,18 @@ public class Document extends AbstractPersistable<Long> {
|
||||
@Column(name = "location", length = 500)
|
||||
private String location;
|
||||
|
||||
@Column(name = "storage_type_enum")
|
||||
private Integer 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 StorageType 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 StorageType storageType) {
|
||||
this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null);
|
||||
this.parentEntityId = parentEntityId;
|
||||
this.name = StringUtils.defaultIfEmpty(name, null);
|
||||
@@ -58,6 +61,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 +83,6 @@ public class Document extends AbstractPersistable<Long> {
|
||||
if (command.isSizeChanged()) {
|
||||
this.size = command.getSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getParentEntityType() {
|
||||
@@ -146,4 +149,7 @@ public class Document extends AbstractPersistable<Long> {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public StorageType storageType() {
|
||||
return StorageType.fromInt(this.storageType);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.documentmanagement.domain;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_image")
|
||||
public final class Image extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "location", length = 500)
|
||||
private String location;
|
||||
|
||||
@Column(name = "storage_type_enum")
|
||||
private Integer storageType;
|
||||
|
||||
public Image(String location, StorageType storageType) {
|
||||
this.location = location;
|
||||
this.storageType = storageType.getValue();
|
||||
}
|
||||
|
||||
protected Image() {
|
||||
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public Integer getStorageType() {
|
||||
return this.storageType;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void setStorageType(Integer storageType) {
|
||||
this.storageType = storageType;
|
||||
}
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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.documentmanagement.domain;
|
||||
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface ImageRepository extends JpaRepository<Image, Long>, JpaSpecificationExecutor<Client> {}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.mifosplatform.infrastructure.documentmanagement.domain;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum StorageType {
|
||||
FILE_SYSTEM(1), S3(2);
|
||||
|
||||
private Integer value;
|
||||
|
||||
StorageType(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private static final Map<Integer, StorageType> intToEnumMap = new HashMap<Integer, StorageType>();
|
||||
static {
|
||||
for (final StorageType type : StorageType.values()) {
|
||||
intToEnumMap.put(type.value, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static StorageType fromInt(final int i) {
|
||||
final StorageType type = intToEnumMap.get(Integer.valueOf(i));
|
||||
return type;
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -7,13 +7,14 @@ package org.mifosplatform.infrastructure.documentmanagement.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class DocumentManagementException extends AbstractPlatformDomainRuleException {
|
||||
public class ContentManagementException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public DocumentManagementException(final String name) {
|
||||
super("error.msg.document.save", "Error while manipulating file " + name + " due to a File system / Amazon S3 issue", name);
|
||||
public ContentManagementException(final String filename, final String message) {
|
||||
super("error.msg.document.save",
|
||||
"Error while manipulating file " + filename + " due to a File system / Amazon S3 issue " + message, filename, message);
|
||||
}
|
||||
|
||||
public DocumentManagementException(final String name, final Long fileSize, final int maxFileSize) {
|
||||
public ContentManagementException(final String name, final Long fileSize, final int maxFileSize) {
|
||||
super("error.msg.document.file.too.big", "Unable to save the document with name" + name + " since its file Size of " + fileSize
|
||||
/ (1024 * 1024) + " MB exceeds the max permissable file size of " + maxFileSize + " MB", name, fileSize);
|
||||
}
|
||||
+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 retrieveFileData(String entityType, Long entityId, Long documentId);
|
||||
|
||||
DocumentData retrieveDocument(String entityType, Long entityId, Long documentId);
|
||||
|
||||
}
|
||||
+51
-20
@@ -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.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory;
|
||||
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 ContentRepositoryFactory contentRepositoryFactory;
|
||||
|
||||
@Autowired
|
||||
public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) {
|
||||
public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
|
||||
final ContentRepositoryFactory documentStoreFactory) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.contentRepositoryFactory = documentStoreFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,40 +46,60 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ
|
||||
// TODO verify if the entities are valid and a user
|
||||
// has data
|
||||
// scope for the particular entities
|
||||
final DocumentMapper mapper = new DocumentMapper(true);
|
||||
final DocumentMapper mapper = new DocumentMapper(true, true);
|
||||
final String sql = "select " + mapper.schema() + " order by d.id";
|
||||
return this.jdbcTemplate.query(sql, mapper, new Object[] { entityType, entityId });
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentData retrieveDocument(final String entityType, final Long entityId, final Long documentId) {
|
||||
|
||||
public FileData retrieveFileData(final String entityType, final Long entityId, final Long documentId) {
|
||||
try {
|
||||
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 });
|
||||
final DocumentMapper mapper = new DocumentMapper(false, false);
|
||||
DocumentData documentData = fetchDocumentDetails(entityType, entityId, documentId, mapper);
|
||||
ContentRepository contentRepository = contentRepositoryFactory.getRepository(documentData.storageType());
|
||||
return contentRepository.fetchFile(documentData);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new DocumentNotFoundException(entityType, entityId, documentId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentData retrieveDocument(String entityType, Long entityId, Long documentId) {
|
||||
try {
|
||||
final DocumentMapper mapper = new DocumentMapper(true, true);
|
||||
return fetchDocumentDetails(entityType, entityId, documentId, mapper);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new DocumentNotFoundException(entityType, entityId, documentId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entityType
|
||||
* @param entityId
|
||||
* @param documentId
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
private DocumentData fetchDocumentDetails(final String entityType, final Long entityId, final Long documentId,
|
||||
final DocumentMapper mapper) {
|
||||
final String sql = "select " + mapper.schema() + " and d.id=? ";
|
||||
return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { entityType, entityId, documentId });
|
||||
}
|
||||
|
||||
private static final class DocumentMapper implements RowMapper<DocumentData> {
|
||||
|
||||
private final boolean hideLocation;
|
||||
private final boolean hideStorageType;
|
||||
|
||||
public DocumentMapper(final boolean hideLocation) {
|
||||
public DocumentMapper(final boolean hideLocation, final boolean hideStorageType) {
|
||||
this.hideLocation = hideLocation;
|
||||
this.hideStorageType = hideStorageType;
|
||||
}
|
||||
|
||||
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_enum as storageType"
|
||||
+ " from m_document d where d.parent_entity_type=? and d.parent_entity_id=?";
|
||||
}
|
||||
|
||||
@@ -89,11 +115,16 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ
|
||||
final String fileType = rs.getString("fileType");
|
||||
final String description = rs.getString("description");
|
||||
String location = null;
|
||||
Integer storageType = null;
|
||||
if (!this.hideLocation) {
|
||||
location = rs.getString("location");
|
||||
}
|
||||
|
||||
return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location);
|
||||
if (!this.hideStorageType) {
|
||||
storageType = rs.getInt("storageType");
|
||||
}
|
||||
return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location,
|
||||
storageType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+24
-44
@@ -5,18 +5,18 @@
|
||||
*/
|
||||
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.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommandValidator;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.Document;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.DocumentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.InvalidEntityTypeForDocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
@@ -34,11 +34,14 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final DocumentRepository documentRepository;
|
||||
private final ContentRepositoryFactory contentRepositoryFactory;
|
||||
|
||||
@Autowired
|
||||
public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository) {
|
||||
public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
final DocumentRepository documentRepository, final ContentRepositoryFactory documentStoreFactory) {
|
||||
this.context = context;
|
||||
this.documentRepository = documentRepository;
|
||||
this.contentRepositoryFactory = documentStoreFactory;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -53,19 +56,13 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
validator.validateForCreate();
|
||||
|
||||
final String fileUploadLocation = FileUtils.generateFileParentDirectory(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId());
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository();
|
||||
|
||||
/** 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 = contentRepository.saveFile(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, contentRepository.getStorageType());
|
||||
|
||||
this.documentRepository.save(document);
|
||||
|
||||
@@ -74,9 +71,6 @@ 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);
|
||||
throw new DocumentManagementException(documentCommand.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,28 +88,20 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
final Document documentForUpdate = this.documentRepository.findOne(documentCommand.getId());
|
||||
if (documentForUpdate == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId(), documentCommand.getId()); }
|
||||
|
||||
StorageType documentStoreType = documentForUpdate.storageType();
|
||||
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);
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository();
|
||||
documentCommand.setLocation(contentRepository.saveFile(inputStream, documentCommand));
|
||||
documentCommand.setStorageType(contentRepository.getStorageType().getValue());
|
||||
}
|
||||
|
||||
documentForUpdate.update(documentCommand);
|
||||
|
||||
if (inputStream != null && documentCommand.isFileNameChanged()) {
|
||||
// delete previous file
|
||||
deleteFile(documentCommand.getName(), oldLocation);
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(documentStoreType);
|
||||
contentRepository.deleteFile(documentCommand.getName(), oldLocation);
|
||||
}
|
||||
|
||||
this.documentRepository.saveAndFlush(documentForUpdate);
|
||||
@@ -125,9 +111,9 @@ 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);
|
||||
throw new DocumentManagementException(documentCommand.getName());
|
||||
} catch (final ContentManagementException cme) {
|
||||
logger.error(cme.getMessage(), cme);
|
||||
throw new ContentManagementException(documentCommand.getName(), cme.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,16 +127,11 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
final Document document = this.documentRepository.findOne(documentCommand.getId());
|
||||
if (document == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(),
|
||||
documentCommand.getParentEntityId(), documentCommand.getId()); }
|
||||
|
||||
this.documentRepository.delete(document);
|
||||
deleteFile(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); }
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(document.storageType());
|
||||
contentRepository.deleteFile(document.getName(), document.getLocation());
|
||||
return new CommandProcessingResult(document.getId());
|
||||
}
|
||||
|
||||
private void validateParentEntityType(final DocumentCommand documentCommand) {
|
||||
@@ -159,7 +140,6 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
}
|
||||
|
||||
private static boolean checkValidEntityType(final String entityType) {
|
||||
|
||||
for (final DOCUMENT_MANAGEMENT_ENTITY entities : DOCUMENT_MANAGEMENT_ENTITY.values()) {
|
||||
if (entities.name().equalsIgnoreCase(entityType)) { return true; }
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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.documentmanagement.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||
|
||||
public interface ImageReadPlatformService {
|
||||
|
||||
ImageData retrieveClientImage(Long clientId);
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.documentmanagement.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.ImageData;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.client.exception.ImageNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ImageReadPlatformServiceImpl implements ImageReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final ContentRepositoryFactory contentRepositoryFactory;
|
||||
private final ClientRepositoryWrapper clientRepositoryWrapper;
|
||||
|
||||
@Autowired
|
||||
public ImageReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource, final ContentRepositoryFactory documentStoreFactory,
|
||||
final ClientRepositoryWrapper clientRepositoryWrapper) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.contentRepositoryFactory = documentStoreFactory;
|
||||
this.clientRepositoryWrapper = clientRepositoryWrapper;
|
||||
}
|
||||
|
||||
private static final class ImageMapper implements RowMapper<ImageData> {
|
||||
|
||||
private final String entityDisplayName;
|
||||
|
||||
public ImageMapper(final String entityDisplayName) {
|
||||
this.entityDisplayName = entityDisplayName;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return " image.id as id, image.location as location, image.storage_type_enum as storageType "
|
||||
+ " from m_image image , m_client client " + " where client.image_id = image.id and client.id=?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = JdbcSupport.getLong(rs, "id");
|
||||
final String location = rs.getString("location");
|
||||
final Integer storageType = JdbcSupport.getInteger(rs, "storageType");
|
||||
return new ImageData(id, location, storageType, entityDisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData retrieveClientImage(Long clientId) {
|
||||
try {
|
||||
Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ImageMapper imageMapper = new ImageMapper(client.getDisplayName());
|
||||
|
||||
final String sql = "select " + imageMapper.schema();
|
||||
|
||||
ImageData imageData = this.jdbcTemplate.queryForObject(sql, imageMapper, new Object[] { clientId });
|
||||
ContentRepository contentRepository = contentRepositoryFactory.getRepository(imageData.storageType());
|
||||
return contentRepository.fetchImage(imageData);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
throw new ImageNotFoundException("clients", clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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.documentmanagement.service;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
public interface ImageWritePlatformService {
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream, Long fileSize);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')")
|
||||
CommandProcessingResult deleteClientImage(Long clientId);
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.documentmanagement.service;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.Image;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.ImageRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePlatformService {
|
||||
|
||||
private final ContentRepositoryFactory contentRepositoryFactory;
|
||||
private final ClientRepositoryWrapper clientRepositoryWrapper;
|
||||
private final ImageRepository imageRepository;
|
||||
|
||||
@Autowired
|
||||
public ImageWritePlatformServiceJpaRepositoryImpl(final ContentRepositoryFactory documentStoreFactory,
|
||||
final ClientRepositoryWrapper clientRepositoryWrapper, final ImageRepository imageRepository) {
|
||||
this.contentRepositoryFactory = documentStoreFactory;
|
||||
this.clientRepositoryWrapper = clientRepositoryWrapper;
|
||||
this.imageRepository = imageRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream,
|
||||
Long fileSize) {
|
||||
final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
|
||||
deletePreviousClientImage(client);
|
||||
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository();
|
||||
String imageLocation = contentRepository.saveImage(inputStream, clientId, imageName, fileSize);
|
||||
return updateClientImage(client, imageLocation, contentRepository.getStorageType());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) {
|
||||
final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
|
||||
deletePreviousClientImage(client);
|
||||
|
||||
ContentRepository contenRepository = this.contentRepositoryFactory.getRepository();
|
||||
final String imageLocation = contenRepository.saveImage(encodedImage, clientId, "image");
|
||||
|
||||
return updateClientImage(client, imageLocation, contenRepository.getStorageType());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deleteClientImage(final Long clientId) {
|
||||
|
||||
final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
Image image = client.getImage();
|
||||
// delete image from the file system
|
||||
if (image != null) {
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(StorageType.fromInt(image.getStorageType()));
|
||||
contentRepository.deleteImage(clientId, image.getLocation());
|
||||
client.setImage(null);
|
||||
this.imageRepository.delete(image);
|
||||
this.clientRepositoryWrapper.save(client);
|
||||
}
|
||||
|
||||
return new CommandProcessingResult(clientId);
|
||||
}
|
||||
|
||||
private void deletePreviousClientImage(final Client client) {
|
||||
Image image = client.getImage();
|
||||
if (image != null) {
|
||||
ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(StorageType.fromInt(image.getStorageType()));
|
||||
contentRepository.deleteImage(client.getId(), image.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
private CommandProcessingResult updateClientImage(final Client client, final String imageLocation, final StorageType storageType) {
|
||||
Image image = client.getImage();
|
||||
if (image == null) {
|
||||
image = new Image(imageLocation, storageType);
|
||||
} else {
|
||||
image.setLocation(imageLocation);
|
||||
image.setStorageType(storageType.getValue());
|
||||
}
|
||||
this.imageRepository.save(image);
|
||||
client.setImage(image);
|
||||
this.clientRepositoryWrapper.save(client);
|
||||
return new CommandProcessingResult(client.getId());
|
||||
}
|
||||
|
||||
}
|
||||
+15
-18
@@ -22,6 +22,7 @@ import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
*/
|
||||
final public class ClientData implements Comparable<ClientData> {
|
||||
|
||||
|
||||
private final Long id;
|
||||
private final String accountNo;
|
||||
private final String externalId;
|
||||
@@ -40,8 +41,7 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
private final Long officeId;
|
||||
private final String officeName;
|
||||
|
||||
private final String imageKey;
|
||||
@SuppressWarnings("unused")
|
||||
private final Long imageId;
|
||||
private final Boolean imagePresent;
|
||||
|
||||
// associations
|
||||
@@ -58,13 +58,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.imageId, 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.imageId, clientData.officeOptions, parentGroups);
|
||||
}
|
||||
|
||||
public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname,
|
||||
@@ -81,14 +81,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 Long imageId) {
|
||||
return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey, null, null);
|
||||
externalId, activationDate, imageId, 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 Long imageId, final Collection<OfficeData> allowedOffices,
|
||||
final Collection<GroupGeneralData> groups) {
|
||||
this.accountNo = accountNo;
|
||||
this.status = status;
|
||||
@@ -107,11 +107,11 @@ 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;
|
||||
if (imageKey != null) {
|
||||
this.imageId = imageId;
|
||||
if (imageId != null) {
|
||||
this.imagePresent = Boolean.TRUE;
|
||||
} else {
|
||||
this.imagePresent = null;
|
||||
this.imagePresent = Boolean.FALSE;
|
||||
}
|
||||
|
||||
// associations
|
||||
@@ -137,16 +137,12 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
return this.officeName;
|
||||
}
|
||||
|
||||
public String imageKey() {
|
||||
return this.imageKey;
|
||||
public Long getImageId() {
|
||||
return this.imageId;
|
||||
}
|
||||
|
||||
public boolean imageKeyDoesNotExist() {
|
||||
return !imageKeyExists();
|
||||
}
|
||||
|
||||
private boolean imageKeyExists() {
|
||||
return StringUtils.isNotBlank(this.imageKey);
|
||||
public Boolean getImagePresent() {
|
||||
return this.imagePresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -194,4 +190,5 @@ final public class ClientData implements Comparable<ClientData> {
|
||||
public LocalDate getActivationDate() {
|
||||
return this.activationDate;
|
||||
}
|
||||
|
||||
}
|
||||
+25
-32
@@ -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;
|
||||
@@ -33,12 +13,17 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.domain.Image;
|
||||
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
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> {
|
||||
@@ -50,6 +35,10 @@ public final class Client extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "office_id", nullable = false)
|
||||
private Office office;
|
||||
|
||||
@OneToOne(optional = true)
|
||||
@JoinColumn(name = "image_id", nullable = true)
|
||||
private Image image;
|
||||
|
||||
/**
|
||||
* A value from {@link ClientStatus}.
|
||||
*/
|
||||
@@ -72,16 +61,12 @@ public final class Client extends AbstractPersistable<Long> {
|
||||
@Column(name = "fullname", length = 100)
|
||||
private String fullname;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "display_name", length = 100, nullable = false)
|
||||
private String displayName;
|
||||
|
||||
@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,16 +370,24 @@ 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 Long officeId() {
|
||||
return this.office.getId();
|
||||
}
|
||||
|
||||
public void setImage(Image image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
return this.image;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.service;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.Page;
|
||||
|
||||
+12
-13
@@ -38,7 +38,7 @@ 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;
|
||||
@@ -92,7 +92,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 +109,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 +159,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 +172,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
|
||||
Collection<GroupGeneralData> parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper,
|
||||
new Object[] { clientId });
|
||||
|
||||
return ClientData.setParentGroups(clientData, parentGroups);
|
||||
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
throw new ClientNotFoundException(clientId);
|
||||
}
|
||||
@@ -206,8 +207,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 });
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append("c.office_id as officeId, o.name as officeName, ");
|
||||
sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
|
||||
sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
|
||||
sqlBuilder.append("c.activation_date as activationDate, c.image_key as imagekey ");
|
||||
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId");
|
||||
sqlBuilder.append("from m_client c ");
|
||||
sqlBuilder.append("join m_office o on o.id = c.office_id ");
|
||||
sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id");
|
||||
@@ -251,11 +251,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 Long imageId = JdbcSupport.getLong(rs, "imageId");
|
||||
final String officeName = rs.getString("officeName");
|
||||
|
||||
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey);
|
||||
externalId, activationDate, imageId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
builder.append("c.office_id as officeId, o.name as officeName, ");
|
||||
builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
|
||||
builder.append("c.fullname as fullname, c.display_name as displayName, ");
|
||||
builder.append("c.activation_date as activationDate, c.image_key as imagekey ");
|
||||
builder.append("c.activation_date as activationDate, c.image_id as imageId ");
|
||||
builder.append("from m_client c ");
|
||||
builder.append("join m_office o on o.id = c.office_id ");
|
||||
|
||||
@@ -299,13 +299,12 @@ 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 officeName = rs.getString("officeName");
|
||||
final Long imageId = JdbcSupport.getLong(rs, "imageId");
|
||||
|
||||
return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName,
|
||||
externalId, activationDate, imageKey);
|
||||
externalId, activationDate, imageId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class ParentGroupsMapper implements RowMapper<GroupGeneralData> {
|
||||
|
||||
-12
@@ -5,12 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.client.service;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
public interface ClientWritePlatformService {
|
||||
|
||||
@@ -22,12 +18,4 @@ public interface ClientWritePlatformService {
|
||||
|
||||
CommandProcessingResult deleteClient(Long clientId);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')")
|
||||
CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')")
|
||||
CommandProcessingResult deleteClientImage(Long clientId);
|
||||
}
|
||||
-75
@@ -5,24 +5,17 @@
|
||||
*/
|
||||
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;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
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.documentmanagement.exception.DocumentManagementException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.office.domain.Office;
|
||||
import org.mifosplatform.organisation.office.domain.OfficeRepository;
|
||||
@@ -34,7 +27,6 @@ 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.exception.ClientMustBePendingToBeDeletedException;
|
||||
import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.domain.GroupRepository;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
|
||||
@@ -221,73 +213,6 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream) {
|
||||
try {
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
String imageUploadLocation = setupForClientImageUpdate(clientId, client);
|
||||
|
||||
String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName);
|
||||
|
||||
return updateClientImage(clientId, client, imageLocation);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
throw new DocumentManagementException(imageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deleteClientImage(final Long clientId) {
|
||||
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
// delete image from the file system
|
||||
if (StringUtils.isNotEmpty(client.imageKey())) {
|
||||
FileUtils.deleteClientImage(clientId, client.imageKey());
|
||||
}
|
||||
return updateClientImage(clientId, client, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) {
|
||||
try {
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final String imageUploadLocation = setupForClientImageUpdate(clientId, client);
|
||||
|
||||
final String imageLocation = FileUtils.saveToFileSystem(encodedImage, imageUploadLocation, "image");
|
||||
|
||||
return updateClientImage(clientId, client, imageLocation);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
throw new DocumentManagementException("image");
|
||||
}
|
||||
}
|
||||
|
||||
private String setupForClientImageUpdate(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());
|
||||
}
|
||||
|
||||
/** 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);
|
||||
|
||||
return new CommandProcessingResult(clientId);
|
||||
}
|
||||
|
||||
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
|
||||
logger.error(dve.getMessage(), dve);
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.configuration.service.ExternalServicesReadPlatformServiceImpl;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.S3ContentRepository;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DocumentStoreFactoryTest {
|
||||
/**
|
||||
@Test
|
||||
public void shouldReturnS3DocumentStoreObjectIfS3Enabled() {
|
||||
S3ClientFactory s3ClientFactoryMock = mock(S3ClientFactory.class);
|
||||
when(s3ClientFactoryMock.getS3Client()).thenReturn(null);
|
||||
|
||||
ExternalServicesReadPlatformServiceImpl externalServicesDomainServiceMock = mock(ExternalServicesReadPlatformServiceImpl.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", ExternalServicesReadPlatformServiceImpl.class)).thenReturn(
|
||||
externalServicesDomainServiceMock);
|
||||
|
||||
ContentRepositoryFactory documentStoreFactory = new ContentRepositoryFactory(applicationContextMock);
|
||||
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(), S3ContentRepository.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);
|
||||
|
||||
ContentRepositoryFactory documentStoreFactory = new ContentRepositoryFactory(applicationContext);
|
||||
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(), FileSystemContentRepository.class);
|
||||
}
|
||||
**/
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FileDocumentStoreTest {
|
||||
/**
|
||||
@Test
|
||||
public void shouldRetrieveDocument() {
|
||||
FileSystemContentRepository fileSystemDocumentStore = new FileSystemContentRepository();
|
||||
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());
|
||||
}
|
||||
**/
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package org.mifosplatform.infrastructure.core.service;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.S3ContentRepository;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException;
|
||||
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import com.amazonaws.AmazonClientException;
|
||||
import com.amazonaws.AmazonServiceException;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.model.DeleteObjectRequest;
|
||||
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||
import com.amazonaws.services.s3.model.PutObjectRequest;
|
||||
import com.amazonaws.services.s3.model.PutObjectResult;
|
||||
import com.amazonaws.services.s3.model.S3Object;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class S3DocumentStoreTest {
|
||||
|
||||
InputStream inputStream;
|
||||
AmazonS3Client s3ClientMock;
|
||||
DocumentCommand documentCommand;
|
||||
DocumentData documentDataMock;
|
||||
/**
|
||||
@Test
|
||||
public void shouldGenerateParentDirectoryFromEntityDetails() {
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", null);
|
||||
String actualParentDirectory = s3DocumentStore.generateFileParentDirectory("entity_type", 1234L);
|
||||
assertTrue(actualParentDirectory.startsWith("documents/entity_type/1234"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGenerateClientImageDirectoryFromResourceId() {
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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());
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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"));
|
||||
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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"));
|
||||
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock);
|
||||
|
||||
s3DocumentStore.saveDocument(inputStream, documentCommand);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRetrieveDocumentFromS3() throws IOException {
|
||||
initRetrieve();
|
||||
when(s3ClientMock.getObject(Matchers.<GetObjectRequest> anyObject())).thenReturn(new S3Object());
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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"));
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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"));
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock);
|
||||
|
||||
s3DocumentStore.retrieveDocument(documentDataMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteImageFromS3() throws IOException {
|
||||
AmazonS3Client s3ClientMock = mock(AmazonS3Client.class);
|
||||
S3ContentRepository s3DocumentStore = new S3ContentRepository("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