description use storage file

This commit is contained in:
Efstratios Giannopoulos 2024-02-06 15:03:49 +02:00
parent fd974d3ee4
commit f7dc3e5a32
7 changed files with 302 additions and 67 deletions

View File

@ -48,6 +48,9 @@ public class AuditableAction {
public static final EventId Description_PublicQuery = new EventId(6004, "Description_PublicQuery"); public static final EventId Description_PublicQuery = new EventId(6004, "Description_PublicQuery");
public static final EventId Description_PublicLookup = new EventId(6005, "Description_PublicLookup"); public static final EventId Description_PublicLookup = new EventId(6005, "Description_PublicLookup");
public static final EventId Description_PersistStatus = new EventId(6006, "Description_PersistStatus"); public static final EventId Description_PersistStatus = new EventId(6006, "Description_PersistStatus");
public static final EventId Description_UploadFieldFiles = new EventId(6007, "Description_UploadFieldFiles");
public static final EventId Description_GetFieldFile = new EventId(6008, "Description_GetFieldFile");
public static final EventId Reference_Query = new EventId(7000, "Reference_Query"); public static final EventId Reference_Query = new EventId(7000, "Reference_Query");
public static final EventId Reference_Lookup = new EventId(7001, "Reference_Lookup"); public static final EventId Reference_Lookup = new EventId(7001, "Reference_Lookup");

View File

@ -0,0 +1,74 @@
package eu.eudat.model.persist;
import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class DescriptionFieldFilePersist {
private UUID descriptionTemplateId;
public static final String _descriptionTemplateId = "descriptionTemplateId";
private String fieldId;
public static final String _fieldId = "fieldId";
public UUID getDescriptionTemplateId() {
return descriptionTemplateId;
}
public void setDescriptionTemplateId(UUID descriptionTemplateId) {
this.descriptionTemplateId = descriptionTemplateId;
}
public String getFieldId() {
return fieldId;
}
public void setFieldId(String fieldId) {
this.fieldId = fieldId;
}
@Component(PersistValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class PersistValidator extends BaseValidator<DescriptionFieldFilePersist> {
public static final String ValidatorName = "DescriptionFieldFilePersistValidator";
private final MessageSource messageSource;
protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<DescriptionFieldFilePersist> modelClass() {
return DescriptionFieldFilePersist.class;
}
@Override
protected List<Specification> specifications(DescriptionFieldFilePersist item) {
return Arrays.asList(
this.spec()
.must(() -> this.isValidGuid(item.getDescriptionTemplateId()))
.failOn(DescriptionFieldFilePersist._descriptionTemplateId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionFieldFilePersist._descriptionTemplateId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getFieldId()))
.failOn(DescriptionFieldFilePersist._fieldId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionFieldFilePersist._fieldId}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -1,7 +1,9 @@
package eu.eudat.service.description; package eu.eudat.service.description;
import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.Description; import eu.eudat.model.Description;
import eu.eudat.model.StorageFile;
import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.model.persist.DescriptionPersist; import eu.eudat.model.persist.DescriptionPersist;
import eu.eudat.model.persist.DescriptionStatusPersist; import eu.eudat.model.persist.DescriptionStatusPersist;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
@ -10,6 +12,7 @@ import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
@ -26,5 +29,6 @@ public interface DescriptionService {
ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException; ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException;
StorageFile uploadFieldFile(DescriptionFieldFilePersist model, MultipartFile file, FieldSet fields) throws IOException;
StorageFileEntity getFieldFile(UUID descriptionId, UUID storageFileId);
} }

View File

@ -10,7 +10,7 @@ import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.description.*; import eu.eudat.commons.types.description.*;
import eu.eudat.commons.types.descriptionreference.DescriptionReferenceDataEntity; import eu.eudat.commons.types.descriptionreference.DescriptionReferenceDataEntity;
import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity; import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity;
import eu.eudat.commons.types.dmpreference.DmpReferenceDataEntity; import eu.eudat.commons.types.descriptiontemplate.fielddata.UploadDataEntity;
import eu.eudat.commons.types.notification.*; import eu.eudat.commons.types.notification.*;
import eu.eudat.commons.types.reference.DefinitionEntity; import eu.eudat.commons.types.reference.DefinitionEntity;
import eu.eudat.configurations.notification.NotificationProperties; import eu.eudat.configurations.notification.NotificationProperties;
@ -23,35 +23,33 @@ import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.builder.DescriptionBuilder;
import eu.eudat.model.builder.StorageFileBuilder;
import eu.eudat.model.deleter.DescriptionDeleter; import eu.eudat.model.deleter.DescriptionDeleter;
import eu.eudat.model.deleter.DescriptionReferenceDeleter; import eu.eudat.model.deleter.DescriptionReferenceDeleter;
import eu.eudat.model.deleter.DescriptionTagDeleter; import eu.eudat.model.deleter.DescriptionTagDeleter;
import eu.eudat.model.file.FileEnvelope; import eu.eudat.model.file.FileEnvelope;
import eu.eudat.model.persist.DescriptionPersist; import eu.eudat.model.persist.*;
import eu.eudat.model.persist.DescriptionReferencePersist;
import eu.eudat.model.persist.DescriptionStatusPersist;
import eu.eudat.model.persist.ReferencePersist;
import eu.eudat.model.persist.descriptionproperties.*; import eu.eudat.model.persist.descriptionproperties.*;
import eu.eudat.model.persist.descriptionreference.DescriptionReferenceDataPersist; import eu.eudat.model.persist.descriptionreference.DescriptionReferenceDataPersist;
import eu.eudat.model.persist.dmpreference.DmpReferenceDataPersist;
import eu.eudat.model.persist.referencedefinition.DefinitionPersist; import eu.eudat.model.persist.referencedefinition.DefinitionPersist;
import eu.eudat.query.*; import eu.eudat.query.*;
import eu.eudat.service.elastic.ElasticService; import eu.eudat.service.elastic.ElasticService;
import eu.eudat.service.storage.StorageFileProperties;
import eu.eudat.service.storage.StorageFileService;
import eu.eudat.service.transformer.FileTransformerService; import eu.eudat.service.transformer.FileTransformerService;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.*;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import org.apache.commons.io.FilenameUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -62,10 +60,14 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.unit.DataSize;
import org.springframework.web.multipart.MultipartFile;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.net.URLConnection;
import java.nio.file.Files; import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -74,36 +76,25 @@ import java.util.stream.Collectors;
public class DescriptionServiceImpl implements DescriptionService { public class DescriptionServiceImpl implements DescriptionService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DescriptionServiceImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DescriptionServiceImpl.class));
private final EntityManager entityManager; private final EntityManager entityManager;
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final ConventionService conventionService; private final ConventionService conventionService;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final MessageSource messageSource; private final MessageSource messageSource;
private final EventBroker eventBroker; private final EventBroker eventBroker;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final UserScope userScope; private final UserScope userScope;
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private final FileTransformerService fileTransformerService; private final FileTransformerService fileTransformerService;
private final NotifyIntegrationEventHandler eventHandler; private final NotifyIntegrationEventHandler eventHandler;
private final NotificationProperties notificationProperties; private final NotificationProperties notificationProperties;
private final ElasticService elasticService; private final ElasticService elasticService;
private final ValidatorFactory validatorFactory;
private final StorageFileProperties storageFileConfig;
private final StorageFileService storageFileService;
@Autowired @Autowired
@ -119,7 +110,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory, QueryFactory queryFactory,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
UserScope userScope, UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService) { XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -136,6 +127,9 @@ public class DescriptionServiceImpl implements DescriptionService {
this.notificationProperties = notificationProperties; this.notificationProperties = notificationProperties;
this.fileTransformerService = fileTransformerService; this.fileTransformerService = fileTransformerService;
this.elasticService = elasticService; this.elasticService = elasticService;
this.validatorFactory = validatorFactory;
this.storageFileConfig = storageFileConfig;
this.storageFileService = storageFileService;
} }
//region Persist //region Persist
@ -405,7 +399,15 @@ public class DescriptionServiceImpl implements DescriptionService {
FieldEntity data = new FieldEntity(); FieldEntity data = new FieldEntity();
if (persist == null) return data; if (persist == null) return data;
if (FieldType.isTextType(fieldType)) data.setTextValue(persist.getTextValue()); if (FieldType.isTextType(fieldType)) {
if (FieldType.UPLOAD.equals(fieldType)){
StorageFile storageFile = this.storageFileService.copyToStorage(UUID.fromString(persist.getTextValue()), StorageType.Main, true, new BaseFieldSet().ensure(StorageFile._id));
this.storageFileService.updatePurgeAt(storageFile.getId(), null);
data.setTextValue(storageFile.getId().toString());
} else {
data.setTextValue(persist.getTextValue());
}
}
else if (FieldType.isDateType(fieldType)) data.setDateValue(persist.getDateValue()); else if (FieldType.isDateType(fieldType)) data.setDateValue(persist.getDateValue());
else if (FieldType.isTextListType(fieldType)) data.setTextListValue(persist.getTextListValue()); else if (FieldType.isTextListType(fieldType)) data.setTextListValue(persist.getTextListValue());
else if (FieldType.isExternalIdentifierType(fieldType) && persist.getExternalIdentifier() != null) data.setExternalIdentifier(this.buildExternalIdentifierEntity(persist.getExternalIdentifier())); else if (FieldType.isExternalIdentifierType(fieldType) && persist.getExternalIdentifier() != null) data.setExternalIdentifier(this.buildExternalIdentifierEntity(persist.getExternalIdentifier()));
@ -702,14 +704,78 @@ public class DescriptionServiceImpl implements DescriptionService {
@Override @Override
public ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException { public ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
String type = exportType;
FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, type); FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, exportType);
headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename());
byte[] data = Files.readAllBytes(fileEnvelope.getFile().toPath()); byte[] data = Files.readAllBytes(fileEnvelope.getFile().toPath());
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<>(data, headers, HttpStatus.OK); return new ResponseEntity<>(data, headers, HttpStatus.OK);
} }
//endregion //endregion
@Override
public StorageFile uploadFieldFile(DescriptionFieldFilePersist model, MultipartFile file, FieldSet fields) throws IOException {
this.authorizationService.authorizeForce(Permission.EditDescription);
DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).ids(model.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).first();
if (descriptionTemplate == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
eu.eudat.commons.types.descriptiontemplate.DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(eu.eudat.commons.types.descriptiontemplate.DefinitionEntity.class, descriptionTemplate.getDefinition());
if (definition == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), eu.eudat.commons.types.descriptiontemplate.DefinitionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
eu.eudat.commons.types.descriptiontemplate.FieldEntity fieldEntity = definition.getFieldById(model.getFieldId()).stream().filter(x -> x != null && x.getData() != null && x.getData().getFieldType().equals(FieldType.UPLOAD)).findFirst().orElse(null);
if (fieldEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getFieldId(), eu.eudat.commons.types.descriptiontemplate.FieldEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
UploadDataEntity uploadDataEntity = (UploadDataEntity)fieldEntity.getData();
if (DataSize.ofBytes(file.getSize()).equals(DataSize.ofMegabytes(uploadDataEntity.getMaxFileSizeInMB()))) {
throw new MyValidationException("The uploaded file is too large");
}
if(!this.conventionService.isListNullOrEmpty(uploadDataEntity.getTypes())) {
boolean isContentTypeAccepted = false;
for (UploadDataEntity.UploadDataOptionEntity option: uploadDataEntity.getTypes()) {
if(Objects.equals(file.getContentType(), option.getValue())) {
isContentTypeAccepted = true;
break;
}
}
if (!isContentTypeAccepted){
throw new MyValidationException("The uploaded file has an unaccepted type");
}
}
StorageFilePersist storageFilePersist = new StorageFilePersist();
storageFilePersist.setName(FilenameUtils.removeExtension(file.getName()));
storageFilePersist.setExtension(FilenameUtils.getExtension(file.getName()));
storageFilePersist.setMimeType(URLConnection.guessContentTypeFromName(file.getName()));
storageFilePersist.setOwnerId(this.userScope.getUserIdSafe());
storageFilePersist.setStorageType(StorageType.Temp);
storageFilePersist.setLifetime(Duration.ofSeconds(this.storageFileConfig.getTempStoreLifetimeSeconds()));
this.validatorFactory.validator(StorageFilePersist.StorageFilePersistValidator.class).validateForce(storageFilePersist);
return this.storageFileService.persistBytes(storageFilePersist, file.getBytes(), BaseFieldSet.build(fields, StorageFile._id, StorageFile._name));
}
@Override
public StorageFileEntity getFieldFile(UUID descriptionId, UUID storageFileId) {
this.authorizationService.authorizeForce(Permission.BrowseDescription);
DescriptionEntity descriptionEntity = this.queryFactory.query(DescriptionQuery.class).isActive(IsActive.Active).ids(descriptionId).first();
if (descriptionEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
DmpDescriptionTemplateEntity dmpDescriptionTemplateEntity = this.queryFactory.query(DmpDescriptionTemplateQuery.class).ids(descriptionEntity.getDmpDescriptionTemplateId()).isActive(IsActive.Active).first();
if (dmpDescriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionEntity.getDmpDescriptionTemplateId(), DmpDescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
DmpEntity dmpEntity = this.queryFactory.query(DmpQuery.class).ids(dmpDescriptionTemplateEntity.getDmpId()).isActive(IsActive.Active).first();
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpDescriptionTemplateEntity.getDmpId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!dmpEntity.getAccessType().equals(DmpAccessType.Public))
{
boolean isDmpUser = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpEntity.getId()).userIds(this.userScope.getUserIdSafe()).isActives(IsActive.Active).count() > 0;
if (!isDmpUser) throw new MyUnauthorizedException();
}
StorageFileEntity storageFile = this.queryFactory.query(StorageFileQuery.class).ids(storageFileId).first();
if (storageFile == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{storageFileId, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale()));
return storageFile;
}
} }

