Adding ability to upload files on s3.

To enable file upload on S3 you need to enable the configuration setting in c_configuration table for s3.
The S3 credentials (access_key, secret_key, bucket_name) in a m_external_services table.
m_client table is normalised into m_client and m_image table. The relation between m_client and m_image is one-to-one for now.
To persist the storage type(s3 or filesystem) a column had been added to m_document.

MISOFX-69 Adding db migrations for s3 support

MIFOSX-69 Adding method to return boolean value of S3 config

MIFOSX-69 - Priti/Harsha - Refactoring Code, extracted interface for Documents upload

MIFOSX-69 Refactored to add S3 Support for file upload. Modified saveDocs to throw Document Management Exception instead of IO exception

MIFOSX-69 Adding S3 support for file upload

MIFOSX-69 - Adding S3 support for Image upload

MIFOSX-69 Adding factory to get instance of data store as per s3 configuration

MIFOSX-69 Changing table s3_details to generic tabe for external services, adding column to store the storage type in m_documents table

MIFOSX-69 Fetching S3 details from database

MIFOSX-69 Adding document storage type while creating a document

MIFOSX-69 Normalising client table to create an image table

Refactoring ClientImage code to save client image in m_image table

Refactored code to get document as file

MIFOSX-69 Retreiving documents from S3

MIFOSX-69 Refactored ClientData to store ImageData

MIFOSX-69 Retrieving images from S3, Modifying Base64 to encode from byte array instead of file in CLientImagesApiResource

MIFOSX-69 Refactored code to delete client image

MIFOSX-69 Deleting Images from S3

Refactored DocumentStore to return StoreType from string value

MIFOSX-69 Refactored to move document deletion responsibility to storageType

MIFOSX-69 Giving more appropriate name to getInstance methods of DocumentStoreFactory as per the instance creation criteria

MIFOSX-69 Deleting Documents from S3

MIFOSX-69 Removing unnecessary s3_param from migration

MIFOSX-69 Adding ability to update document and update doc store type

MIFOSX-69 Renaming method to follow java conventions

MIFOSX-69 Adding filename to error log for s3 file retrieval

