From 2f20675348fc85e9c889410d941aa05724dad5dc Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Fri, 3 Nov 2023 16:02:47 +0200 Subject: [PATCH] Adding reference persisting on dmp persist (work in progress), adding separate persist models for dmp properties --- .../errorcode/ErrorThesaurusProperties.java | 10 + .../eu/eudat/model/persist/DmpPersist.java | 12 ++ .../eudat/model/persist/ReferencePersist.java | 10 + .../dmpproperties/DmpBlueprintValue.java | 1 + .../DmpBlueprintValuePersist.java | 35 ++++ .../persist/dmpproperties/DmpContact.java | 1 + .../dmpproperties/DmpContactPersist.java | 45 +++++ .../dmpproperties/DmpPropertiesPersist.java | 12 +- .../DescriptionTemplateTypeServiceImpl.java | 2 +- .../java/eu/eudat/service/dmp/DmpService.java | 2 +- .../eu/eudat/service/dmp/DmpServiceImpl.java | 175 ++++++++++++++---- .../web/src/main/resources/config/errors.yml | 3 + 12 files changed, 262 insertions(+), 46 deletions(-) create mode 100644 dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValuePersist.java create mode 100644 dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContactPersist.java diff --git a/dmp-backend/core/src/main/java/eu/eudat/errorcode/ErrorThesaurusProperties.java b/dmp-backend/core/src/main/java/eu/eudat/errorcode/ErrorThesaurusProperties.java index 929ae2a8f..53b6cd926 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/errorcode/ErrorThesaurusProperties.java +++ b/dmp-backend/core/src/main/java/eu/eudat/errorcode/ErrorThesaurusProperties.java @@ -54,4 +54,14 @@ public class ErrorThesaurusProperties { public void setDescriptionTemplateNewVersionConflict(ErrorDescription descriptionTemplateNewVersionConflict) { DescriptionTemplateNewVersionConflict = descriptionTemplateNewVersionConflict; } + + private ErrorDescription dmpNewVersionConflict; + + public ErrorDescription getDmpNewVersionConflict() { + return dmpNewVersionConflict; + } + + public void setDmpNewVersionConflict(ErrorDescription dmpNewVersionConflict) { + this.dmpNewVersionConflict = dmpNewVersionConflict; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java index d1b2f8b45..fd6133d8a 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java @@ -2,9 +2,11 @@ package eu.eudat.model.persist; import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.validation.ValidId; +import eu.eudat.model.Reference; import eu.eudat.model.persist.dmpproperties.DmpPropertiesPersist; import java.time.Instant; +import java.util.List; import java.util.UUID; public class DmpPersist { @@ -22,6 +24,8 @@ public class DmpPersist { private String language; + private List references; + private String hash; public UUID getId() { @@ -72,6 +76,14 @@ public class DmpPersist { this.language = language; } + public List getReferences() { + return references; + } + + public void setReferences(List references) { + this.references = references; + } + public String getHash() { return hash; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java index 29250acd6..ea0073ad8 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java @@ -46,6 +46,8 @@ public class ReferencePersist { @ValidEnum(message = "{validation.empty}") private ReferenceSourceType sourceType; + private String hash; + //private UserInfoPersist createdBy; ToDo @@ -120,4 +122,12 @@ public class ReferencePersist { public void setSourceType(ReferenceSourceType sourceType) { this.sourceType = sourceType; } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValue.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValue.java index f255fc7cd..e3302e132 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValue.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValue.java @@ -21,4 +21,5 @@ public class DmpBlueprintValue { public void setFieldValue(String fieldValue) { this.fieldValue = fieldValue; } + } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValuePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValuePersist.java new file mode 100644 index 000000000..22256f6cb --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpBlueprintValuePersist.java @@ -0,0 +1,35 @@ +package eu.eudat.model.persist.dmpproperties; + +public class DmpBlueprintValuePersist { + + private String fieldId; + + private String fieldName; + + private String fieldValue; + + public String getFieldId() { + return fieldId; + } + + public void setFieldId(String fieldId) { + this.fieldId = fieldId; + } + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public String getFieldValue() { + return fieldValue; + } + + public void setFieldValue(String fieldValue) { + this.fieldValue = fieldValue; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContact.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContact.java index c60fe0c08..99519ee5b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContact.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContact.java @@ -41,4 +41,5 @@ public class DmpContact { public void setEmail(String email) { this.email = email; } + } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContactPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContactPersist.java new file mode 100644 index 000000000..b7309b775 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpContactPersist.java @@ -0,0 +1,45 @@ +package eu.eudat.model.persist.dmpproperties; + +public class DmpContactPersist { + + String userId; + + String firstName; + + String lastName; + + String email; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpPropertiesPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpPropertiesPersist.java index 849af6404..aeb38f7dc 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpPropertiesPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/dmpproperties/DmpPropertiesPersist.java @@ -4,23 +4,23 @@ import java.util.List; public class DmpPropertiesPersist { - private List dmpBlueprintValues; + private List dmpBlueprintValues; - private List contacts; + private List contacts; - public List getDmpBlueprintValues() { + public List getDmpBlueprintValues() { return dmpBlueprintValues; } - public void setDmpBlueprintValues(List dmpBlueprintValues) { + public void setDmpBlueprintValues(List dmpBlueprintValues) { this.dmpBlueprintValues = dmpBlueprintValues; } - public List getContacts() { + public List getContacts() { return contacts; } - public void setContacts(List contacts) { + public void setContacts(List contacts) { this.contacts = contacts; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplatetype/DescriptionTemplateTypeServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplatetype/DescriptionTemplateTypeServiceImpl.java index 59c7f71b8..68d2e388c 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplatetype/DescriptionTemplateTypeServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplatetype/DescriptionTemplateTypeServiceImpl.java @@ -112,7 +112,7 @@ public class DescriptionTemplateTypeServiceImpl implements DescriptionTemplateTy } public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { - logger.debug("deleting dataset: {}", id); + logger.debug("deleting descriptionTemplateType: {}", id); this.authorizationService.authorizeForce(Permission.DeleteDescriptionTemplateType); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java index 04c5875ce..43301a64f 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpService.java @@ -19,7 +19,7 @@ import java.util.UUID; public interface DmpService { - Dmp persist(DmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; + Dmp persist(DmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java index 7de87d0e5..1ac3e701e 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmp/DmpServiceImpl.java @@ -4,20 +4,29 @@ 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.IsActive; +import eu.eudat.commons.types.reference.DefinitionEntity; +import eu.eudat.commons.types.reference.FieldEntity; import eu.eudat.convention.ConventionService; -import eu.eudat.data.DescriptionTemplateEntity; import eu.eudat.data.DmpEntity; +import eu.eudat.data.DmpReferenceEntity; +import eu.eudat.data.ReferenceEntity; import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.event.DmpTouchedEvent; import eu.eudat.event.EventBroker; -import eu.eudat.model.DescriptionTemplate; import eu.eudat.model.Dmp; -import eu.eudat.model.builder.DescriptionTemplateBuilder; +import eu.eudat.model.Reference; import eu.eudat.model.builder.DmpBuilder; import eu.eudat.model.deleter.DmpDeleter; +import eu.eudat.model.deleter.ReferenceDeleter; import eu.eudat.model.persist.DmpPersist; +import eu.eudat.model.persist.ReferencePersist; +import eu.eudat.model.persist.referencedefinition.DefinitionPersist; +import eu.eudat.model.persist.referencedefinition.FieldPersist; import eu.eudat.query.DmpQuery; +import eu.eudat.query.DmpReferenceQuery; +import eu.eudat.query.ReferenceQuery; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; @@ -33,6 +42,7 @@ import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; 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; @@ -43,8 +53,11 @@ import javax.management.InvalidApplicationException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import java.time.Instant; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @Service public class DmpServiceImpl implements DmpService { @@ -67,6 +80,8 @@ public class DmpServiceImpl implements DmpService { private final MessageSource messageSource; + private final XmlHandlingService xmlHandlingService; + private final JsonHandlingService jsonHandlingService; private final EventBroker eventBroker; @@ -80,7 +95,7 @@ public class DmpServiceImpl implements DmpService { QueryFactory queryFactory, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, - JsonHandlingService jsonHandlingService, + XmlHandlingService xmlHandlingService, JsonHandlingService jsonHandlingService, EventBroker eventBroker) { this.entityManager = entityManager; this.authorizationService = authorizationService; @@ -90,49 +105,23 @@ public class DmpServiceImpl implements DmpService { this.conventionService = conventionService; this.errors = errors; this.messageSource = messageSource; + this.xmlHandlingService = xmlHandlingService; this.jsonHandlingService = jsonHandlingService; this.eventBroker = eventBroker; } - public Dmp persist(DmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { + public Dmp persist(DmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException { logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields)); this.authorizationService.authorizeForce(Permission.EditDmp); - Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); + DmpEntity data = this.patchAndSave(model); - DmpEntity data; - 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()); - } else { - data = new DmpEntity(); - data.setId(UUID.randomUUID()); - data.setGroupId(UUID.randomUUID()); - data.setVersion((short) 1); - data.setIsActive(IsActive.Active); - data.setCreatedAt(Instant.now()); - } - - data.setLabel(model.getLabel()); - data.setStatus(model.getStatus()); - try { - data.setProperties(this.jsonHandlingService.toJson(model.getProperties())); - } catch (JsonProcessingException e) { - logger.error(e.getMessage(), e); - } - data.setDescription(model.getDescription()); - data.setUpdatedAt(Instant.now()); - if (isUpdate) - this.entityManager.merge(data); - else - this.entityManager.persist(data); - - this.entityManager.flush(); + this.patchAndSave(model.getReferences(), data.getId()); this.eventBroker.emit(new DmpTouchedEvent(data.getId())); - return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(BaseFieldSet.build(fields, Dmp._id), data); + + return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data); } public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { @@ -156,8 +145,7 @@ public class DmpServiceImpl implements DmpService { latestVersionDmpEntityQuery.setOrder(new Ordering().addDescending(Dmp._version)); DmpEntity latestVersionDmpEntity = latestVersionDmpEntityQuery.first(); if (!latestVersionDmpEntity.getVersion().equals(oldDmpEntity.getVersion())){ - //TODO: (THGIANNOS) Create event for version conflict -// throw new MyValidationException(this.errors.getDescriptionTemplateNewVersionConflict().getCode(), this.errors.getDescriptionTemplateNewVersionConflict().getMessage()); + throw new MyValidationException(this.errors.getDmpNewVersionConflict().getCode(), this.errors.getDmpNewVersionConflict().getMessage()); } DmpEntity data = new DmpEntity(); @@ -180,4 +168,115 @@ public class DmpServiceImpl implements DmpService { return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(BaseFieldSet.build(fields, Dmp._id), data); } + private DmpEntity patchAndSave(DmpPersist model) throws JsonProcessingException { + Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); + + DmpEntity data; + 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()); + } else { + data = new DmpEntity(); + data.setId(UUID.randomUUID()); + data.setGroupId(UUID.randomUUID()); + data.setVersion((short) 1); + data.setIsActive(IsActive.Active); + data.setCreatedAt(Instant.now()); + } + + data.setLabel(model.getLabel()); + data.setStatus(model.getStatus()); + data.setProperties(this.jsonHandlingService.toJson(model.getProperties())); + data.setDescription(model.getDescription()); + data.setUpdatedAt(Instant.now()); + if (isUpdate) + this.entityManager.merge(data); + else + this.entityManager.persist(data); + + this.entityManager.flush(); + + return data; + } + + private void patchAndSave(List models, UUID dmpId) throws InvalidApplicationException { + if (models == null || models.isEmpty()) + return; + + List references = this.queryFactory.query(DmpReferenceQuery.class).dmpIds(dmpId).collect(); + Map> referencesLookup = this.conventionService.toDictionaryOfList(references, DmpReferenceEntity::getDmpId); + + List 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 updatedReferencesIds = models.stream().map(ReferencePersist::getId).filter(this.conventionService::isValidGuid).distinct().toList(); + List toDelete = existingReferences.stream().filter(x -> !updatedReferencesIds.contains(x.getId())).toList(); + this.deleterFactory.deleter(ReferenceDeleter.class).delete(toDelete); + + Map existingReferencesLookup = existingReferences.stream().collect(Collectors.toMap(ReferenceEntity::getId, x -> x)); + + for (ReferencePersist model : models) { + Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); + + ReferenceEntity data; + if (isUpdate) { + if (!existingReferencesLookup.containsKey(model.getId())) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Reference.class.getSimpleName()}, LocaleContextHolder.getLocale())); + data = existingReferencesLookup.get(model.getId()); + if (data == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Reference.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.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()); + } + + data.setDefinition(this.xmlHandlingService.toXmlSafe(this.buildDefinitionEntity(model.getDefinition()))); + data.setUpdatedAt(Instant.now()); + data.setReference(model.getReference()); + data.setAbbreviation(model.getAbbreviation()); + data.setSource(model.getSource()); + data.setSourceType(model.getSourceType()); + + if (isUpdate) + this.entityManager.merge(data); + else + this.entityManager.persist(data); + + } + this.entityManager.flush(); + + } + + 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.buildFieldEntity(fieldPersist)); + } + } + + return data; + } + + private @NotNull FieldEntity buildFieldEntity(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; + } + } diff --git a/dmp-backend/web/src/main/resources/config/errors.yml b/dmp-backend/web/src/main/resources/config/errors.yml index e566b2f1a..4e7d42118 100644 --- a/dmp-backend/web/src/main/resources/config/errors.yml +++ b/dmp-backend/web/src/main/resources/config/errors.yml @@ -29,3 +29,6 @@ error-thesaurus: description-template-new-version-conflict: code: 114 message: version to update not the latest + dmp-new-version-conflict: + code: 115 + message: version to update not the latest