View File

@ -16,8 +16,8 @@ import java.util.UUID;
public interface StorageFileService extends ApplicationListener<ApplicationReadyEvent> { public interface StorageFileService extends ApplicationListener<ApplicationReadyEvent> {
StorageFile persistBytes(StorageFilePersist model, byte[] payload, FieldSet fields) throws IOException; StorageFile persistBytes(StorageFilePersist model, byte[] payload, FieldSet fields) throws IOException;
StorageFile persistString(StorageFilePersist model, String payload, FieldSet fields, Charset charset) throws IOException; StorageFile persistString(StorageFilePersist model, String payload, FieldSet fields, Charset charset) throws IOException;
boolean moveToStorage(UUID fileId, StorageType storageType); StorageFile moveToStorage(UUID fileId, StorageType storageType, boolean replaceDestination, FieldSet fields);
boolean copyToStorage(UUID fileId, StorageType storageType); StorageFile copyToStorage(UUID fileId, StorageType storageType, boolean replaceDestination, FieldSet fields);
boolean exists(UUID fileId); boolean exists(UUID fileId);

View File

@ -93,7 +93,7 @@ public class StorageFileServiceImpl implements StorageFileService {
this.entityManager.persist(storageFile); this.entityManager.persist(storageFile);
this.entityManager.flush(); this.entityManager.flush();
return this.builderFactory.builder(StorageFileBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Description._id), storageFile); return this.builderFactory.builder(StorageFileBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, StorageFile._id), storageFile);
} }
@Override @Override
@ -104,6 +104,7 @@ public class StorageFileServiceImpl implements StorageFileService {
private StorageFileEntity buildDataEntry(StorageFilePersist model) { private StorageFileEntity buildDataEntry(StorageFilePersist model) {
StorageFileEntity data = new StorageFileEntity(); StorageFileEntity data = new StorageFileEntity();
data.setId(UUID.randomUUID());
data.setFileRef(UUID.randomUUID().toString().replaceAll("-", "").toLowerCase(Locale.ROOT)); data.setFileRef(UUID.randomUUID().toString().replaceAll("-", "").toLowerCase(Locale.ROOT));
data.setName(model.getName()); data.setName(model.getName());
data.setOwnerId(model.getOwnerId()); data.setOwnerId(model.getOwnerId());
@ -117,42 +118,74 @@ public class StorageFileServiceImpl implements StorageFileService {
} }
@Override @Override
public boolean moveToStorage(UUID fileId, StorageType storageType) { public StorageFile moveToStorage(UUID fileId, StorageType storageType, boolean replaceDestination, FieldSet fields) {
try { try {
StorageFileEntity storageFile = this.entityManager.find(StorageFileEntity.class, fileId); StorageFileEntity storageFile = this.entityManager.find(StorageFileEntity.class, fileId);
if (storageFile == null) return false; if (storageFile == null) return null;
this.authorizeForce(storageFile, StorageFilePermission.Read); this.authorizeForce(storageFile, StorageFilePermission.Read);
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType())); File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
if (!file.exists()) return false; if (!file.exists()) return null;
File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType())); File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
if (file.exists() && !replaceDestination) return null;
boolean fileCopied = FileCopyUtils.copy(file, destinationFile) > 0; boolean fileCopied = FileCopyUtils.copy(file, destinationFile) > 0;
if (!fileCopied) return false; if (!fileCopied) return null;
return file.delete();
storageFile.setStorageType(storageType);
this.entityManager.merge(storageFile);
file.delete();
this.entityManager.merge(storageFile);
return this.builderFactory.builder(StorageFileBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, StorageFile._id), storageFile);
} }
catch (Exception ex) { catch (Exception ex) {
logger.warn("problem reading byte content of storage file " + fileId, ex); logger.warn("problem reading byte content of storage file " + fileId, ex);
return false; return null;
} }
} }
@Override @Override
public boolean copyToStorage(UUID fileId, StorageType storageType) { public StorageFile copyToStorage(UUID fileId, StorageType storageType, boolean replaceDestination, FieldSet fields) {
try { try {
StorageFileEntity storageFile = this.entityManager.find(StorageFileEntity.class, fileId); StorageFileEntity storageFile = this.entityManager.find(StorageFileEntity.class, fileId);
if (storageFile == null) return false; if (storageFile == null) return null;
this.authorizeForce(storageFile, StorageFilePermission.Read); this.authorizeForce(storageFile, StorageFilePermission.Read);
File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType())); File file = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
if (!file.exists()) return false; if (!file.exists()) return null;
File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageType));
if (file.exists() && !replaceDestination) return null;
boolean fileCopied = FileCopyUtils.copy(file, destinationFile) > 0;
if (!fileCopied) return null;
StorageFileEntity data = new StorageFileEntity();
data.setId(UUID.randomUUID());
data.setFileRef(storageFile.getFileRef());
data.setName(storageFile.getName());
data.setOwnerId(storageFile.getOwnerId());
data.setExtension(storageFile.getExtension());
data.setStorageType(storageFile.getStorageType());
data.setMimeType(storageFile.getMimeType());
data.setCreatedAt(Instant.now());
data.setPurgeAt(storageFile.getPurgeAt());
this.entityManager.persist(data);
this.entityManager.merge(storageFile);
return this.builderFactory.builder(StorageFileBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, StorageFile._id), storageFile);
File destinationFile = new File(this.filePath(storageFile.getFileRef(), storageFile.getStorageType()));
return FileCopyUtils.copy(file, destinationFile) > 0;
} }
catch (Exception ex) { catch (Exception ex) {
logger.warn("problem reading byte content of storage file " + fileId, ex); logger.warn("problem reading byte content of storage file " + fileId, ex);
return false; return null;
} }
} }