Adding data migrations and changing the version for migration file
This commit is contained in:
Harshac
2013-06-03 10:29:30 +05:30
committed by Vishwas Babu AJ
parent 14c8eabdcc
commit 9c1f9cd912
38 changed files with 1517 additions and 494 deletions
@@ -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);
}
}
@@ -0,0 +1,55 @@
package org.mifosplatform.infrastructure.core.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SuppressWarnings("unused")
@RunWith(MockitoJUnitRunner.class)
public class DocumentStoreFactoryTest {
@Test
public void shouldReturnS3DocumentStoreObjectIfS3Enabled(){
S3ClientFactory s3ClientFactoryMock = mock(S3ClientFactory.class);
when(s3ClientFactoryMock.getS3Client()).thenReturn(null);
ExternalServicesDomainService externalServicesDomainServiceMock = mock(ExternalServicesDomainService.class);
when(externalServicesDomainServiceMock.getValue("s3_bucket_name")).thenReturn("bucket_name");
ConfigurationDomainService configurationDomainServiceMock = mock(ConfigurationDomainService.class);
when(configurationDomainServiceMock.isAmazonS3Enabled()).thenReturn(true);
ApplicationContext applicationContextMock = mock(ApplicationContext.class);
when(applicationContextMock.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainServiceMock);
when(applicationContextMock.getBean("s3ClientFactory", S3ClientFactory.class)).thenReturn(s3ClientFactoryMock);
when(applicationContextMock.getBean("externalServicesDomainService", ExternalServicesDomainService.class)).thenReturn(externalServicesDomainServiceMock);
DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContextMock);
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),S3DocumentStore.class);
}
@Test
public void shouldReturnFileSystemDocumentStoreObjectIfS3IsDisabled(){
ConfigurationDomainService configurationDomainService = mock(ConfigurationDomainService.class);
when(configurationDomainService.isAmazonS3Enabled()).thenReturn(false);
ApplicationContext applicationContext = mock(ApplicationContext.class);
when(applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainService);
DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContext);
assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),FileSystemDocumentStore.class);
}
}
@@ -0,0 +1,31 @@
package org.mifosplatform.infrastructure.core.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
import org.mockito.runners.MockitoJUnitRunner;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SuppressWarnings("unused")
@RunWith(MockitoJUnitRunner.class)
public class FileDocumentStoreTest {
@Test
public void shouldRetrieveDocument(){
FileSystemDocumentStore fileSystemDocumentStore = new FileSystemDocumentStore();
DocumentData documentDataMock = mock(DocumentData.class);
when(documentDataMock.fileName()).thenReturn("some_file");
when(documentDataMock.fileName()).thenReturn("content_type");
when(documentDataMock.fileLocation()).thenReturn("abcd");
FileData fileData = fileSystemDocumentStore.retrieveDocument(documentDataMock);
assertEquals(fileData.name(), documentDataMock.fileName());
assertEquals(fileData.contentType(), documentDataMock.contentType());
}
}
@@ -0,0 +1,135 @@
package org.mifosplatform.infrastructure.core.service;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand;
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException;
import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException;
import org.mockito.Matchers;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
import java.io.InputStream;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
@SuppressWarnings("unused")
@RunWith(MockitoJUnitRunner.class)
public class S3DocumentStoreTest {
InputStream inputStream;
AmazonS3Client s3ClientMock;
DocumentCommand documentCommand;
DocumentData documentDataMock;
@Test
public void shouldGenerateParentDirectoryFromEntityDetails(){
S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null);
String actualParentDirectory = s3DocumentStore.generateFileParentDirectory("entity_type", 1234L);
assertTrue(actualParentDirectory.startsWith("documents/entity_type/1234"));
}
@Test
public void shouldGenerateClientImageDirectoryFromResourceId(){
S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null);
String actualParentDirectory = s3DocumentStore.generateClientImageParentDirectory(1234L);
assertTrue(actualParentDirectory.startsWith("images/clients/1234"));
}
@Test
public void shouldSaveADocumentToS3() throws IOException {
initUpload();
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenReturn(new PutObjectResult());
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
s3DocumentStore.saveDocument(inputStream, documentCommand);
verify(s3ClientMock, times(1)).putObject(Matchers.<PutObjectRequest>anyObject());
}
@Test(expected = DocumentManagementException.class)
public void shouldCatchAmazonServiceExceptionWhenUnableToUploadDocumentToS3() throws IOException {
initUpload();
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenThrow(new AmazonServiceException("serviceException"));
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
s3DocumentStore.saveDocument(inputStream, documentCommand);
}
@Test(expected = DocumentManagementException.class)
public void shouldCatchAmazonClientExceptionWhenUnableToUploadDocumentToS3() throws IOException {
initUpload();
when(s3ClientMock.putObject(Matchers.<PutObjectRequest>anyObject())).thenThrow(new AmazonClientException("clientException"));
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock);
s3DocumentStore.saveDocument(inputStream, documentCommand);
}
@Test
public void shouldRetrieveDocumentFromS3() throws IOException {
initRetrieve();
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenReturn(new S3Object());
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
FileData fileData = s3DocumentStore.retrieveDocument(documentDataMock);
verify(s3ClientMock, times(1)).getObject(Matchers.<GetObjectRequest>anyObject());
assertEquals(fileData.name(), "name");
assertEquals(fileData.contentType(), "type");
}
@Test(expected = DocumentNotFoundException.class)
public void shouldCatchAmazonServiceExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException {
initRetrieve();
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenThrow(new AmazonServiceException("serviceException"));
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
s3DocumentStore.retrieveDocument(documentDataMock);
}
@Test(expected = DocumentNotFoundException.class)
public void shouldCatchAmazonClientExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException {
initRetrieve();
when(s3ClientMock.getObject(Matchers.<GetObjectRequest>anyObject())).thenThrow(new AmazonClientException("clientException"));
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
s3DocumentStore.retrieveDocument(documentDataMock);
}
@Test
public void shouldDeleteImageFromS3() throws IOException {
AmazonS3Client s3ClientMock = mock(AmazonS3Client.class);
S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock);
s3DocumentStore.deleteImage(123L,"imagekey");
verify(s3ClientMock, times(1)).deleteObject(Matchers.<DeleteObjectRequest>anyObject());
}
private void initRetrieve() {
s3ClientMock = mock(AmazonS3Client.class);
documentDataMock = mock(DocumentData.class);
when(documentDataMock.fileLocation()).thenReturn("path");
when(documentDataMock.fileName()).thenReturn("name");
when(documentDataMock.contentType()).thenReturn("type");
}
private void initUpload(){
inputStream = S3DocumentStoreTest.class.getResourceAsStream("DummyString");
s3ClientMock = mock(AmazonS3Client.class);
documentCommand = new DocumentCommand(null, -1l, "parentEntityType", 1l, null, "someFileName", 3l, null, null, null);
}
}