argos/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java

823 lines
41 KiB
Java

package eu.eudat.service.dmp;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.*;
import eu.eudat.commons.enums.notification.NotificationContactType;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.commons.types.actionconfirmation.DmpInvitationEntity;
import eu.eudat.commons.types.dmp.DmpBlueprintValueEntity;
import eu.eudat.commons.types.dmp.DmpContactEntity;
import eu.eudat.commons.types.dmp.DmpPropertiesEntity;
import eu.eudat.commons.types.notification.*;
import eu.eudat.commons.types.reference.DefinitionEntity;
import eu.eudat.commons.types.reference.FieldEntity;
import eu.eudat.commons.validation.ValidatorFactory;
import eu.eudat.configurations.notification.NotificationProperties;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DmpTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotificationIntegrationEventHandler;
import eu.eudat.model.Dmp;
import eu.eudat.model.DmpUser;
import eu.eudat.model.Reference;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.builder.DmpBuilder;
import eu.eudat.model.builder.DmpUserBuilder;
import eu.eudat.model.deleter.*;
import eu.eudat.model.file.FileEnvelope;
import eu.eudat.model.persist.*;
import eu.eudat.model.persist.actionconfirmation.DmpInvitationPersist;
import eu.eudat.model.persist.dmpproperties.DmpBlueprintValuePersist;
import eu.eudat.model.persist.dmpproperties.DmpContactPersist;
import eu.eudat.model.persist.dmpproperties.DmpPropertiesPersist;
import eu.eudat.model.persist.referencedefinition.DefinitionPersist;
import eu.eudat.model.persist.referencedefinition.FieldPersist;
import eu.eudat.query.*;
import eu.eudat.service.actionconfirmation.ActionConfirmationService;
import eu.eudat.service.description.DescriptionService;
import eu.eudat.service.elastic.ElasticService;
import eu.eudat.service.transformer.FileTransformerService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
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.FieldSet;
import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager;
import jakarta.xml.bind.JAXBException;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.nio.file.Files;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class DmpServiceImpl implements DmpService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DmpServiceImpl.class));
private final EntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final QueryFactory queryFactory;
private final ConventionService conventionService;
private final ErrorThesaurusProperties errors;
private final MessageSource messageSource;
private final XmlHandlingService xmlHandlingService;
private final JsonHandlingService jsonHandlingService;
private final UserScope userScope;
private final EventBroker eventBroker;
private final DescriptionService descriptionService;
private final FileTransformerService fileTransformerService;
private final NotificationIntegrationEventHandler eventHandler;
private final NotificationProperties notificationProperties;
private final ActionConfirmationService actionConfirmationService;
private final ValidatorFactory validatorFactory;
private final ElasticService elasticService;
@Autowired
public DmpServiceImpl(
EntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
QueryFactory queryFactory,
ConventionService conventionService,
ErrorThesaurusProperties errors,
MessageSource messageSource,
XmlHandlingService xmlHandlingService,
JsonHandlingService jsonHandlingService,
UserScope userScope, EventBroker eventBroker, DescriptionService descriptionService, NotificationIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, ActionConfirmationService actionConfirmationService, FileTransformerService fileTransformerService, ValidatorFactory validatorFactory, ElasticService elasticService) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.queryFactory = queryFactory;
this.conventionService = conventionService;
this.errors = errors;
this.messageSource = messageSource;
this.xmlHandlingService = xmlHandlingService;
this.jsonHandlingService = jsonHandlingService;
this.userScope = userScope;
this.eventBroker = eventBroker;
this.descriptionService = descriptionService;
this.fileTransformerService = fileTransformerService;
this.eventHandler = eventHandler;
this.notificationProperties = notificationProperties;
this.actionConfirmationService = actionConfirmationService;
this.validatorFactory = validatorFactory;
this.elasticService = elasticService;
}
public Dmp persist(DmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException {
this.authorizationService.authorizeForce(Permission.EditDmp);
DmpEntity data = this.patchAndSave(model);
this.patchAndSaveReferences(model.getReferences(), data.getId());
this.patchAndSaveTemplates(data.getId(), model.getDescriptionTemplates());
this.eventBroker.emit(new DmpTouchedEvent(data.getId()));
this.sendNotification(data);
this.elasticService.persistDmp(data);
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
private void sendNotification(DmpEntity dmp) throws InvalidApplicationException {
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(dmp.getId())
.isActives(IsActive.Active)
.collect();
if (existingUsers == null || existingUsers.size() <= 1){
return;
}
for (DmpUserEntity dmpUser : existingUsers) {
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
if (user != null){
NotificationIntegrationEvent event = new NotificationIntegrationEvent();
event.setUserId(this.userScope.getUserId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
event.setContactTypeHint(NotificationContactType.EMAIL);
event = this.applyNotificationType(dmp.getStatus(), event);
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName()));
fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserId()).first().getName()));
fieldInfoList.add(new FieldInfo("{name}", DataType.String, dmp.getLabel()));
fieldInfoList.add(new FieldInfo("{id}", DataType.String, dmp.getId().toString()));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
}
}
}
private NotificationIntegrationEvent applyNotificationType(DmpStatus status, NotificationIntegrationEvent event) {
switch (status) {
case Draft:
event.setNotificationType(notificationProperties.getDmpModifiedType());
return event;
case Finalized:
event.setNotificationType(notificationProperties.getDmpFinalisedType());
return event;
default:
throw new MyApplicationException("Unsupported Dmp Status.");
}
}
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
logger.debug("deleting dmp: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteDmp);
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
}
@Override
public Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException {
this.authorizationService.authorizeForce(Permission.CreateNewVersionDmp);
DmpEntity oldDmpEntity = this.entityManager.find(DmpEntity.class, model.getId());
if (oldDmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(oldDmpEntity.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
DmpQuery latestVersionDmpEntityQuery = this.queryFactory.query(DmpQuery.class).groupIds(oldDmpEntity.getGroupId()).versionStatuses(DmpVersionStatus.Current);
List<DmpEntity> latestVersionDmps = latestVersionDmpEntityQuery.collect();
if (latestVersionDmps.isEmpty()) throw new MyValidationException("Previous dmp not found");
if (latestVersionDmps.size() > 1) throw new MyValidationException("Multiple previous dmps found");
if (!latestVersionDmps.get(0).getVersion().equals(oldDmpEntity.getVersion())){
throw new MyValidationException(this.errors.getDmpNewVersionConflict().getCode(), this.errors.getDmpNewVersionConflict().getMessage());
}
DmpEntity newDmp = new DmpEntity();
newDmp.setId(UUID.randomUUID());
newDmp.setIsActive(IsActive.Active);
newDmp.setCreatedAt(Instant.now());
newDmp.setUpdatedAt(Instant.now());
newDmp.setGroupId(oldDmpEntity.getGroupId());
newDmp.setVersionStatus(DmpVersionStatus.Current);
newDmp.setVersion((short)(oldDmpEntity.getVersion() + 1));
newDmp.setDescription(model.getDescription());
newDmp.setLabel(model.getLabel());
newDmp.setLanguage(oldDmpEntity.getLanguage());
newDmp.setStatus(DmpStatus.Draft);
newDmp.setProperties(oldDmpEntity.getProperties());
newDmp.setBlueprintId(model.getBlueprintId());
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(model.getId())
.isActives(IsActive.Active)
.collect();
List<DmpReferenceEntity> dmpReferences = this.queryFactory.query(DmpReferenceQuery.class)
.dmpIds(model.getId())
.isActives(IsActive.Active)
.collect();
List<DmpDescriptionTemplateEntity> dmpDescriptionTemplates = this.queryFactory.query(DmpDescriptionTemplateQuery.class)
.dmpIds(model.getId())
.isActive(IsActive.Active)
.collect();
for (DmpUserEntity dmpUser : dmpUsers) {
DmpUserEntity newUser = new DmpUserEntity();
newUser.setId(UUID.randomUUID());
newUser.setDmpId(newDmp.getId());
newUser.setUserId(dmpUser.getUserId());
newUser.setRole(dmpUser.getRole());
newUser.setCreatedAt(Instant.now());
newUser.setUpdatedAt(Instant.now());
newUser.setIsActive(IsActive.Active);
this.entityManager.persist(newUser);
}
for (DmpReferenceEntity dmpReference : dmpReferences) {
DmpReferenceEntity newReference = new DmpReferenceEntity();
newReference.setId(UUID.randomUUID());
newReference.setDmpId(newDmp.getId());
newReference.setReferenceId(dmpReference.getReferenceId());
newReference.setData(dmpReference.getData());
newReference.setCreatedAt(Instant.now());
newReference.setUpdatedAt(Instant.now());
newReference.setIsActive(IsActive.Active);
this.entityManager.persist(newReference);
}
for (DmpDescriptionTemplateEntity dmpDescriptionTemplate : dmpDescriptionTemplates) {
DmpDescriptionTemplateEntity newTemplate = new DmpDescriptionTemplateEntity();
newTemplate.setId(UUID.randomUUID());
newTemplate.setDmpId(newDmp.getId());
newTemplate.setDescriptionTemplateGroupId(dmpDescriptionTemplate.getDescriptionTemplateGroupId());
newTemplate.setSectionId(dmpDescriptionTemplate.getSectionId());
newTemplate.setCreatedAt(Instant.now());
newTemplate.setUpdatedAt(Instant.now());
newTemplate.setIsActive(IsActive.Active);
this.entityManager.persist(newTemplate);
}
for (UUID descriptionId : model.getDescriptions()) {
this.descriptionService.clone(newDmp.getId(), descriptionId);
}
this.entityManager.persist(newDmp);
oldDmpEntity.setVersionStatus(DmpVersionStatus.Previous);
this.entityManager.merge(oldDmpEntity);
this.entityManager.flush();
this.elasticService.persistDmp(oldDmpEntity);
this.elasticService.persistDmp(newDmp);
return this.builderFactory.builder(DmpBuilder.class).build(BaseFieldSet.build(fields, Dmp._id), newDmp);
}
@Override
public Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException {
this.authorizationService.authorizeForce(Permission.CloneDmp);
DmpEntity existingDmpEntity = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).ids(model.getId()).firstAs(fields);
if (!this.conventionService.isValidGuid(model.getId()) || existingDmpEntity == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
DmpEntity newDmp = new DmpEntity();
newDmp.setId(UUID.randomUUID());
newDmp.setIsActive(IsActive.Active);
newDmp.setCreatedAt(Instant.now());
newDmp.setUpdatedAt(Instant.now());
newDmp.setGroupId(UUID.randomUUID());
newDmp.setVersion((short) 1);
newDmp.setDescription(model.getDescription());
newDmp.setLabel(model.getLabel());
newDmp.setLanguage(existingDmpEntity.getLanguage());
newDmp.setStatus(DmpStatus.Draft);
newDmp.setProperties(existingDmpEntity.getProperties());
newDmp.setBlueprintId(existingDmpEntity.getBlueprintId());
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(model.getId())
.isActives(IsActive.Active)
.collect();
List<DmpReferenceEntity> dmpReferences = this.queryFactory.query(DmpReferenceQuery.class)
.dmpIds(model.getId())
.isActives(IsActive.Active)
.collect();
List<DmpDescriptionTemplateEntity> dmpDescriptionTemplates = this.queryFactory.query(DmpDescriptionTemplateQuery.class)
.dmpIds(model.getId())
.isActive(IsActive.Active)
.collect();
for (DmpUserEntity dmpUser : dmpUsers) {
DmpUserEntity newUser = new DmpUserEntity();
newUser.setId(UUID.randomUUID());
newUser.setDmpId(newDmp.getId());
newUser.setUserId(dmpUser.getUserId());
newUser.setRole(dmpUser.getRole());
newUser.setCreatedAt(Instant.now());
newUser.setUpdatedAt(Instant.now());
newUser.setIsActive(IsActive.Active);
this.entityManager.persist(newUser);
}
for (DmpReferenceEntity dmpReference : dmpReferences) {
DmpReferenceEntity newReference = new DmpReferenceEntity();
newReference.setId(UUID.randomUUID());
newReference.setDmpId(newDmp.getId());
newReference.setReferenceId(dmpReference.getReferenceId());
newReference.setData(dmpReference.getData());
newReference.setCreatedAt(Instant.now());
newReference.setUpdatedAt(Instant.now());
newReference.setIsActive(IsActive.Active);
this.entityManager.persist(newReference);
}
for (DmpDescriptionTemplateEntity dmpDescriptionTemplate : dmpDescriptionTemplates) {
DmpDescriptionTemplateEntity newTemplate = new DmpDescriptionTemplateEntity();
newTemplate.setId(UUID.randomUUID());
newTemplate.setDmpId(newDmp.getId());
newTemplate.setDescriptionTemplateGroupId(dmpDescriptionTemplate.getDescriptionTemplateGroupId());
newTemplate.setSectionId(dmpDescriptionTemplate.getSectionId());
newTemplate.setCreatedAt(Instant.now());
newTemplate.setUpdatedAt(Instant.now());
newTemplate.setIsActive(IsActive.Active);
this.entityManager.persist(newTemplate);
}
this.entityManager.flush();
this.elasticService.persistDmp(newDmp);
DmpEntity resultingDmpEntity = this.queryFactory.query(DmpQuery.class).ids(newDmp.getId()).firstAs(fields);
return this.builderFactory.builder(DmpBuilder.class).build(fields, resultingDmpEntity);
}
@Override
public List<DmpUser> assignUsers(UUID dmpId, List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
this.authorizationService.authorizeForce(Permission.AssignDmpUsers);
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, dmpId);
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(dmpId)
.isActives(IsActive.Active)
.collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
for (DmpUserPersist dmpUser : model) {
DmpUserEntity dmpUserEntity = existingUsers.stream().filter(x-> x.getDmpId().equals(dmpId) && x.getUserId().equals(dmpUser.getUser()) && x.getRole().equals(dmpUser.getRole())).findFirst().orElse(null);
if (dmpUserEntity == null){
dmpUserEntity = new DmpUserEntity();
dmpUserEntity.setId(UUID.randomUUID());
dmpUserEntity.setDmpId(dmpId);
dmpUserEntity.setUserId(dmpUser.getUser());
dmpUserEntity.setRole(dmpUser.getRole());
dmpUserEntity.setCreatedAt(Instant.now());
dmpUserEntity.setUpdatedAt(Instant.now());
dmpUserEntity.setIsActive(IsActive.Active);
this.entityManager.persist(dmpUserEntity);
}
updatedCreatedIds.add(dmpUserEntity.getUserId());
}
List<DmpUserEntity> toDelete = existingUsers.stream().filter(x-> updatedCreatedIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
if (!toDelete.isEmpty()) this.deleterFactory.deleter(DmpUserDeleter.class).delete(toDelete);
this.entityManager.flush();
List<DmpUserEntity> persisted = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(dmpId)
.isActives(IsActive.Active)
.collect();
this.elasticService.persistDmp(dmpEntity);
return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted);
}
@Override
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
this.authorizationService.authorizeForce(Permission.AssignDmpUsers);
DmpEntity data = this.entityManager.find(DmpEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class)
.dmpIds(model.getDmpId()).ids(model.getId()).userRoles(model.getRole())
.collect();
if (!existingUsers.isEmpty()) this.deleterFactory.deleter(DmpUserDeleter.class).delete(existingUsers);
this.entityManager.flush();
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, model.getDmpId());
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDmpId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.elasticService.persistDmp(dmpEntity);
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
@Override
public ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException {
HttpHeaders headers = new HttpHeaders();
String type = exportType;
FileEnvelope fileEnvelope = this.fileTransformerService.exportDmp(id, type);
headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename());
byte[] data = Files.readAllBytes(fileEnvelope.getFile().toPath());
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<>(data, headers, HttpStatus.OK);
}
private DmpEntity patchAndSave(DmpPersist model) throws JsonProcessingException, InvalidApplicationException {
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
DmpEntity data;
DmpUserEntity dmpUserEntity = new DmpUserEntity();
if (isUpdate) {
data = this.entityManager.find(DmpEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (model.getStatus() != null && model.getStatus() == DmpStatus.Finalized && data.getStatus() != DmpStatus.Finalized) {
this.authorizationService.authorizeForce(Permission.FinalizeDmp);
data.setStatus(model.getStatus());
data.setFinalizedAt(Instant.now());
}
} else {
data = new DmpEntity();
data.setId(UUID.randomUUID());
data.setGroupId(UUID.randomUUID());
data.setVersion((short) 1);
data.setStatus(DmpStatus.Draft);
data.setVersionStatus(DmpVersionStatus.Current);
data.setCreatorId(userScope.getUserId());
data.setBlueprintId(model.getBlueprint());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
dmpUserEntity.setId(UUID.randomUUID());
dmpUserEntity.setDmpId(data.getId());
dmpUserEntity.setUserId(userScope.getUserId());
dmpUserEntity.setRole(DmpUserRole.Owner);
dmpUserEntity.setCreatedAt(Instant.now());
dmpUserEntity.setUpdatedAt(Instant.now());
dmpUserEntity.setIsActive(IsActive.Active);
}
data.setLabel(model.getLabel());
data.setProperties(this.jsonHandlingService.toJson(this.buildDmpPropertiesEntity(model.getProperties())));
data.setDescription(model.getDescription());
data.setAccessType(model.getAccessType());
data.setUpdatedAt(Instant.now());
if (isUpdate)
this.entityManager.merge(data);
else {
this.entityManager.persist(data);
this.entityManager.persist(dmpUserEntity);
}
this.entityManager.flush();
return data;
}
private @NotNull DmpPropertiesEntity buildDmpPropertiesEntity(DmpPropertiesPersist persist){
DmpPropertiesEntity data = new DmpPropertiesEntity();
if (persist == null) return data;
if (!this.conventionService.isListNullOrEmpty(persist.getContacts())){
data.setContacts(new ArrayList<>());
for (DmpContactPersist contactPersist: persist.getContacts()) {
data.getContacts().add(this.buildDmpContactEntity(contactPersist));
}
}
if (!this.conventionService.isListNullOrEmpty(persist.getDmpBlueprintValues())){
data.setDmpBlueprintValues(new ArrayList<>());
for (DmpBlueprintValuePersist fieldValuePersist: persist.getDmpBlueprintValues()) {
data.getDmpBlueprintValues().add(this.buildDmpBlueprintValueEntity(fieldValuePersist));
}
}
return data;
}
private @NotNull DmpContactEntity buildDmpContactEntity(DmpContactPersist persist){
DmpContactEntity data = new DmpContactEntity();
if (persist == null) return data;
data.setEmail(persist.getEmail());
data.setLastName(persist.getLastName());
data.setFirstName(persist.getFirstName());
data.setUserId(persist.getUserId());
return data;
}
private @NotNull DmpBlueprintValueEntity buildDmpBlueprintValueEntity(DmpBlueprintValuePersist persist){
DmpBlueprintValueEntity data = new DmpBlueprintValueEntity();
if (persist == null) return data;
data.setValue(persist.getFieldValue());
data.setFieldId(persist.getFieldId());
return data;
}
private void patchAndSaveReferences(List<DmpReferencePersist> models, UUID dmpId) throws InvalidApplicationException {
if (models == null || models.isEmpty())
return;
List<DmpReferenceEntity> references = this.queryFactory.query(DmpReferenceQuery.class).dmpIds(dmpId).collect();
Map<UUID, List<DmpReferenceEntity>> referencesLookup = this.conventionService.toDictionaryOfList(references, DmpReferenceEntity::getDmpId);
List<ReferenceEntity> existingReferences;
if (referencesLookup.containsKey(dmpId))
existingReferences = this.queryFactory.query(ReferenceQuery.class).ids(referencesLookup.get(dmpId).stream().map(DmpReferenceEntity::getId).toList()).collect();
else
existingReferences = new ArrayList<>();
List<UUID> updatedReferencesIds = models.stream().map(x -> x.getReference().getId()).filter(this.conventionService::isValidGuid).distinct().toList();
List<ReferenceEntity> toDelete = existingReferences.stream().filter(x -> !updatedReferencesIds.contains(x.getId())).toList();
List<DmpReferenceEntity> dmpReferenceRecordsToDelete = this.queryFactory.query(DmpReferenceQuery.class).referenceIds(toDelete.stream().map(ReferenceEntity::getId).toList()).collect();
this.deleterFactory.deleter(DmpReferenceDeleter.class).delete(dmpReferenceRecordsToDelete);
Map<UUID, ReferenceEntity> existingReferencesLookup = existingReferences.stream().collect(Collectors.toMap(ReferenceEntity::getId, x -> x));
for (DmpReferencePersist model : models) {
ReferencePersist referenceModel = model.getReference();
Boolean isUpdate = this.conventionService.isValidGuid(referenceModel.getId());
ReferenceEntity data;
if (isUpdate) {
if (!existingReferencesLookup.containsKey(referenceModel.getId()))
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{referenceModel.getId(), Reference.class.getSimpleName()}, LocaleContextHolder.getLocale()));
data = existingReferencesLookup.get(referenceModel.getId());
if (data == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{referenceModel.getId(), Reference.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(referenceModel.getHash()))
throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
} else {
data = new ReferenceEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
DmpReferenceEntity dmpReference = new DmpReferenceEntity();
dmpReference.setReferenceId(data.getId());
dmpReference.setDmpId(dmpId);
dmpReference.setCreatedAt(Instant.now());
dmpReference.setUpdatedAt(Instant.now());
dmpReference.setIsActive(IsActive.Active);
dmpReference.setData(model.getData());
this.entityManager.persist(dmpReference);
}
data.setDefinition(this.xmlHandlingService.toXmlSafe(this.buildDefinitionEntity(referenceModel.getDefinition())));
data.setUpdatedAt(Instant.now());
data.setReference(referenceModel.getReference());
data.setAbbreviation(referenceModel.getAbbreviation());
data.setSource(referenceModel.getSource());
data.setSourceType(referenceModel.getSourceType());
if (isUpdate)
this.entityManager.merge(data);
else
this.entityManager.persist(data);
}
this.entityManager.flush();
}
private void patchAndSaveTemplates(UUID id, List<DmpDescriptionTemplatePersist> models) throws InvalidApplicationException {
if (models == null) models = new ArrayList<>();
List<DmpDescriptionTemplateEntity> items = this.queryFactory.query(DmpDescriptionTemplateQuery.class).isActive(IsActive.Active).dmpIds(id).collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
for (DmpDescriptionTemplatePersist model : models) {
DmpDescriptionTemplateEntity data = items.stream().filter(x -> x.getDescriptionTemplateGroupId().equals(model.getDescriptionTemplateGroupId()) && x.getSectionId().equals(model.getSectionId())).findFirst().orElse(null);
if (data == null){
data = new DmpDescriptionTemplateEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
data.setDmpId(id);
data.setSectionId(model.getSectionId());
data.setDescriptionTemplateGroupId(model.getDescriptionTemplateGroupId());
this.entityManager.persist(data);
}
updatedCreatedIds.add(data.getId());
}
List<DmpDescriptionTemplateEntity> toDelete = items.stream().filter(x-> updatedCreatedIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList());
this.deleterFactory.deleter(DmpDescriptionTemplateDeleter.class).delete(toDelete);
}
private @NotNull DefinitionEntity buildDefinitionEntity(DefinitionPersist persist){
DefinitionEntity data = new DefinitionEntity();
if (persist == null) return data;
if (!this.conventionService.isListNullOrEmpty(persist.getFields())){
data.setFields(new ArrayList<>());
for (FieldPersist fieldPersist: persist.getFields()) {
data.getFields().add(this.buildDmpContactEntity(fieldPersist));
}
}
return data;
}
private @NotNull FieldEntity buildDmpContactEntity(FieldPersist persist){
FieldEntity data = new FieldEntity();
if (persist == null) return data;
data.setCode(persist.getCode());
data.setDataType(persist.getDataType());
data.setCode(persist.getCode());
return data;
}
private DmpUserEntity checkUserRoleIfExists(List<DmpUserEntity> dmpUserEntities, UUID dmp, UUID user, DmpUserRole role) {
for (DmpUserEntity dmpUser : dmpUserEntities) {
if (dmpUser.getDmpId().equals(dmp) && dmpUser.getUserId().equals(user) && dmpUser.getRole() == role) {
return dmpUser;
};
}
return null;
}
// invites
public void inviteUsers(UUID id, DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
this.authorizationService.authorizeForce(Permission.InviteDmpUsers);
DmpEntity dmp = this.queryFactory.query(DmpQuery.class).ids(id).first();
if (dmp == null){
throw new InvalidApplicationException("Dmp does not exist!");
}
for (DmpUserInviteTypePersist type :model.getUsers()) {
if (type.getUserId() != null){
DmpUserPersist dmpUserPersist = new DmpUserPersist();
dmpUserPersist.setUser(type.getUserId());
dmpUserPersist.setRole(model.getRole());
this.assignUsers(id, List.of(dmpUserPersist), null);
this.sendDmpInvitationExistingUser(type.getUserId(), dmp, model.getRole());
} else if (type.getEmail() != null) {
this.sendDmpInvitationExternalUser(type.getEmail(),dmp, model.getRole());
}
}
}
private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException {
UserEntity recipient = this.queryFactory.query(UserQuery.class).ids(userId).isActive(IsActive.Active).first();
String email = this.queryFactory.query(UserContactInfoQuery.class).userIds(recipient.getId()).first().getValue();
NotificationIntegrationEvent event = new NotificationIntegrationEvent();
event.setUserId(this.userScope.getUserIdSafe());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
event.setContactTypeHint(NotificationContactType.EMAIL);
event.setNotificationType(notificationProperties.getDmpInvitationExistingUserType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, recipient.getName()));
fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserIdSafe()).first().getName()));
fieldInfoList.add(new FieldInfo("{dmpname}", DataType.String, dmp.getLabel()));
fieldInfoList.add(new FieldInfo("{dmprole}", DataType.String, role.toString()));
fieldInfoList.add(new FieldInfo("{id}", DataType.String, dmp.getId().toString()));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
private void sendDmpInvitationExternalUser(String email, DmpEntity dmp, DmpUserRole role) throws JAXBException, InvalidApplicationException {
String token = this.createActionConfirmation(email, dmp, role);
NotificationIntegrationEvent event = new NotificationIntegrationEvent();
event.setUserId(this.userScope.getUserIdSafe());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
event.setContactTypeHint(NotificationContactType.EMAIL);
event.setNotificationType(notificationProperties.getDmpInvitationExternalUserType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, email));
fieldInfoList.add(new FieldInfo("{confirmationToken}", DataType.String, token));
fieldInfoList.add(new FieldInfo("{dmpname}", DataType.String, dmp.getLabel()));
fieldInfoList.add(new FieldInfo("{dmprole}", DataType.String, role.toString()));
data.setFields(fieldInfoList);
event.setData(jsonHandlingService.toJsonSafe(data));
eventHandler.handle(event);
}
private String createActionConfirmation(String email, DmpEntity dmp, DmpUserRole role) throws JAXBException, InvalidApplicationException {
ActionConfirmationPersist persist = new ActionConfirmationPersist();
persist.setType(ActionConfirmationType.DmpInvitation);
persist.setStatus(ActionConfirmationStatus.Requested);
persist.setToken(UUID.randomUUID().toString());
persist.setDmpInvitation(new DmpInvitationPersist(email, dmp.getId(), role));
persist.setCreatedById(this.userScope.getUserIdSafe());
persist.setExpiresAt(Instant.now().plusSeconds(this.notificationProperties.getEmailExpirationTimeSeconds()));
validatorFactory.validator(ActionConfirmationPersist.ActionConfirmationPersistValidator.class).validateForce(persist);
this.actionConfirmationService.persist(persist, null);
return persist.getToken();
}
public void dmpInvitationAccept(String token) throws InvalidApplicationException, IOException {
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.DmpInvitation).isActive(IsActive.Active).first();
if (action == null){
throw new MyApplicationException("Token does not exist!");
}
if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){
throw new MyApplicationException("Invitation is already confirmed!");
}
if (action.getExpiresAt().compareTo(Instant.now()) < 0){
throw new MyApplicationException("Token has expired!");
}
DmpInvitationEntity dmpInvitation = this.xmlHandlingService.fromXmlSafe(DmpInvitationEntity.class, action.getData());
DmpUserPersist model = new DmpUserPersist();
model.setUser(this.userScope.getUserIdSafe());
model.setRole(dmpInvitation.getRole());
this.assignUsers(dmpInvitation.getDmpId(), List.of(model), null);
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
}
}