View File

@ -1,11 +1,19 @@
package eu.eudat.controllers.v2; package eu.eudat.controllers.v2;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.audit.AuditableAction; import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.DmpAccessType; import eu.eudat.commons.enums.DmpAccessType;
import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.DmpStatus;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.enums.StorageType;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.StorageFile;
import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.model.persist.StorageFilePersist;
import eu.eudat.service.storage.StorageFileService;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.validation.ValidationFilterAnnotation; import gr.cite.tools.validation.ValidationFilterAnnotation;
import eu.eudat.model.Description; import eu.eudat.model.Description;
import eu.eudat.model.Dmp; import eu.eudat.model.Dmp;
@ -21,7 +29,6 @@ import eu.eudat.query.DmpQuery;
import eu.eudat.query.lookup.DescriptionLookup; import eu.eudat.query.lookup.DescriptionLookup;
import eu.eudat.service.description.DescriptionService; import eu.eudat.service.description.DescriptionService;
import eu.eudat.service.elastic.ElasticQueryHelperService; import eu.eudat.service.elastic.ElasticQueryHelperService;
import eu.eudat.service.transformer.FileTransformerService;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.censor.CensorFactory; import gr.cite.tools.data.censor.CensorFactory;
@ -32,15 +39,22 @@ import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.net.URLConnection;
import java.time.Duration;
import java.util.*; import java.util.*;
import static eu.eudat.authorization.AuthorizationFlags.Public; import static eu.eudat.authorization.AuthorizationFlags.Public;
@ -63,6 +77,8 @@ public class DescriptionController {
private final MessageSource messageSource; private final MessageSource messageSource;
private final ElasticQueryHelperService elasticQueryHelperService; private final ElasticQueryHelperService elasticQueryHelperService;
private final StorageFileService storageFileService;
private final ConventionService conventionService;
public DescriptionController( public DescriptionController(
BuilderFactory builderFactory, BuilderFactory builderFactory,
AuditService auditService, AuditService auditService,
@ -70,7 +86,7 @@ public class DescriptionController {
CensorFactory censorFactory, CensorFactory censorFactory,
QueryFactory queryFactory, QueryFactory queryFactory,
MessageSource messageSource, MessageSource messageSource,
ElasticQueryHelperService elasticQueryHelperService) { ElasticQueryHelperService elasticQueryHelperService, StorageFileService storageFileService, ConventionService conventionService) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.auditService = auditService; this.auditService = auditService;
this.descriptionService = descriptionService; this.descriptionService = descriptionService;
@ -78,6 +94,8 @@ public class DescriptionController {
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.messageSource = messageSource; this.messageSource = messageSource;
this.elasticQueryHelperService = elasticQueryHelperService; this.elasticQueryHelperService = elasticQueryHelperService;
this.storageFileService = storageFileService;
this.conventionService = conventionService;
} }
@PostMapping("public/query") @PostMapping("public/query")
@ -192,4 +210,41 @@ public class DescriptionController {
return this.descriptionService.export(id, exportType); return this.descriptionService.export(id, exportType);
} }
@PostMapping("field-file/upload")
@Transactional
@ValidationFilterAnnotation(validator = DescriptionFieldFilePersist.PersistValidator.ValidatorName, argumentName = "model")
public StorageFile uploadFieldFiles(@RequestParam("file") MultipartFile file, @RequestParam("model") DescriptionFieldFilePersist model, FieldSet fieldSet) throws IOException {
logger.debug(new MapLogEntry("uploadFieldFiles" + Description.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
StorageFile persisted = this.descriptionService.uploadFieldFile(model, file, fieldSet);
this.auditService.track(AuditableAction.Description_UploadFieldFiles, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
));
return persisted;
}
@GetMapping("{id}/field-file/{fileId}")
public ResponseEntity<ByteArrayResource> getFieldFile(@PathVariable("id") UUID id, @PathVariable("fileId") UUID fileId) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("get Field File" + Description.class.getSimpleName()).And("id", id).And("fileId", fileId));
StorageFileEntity storageFile = this.descriptionService.getFieldFile(id, fileId);
byte[] file = this.storageFileService.readAsBytesSafe(id);
if (file == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.auditService.track(AuditableAction.Description_GetFieldFile, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id)
));
String contentType = storageFile.getMimeType();
if (this.conventionService.isNullOrEmpty(contentType)) contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
return ResponseEntity.ok()
.contentType(MediaType.valueOf(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + storageFile.getName() + (storageFile.getExtension().startsWith(".") ? "" : ".") + storageFile.getExtension() + "\"")
.body(new ByteArrayResource(file));
}
} }