diff --git a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java index 5c887e269..49de636cb 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java +++ b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java @@ -44,6 +44,7 @@ public class AuditableAction { public static final EventId Dmp_PublicLookup = new EventId(5012, "Dmp_PublicLookup"); public static final EventId Dmp_Finalize = new EventId(5013, "Dmp_Finalize"); public static final EventId Dmp_Undo_Finalize = new EventId(5014, "Dmp_Undo_Finalize"); + public static final EventId Dmp_Validate = new EventId(5015, "Dmp_Validate"); public static final EventId Description_Query = new EventId(6000, "Description_Query"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpValidationOutput.java b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpValidationOutput.java new file mode 100644 index 000000000..421ffa0c4 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpValidationOutput.java @@ -0,0 +1,30 @@ +package eu.eudat.commons.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import eu.eudat.data.converters.enums.DatabaseEnum; + +import java.util.Map; + +public enum DmpValidationOutput implements DatabaseEnum { + + Valid((short) 1), + Invalid((short) 2); + + private final Short value; + + DmpValidationOutput(Short value) { + this.value = value; + } + + @JsonValue + public Short getValue() { + return value; + } + + private static final Map map = EnumUtils.getEnumValueMap(DmpValidationOutput.class); + + public static DmpValidationOutput of(Short i) { + return map.get(i); + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/DmpValidationResult.java b/dmp-backend/core/src/main/java/eu/eudat/model/DmpValidationResult.java new file mode 100644 index 000000000..f95126af8 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/DmpValidationResult.java @@ -0,0 +1,51 @@ +package eu.eudat.model; + +import eu.eudat.commons.enums.DmpValidationOutput; + +import java.util.List; +import java.util.UUID; + +public class DmpValidationResult { + + private UUID id; + + public static final String _id = "id"; + + private DmpValidationOutput result; + + private List errors; + + public static final String _result = "result"; + + public DmpValidationResult() { + } + + public DmpValidationResult(UUID id, DmpValidationOutput result) { + this.id = id; + this.result = result; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public DmpValidationOutput getResult() { + return result; + } + + public void setResult(DmpValidationOutput result) { + this.result = result; + } + + public List getErrors() { + return errors; + } + + public void setErrors(List errors) { + this.errors = errors; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/DmpCensor.java b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/DmpCensor.java index a58bc4078..97c4813bb 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/censorship/DmpCensor.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/censorship/DmpCensor.java @@ -36,7 +36,7 @@ public class DmpCensor extends BaseCensor { public void censor(FieldSet fields, UUID userId) { logger.debug(new DataLogEntry("censoring fields", fields)); - if (fields.isEmpty()) + if (fields == null || fields.isEmpty()) return; this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation); 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 5c8b4c39d..59000bde1 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 @@ -5,6 +5,7 @@ import eu.eudat.commons.XmlHandlingService; import eu.eudat.commons.enums.DmpAccessType; import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.types.dmpblueprint.DefinitionEntity; import eu.eudat.commons.types.dmpblueprint.SectionEntity; import eu.eudat.commons.validation.BaseValidator; import eu.eudat.data.*; @@ -188,16 +189,16 @@ public class DmpPersist { @Override protected List specifications(DmpPersist item) { - DmpEntity dmpEntity = null; DmpBlueprintEntity dmpBlueprintEntity = null; try { - dmpEntity = this.isValidGuid(item.getId()) ? this.entityManager.find(DmpEntity.class, item.getId()) : null; - if(dmpEntity != null) dmpBlueprintEntity = this.isValidGuid(item.getBlueprint()) ? this.entityManager.find(DmpBlueprintEntity.class, dmpEntity.getBlueprintId()) : null; + dmpBlueprintEntity = this.isValidGuid(item.getBlueprint()) ? this.entityManager.find(DmpBlueprintEntity.class, item.getBlueprint()) : null; } catch (InvalidApplicationException e) { throw new RuntimeException(e); } DmpBlueprintEntity finalDmpBlueprintEntity = dmpBlueprintEntity; + DefinitionEntity definition = dmpBlueprintEntity == null ? null : this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, dmpBlueprintEntity.getDefinition()); + return Arrays.asList( this.spec() .iff(() -> this.isValidGuid(item.getId())) @@ -234,7 +235,7 @@ public class DmpPersist { .iff(() -> !this.isNull(item.getProperties())) .on(DmpPersist._properties) .over(item.getProperties()) - .using(() -> this.validatorFactory.validator(DmpPropertiesPersist.DmpPropertiesPersistValidator.class).setStatus(item.getStatus())), + .using(() -> this.validatorFactory.validator(DmpPropertiesPersist.DmpPropertiesPersistValidator.class).setStatus(item.getStatus()).withDefinition(definition)), this.spec() .iff(() -> item.getStatus() == DmpStatus.Finalized) .must(() -> !this.isNull(item.getLanguage())) 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 index 70793d157..394766a3d 100644 --- 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 @@ -1,6 +1,16 @@ package eu.eudat.model.persist.dmpproperties; +import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.commons.enums.DmpBlueprintFieldCategory; +import eu.eudat.commons.types.dmpblueprint.DefinitionEntity; +import eu.eudat.commons.types.dmpblueprint.FieldEntity; +import eu.eudat.commons.types.dmpblueprint.ReferenceTypeFieldEntity; import eu.eudat.commons.validation.BaseValidator; +import eu.eudat.model.DmpDescriptionTemplate; +import eu.eudat.model.ReferenceType; +import eu.eudat.query.ReferenceTypeQuery; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.specification.Specification; import eu.eudat.convention.ConventionService; @@ -12,7 +22,6 @@ import org.springframework.context.annotation.Scope; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -55,14 +64,19 @@ public class DmpBlueprintValuePersist { public static class DmpBlueprintValuePersistValidator extends BaseValidator { private final ValidatorFactory validatorFactory; + private final QueryFactory queryFactory; private final MessageSource messageSource; public static final String ValidatorName = "DmpBlueprintValuePersistValidator"; - protected DmpBlueprintValuePersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) { + private DefinitionEntity definition; + private FieldEntity fieldEntity; + + protected DmpBlueprintValuePersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, QueryFactory queryFactory, MessageSource messageSource) { super(conventionService, errors); this.validatorFactory = validatorFactory; - this.messageSource = messageSource; + this.queryFactory = queryFactory; + this.messageSource = messageSource; } @Override @@ -72,21 +86,39 @@ public class DmpBlueprintValuePersist { @Override protected List specifications(DmpBlueprintValuePersist item) { + fieldEntity = definition != null && this.isValidGuid(item.getFieldId())? definition.getFieldById(item.getFieldId()).stream().findFirst().orElse(null) : null; + boolean required = fieldEntity != null && fieldEntity.isRequired(); + return Arrays.asList( -// this.spec() -// .must(() -> !this.isValidGuid(item.getFieldId())) -// .failOn(DmpBlueprintValuePersist._fieldId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpBlueprintValuePersist._fieldId}, LocaleContextHolder.getLocale())), -// this.spec() -// .iff(() -> this.isNull(item.getReferences()) || item.getReferences().isEmpty()) -// .must(() -> !this.isEmpty(item.getFieldValue())) -// .failOn(DmpBlueprintValuePersist._fieldValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpBlueprintValuePersist._fieldValue}, LocaleContextHolder.getLocale())), - this.navSpec() + this.spec() + .must(() -> this.isValidGuid(item.getFieldId())) + .failOn(DmpBlueprintValuePersist._fieldId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpBlueprintValuePersist._fieldId}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> fieldEntity.getCategory().equals(DmpBlueprintFieldCategory.Extra) && this.isListNullOrEmpty(item.getReferences()) && required) + .must(() -> !this.isEmpty(item.getFieldValue())) + .failOn(DmpBlueprintValuePersist._fieldValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{fieldEntity.getLabel()}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> fieldEntity.getCategory().equals(DmpBlueprintFieldCategory.ReferenceType) && this.isEmpty(item.getFieldValue()) && required) + .must(() -> !this.isListNullOrEmpty(item.getReferences())) + // TODO: Cast Exception +// .failOn(DmpBlueprintValuePersist._references).failWith(messageSource.getMessage("Validation_Required", new Object[]{!this.isEmpty(fieldEntity.getLabel()) ? fieldEntity.getLabel() : this.getReferenceTypeName(((ReferenceTypeFieldEntity) fieldEntity).getReferenceTypeId())}, LocaleContextHolder.getLocale())), + .failOn(DmpBlueprintValuePersist._references).failWith(messageSource.getMessage("Validation_Required", new Object[]{!this.isEmpty(fieldEntity.getLabel()) ? fieldEntity.getLabel() : DmpBlueprintValuePersist._references}, LocaleContextHolder.getLocale())), + this.navSpec() .iff(() -> !this.isListNullOrEmpty(item.getReferences())) .on(DmpBlueprintValuePersist._references) .over(item.getReferences()) - .using((itm) -> this.validatorFactory.validator(ReferencePersist.ReferencePersistValidator.class)) + .using((itm) -> this.validatorFactory.validator(ReferencePersist.ReferenceWithoutTypePersistValidator.class)) ); } + + public DmpBlueprintValuePersistValidator withDefinition(DefinitionEntity definition) { + this.definition = definition; + return this; + } + + private String getReferenceTypeName(UUID referenceTypeId){ + return this.queryFactory.query(ReferenceTypeQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(referenceTypeId).firstAs(new BaseFieldSet().ensure(ReferenceType._name)).getName(); + } } } 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 af466b46d..78835f8c4 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 @@ -1,6 +1,7 @@ package eu.eudat.model.persist.dmpproperties; import eu.eudat.commons.enums.DmpStatus; +import eu.eudat.commons.types.dmpblueprint.DefinitionEntity; import eu.eudat.commons.validation.BaseValidator; import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.specification.Specification; @@ -54,6 +55,7 @@ public class DmpPropertiesPersist { private final MessageSource messageSource; private DmpStatus status; + private DefinitionEntity definition; protected DmpPropertiesPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) { super(conventionService, errors); @@ -74,7 +76,7 @@ public class DmpPropertiesPersist { .on(DmpPropertiesPersist._dmpBlueprintValues) .over(item.getDmpBlueprintValues()) .mapKey((k) -> ((UUID)k).toString()) - .using((itm) -> this.validatorFactory.validator(DmpBlueprintValuePersist.DmpBlueprintValuePersistValidator.class)), + .using((itm) -> this.validatorFactory.validator(DmpBlueprintValuePersist.DmpBlueprintValuePersistValidator.class).withDefinition(definition)), this.navSpec() .iff(() -> this.status == DmpStatus.Finalized && !this.isListNullOrEmpty(item.getContacts())) .on(DmpPropertiesPersist._contacts) @@ -89,6 +91,11 @@ public class DmpPropertiesPersist { return this; } + public DmpPropertiesPersistValidator withDefinition(DefinitionEntity definition) { + this.definition = definition; + return this; + } + } } 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 54a8734fb..bcdc72300 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 @@ -2,6 +2,7 @@ package eu.eudat.service.dmp; import eu.eudat.model.Dmp; import eu.eudat.model.DmpUser; +import eu.eudat.model.DmpValidationResult; import eu.eudat.model.persist.*; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; @@ -28,6 +29,8 @@ public interface DmpService { void undoFinalize(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException; + DmpValidationResult validate(UUID id) throws InvalidApplicationException; + Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException; Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, 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 13e4b48fb..d4a549a5a 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 @@ -14,6 +14,7 @@ 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.dmpblueprint.ReferenceTypeFieldEntity; +import eu.eudat.commons.types.dmpblueprint.SectionEntity; import eu.eudat.commons.types.dmpreference.DmpReferenceDataEntity; import eu.eudat.commons.types.notification.*; import eu.eudat.commons.types.reference.DefinitionEntity; @@ -32,6 +33,7 @@ import eu.eudat.model.*; import eu.eudat.model.builder.DmpBuilder; import eu.eudat.model.builder.DmpUserBuilder; import eu.eudat.model.deleter.*; +import eu.eudat.model.dmpreference.DmpReferenceData; import eu.eudat.model.file.FileEnvelope; import eu.eudat.model.persist.*; import eu.eudat.model.persist.actionconfirmation.DmpInvitationPersist; @@ -909,6 +911,10 @@ public class DmpServiceImpl implements DmpService { throw new MyApplicationException("DMP is already finalized"); } + if (this.validate(id).getResult().equals(DescriptionValidationOutput.Invalid)){ + throw new MyApplicationException("Dmp is invalid"); + } + List descriptions = this.queryFactory.query(DescriptionQuery.class) .authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(id).isActive(IsActive.Active).collect(); @@ -973,6 +979,182 @@ public class DmpServiceImpl implements DmpService { this.sendNotification(dmp); } + public DmpValidationResult validate(UUID id) throws InvalidApplicationException { + + DmpEntity dmp = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(id).isActive(IsActive.Active).first(); + + if (dmp == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + DmpValidationResult dmpValidationResult = new DmpValidationResult(dmp.getId(), DmpValidationOutput.Invalid); + + DmpPersist.DmpPersistValidator validator = this.validatorFactory.validator(DmpPersist.DmpPersistValidator.class); + validator.validate(this.buildDmpPersist(dmp)); + if (validator.result().isValid()) dmpValidationResult.setResult(DmpValidationOutput.Valid); + else dmpValidationResult.setErrors(validator.result().getErrors().stream().map(x -> x.getErrorMessage()).collect(Collectors.toList())); + + return dmpValidationResult; + } + + // build persist + + private @NotNull DmpPersist buildDmpPersist(DmpEntity data) throws InvalidApplicationException { + DmpPersist persist = new DmpPersist(); + if (data == null) return persist; + + DmpBlueprintEntity dmpBlueprintEntity = this.entityManager.find(DmpBlueprintEntity.class, data.getBlueprintId()); + if (dmpBlueprintEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{data.getBlueprintId(), DmpBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + persist.setId(data.getId()); + persist.setHash(data.getId().toString()); + persist.setLabel(data.getLabel()); + persist.setStatus(DmpStatus.Finalized); + persist.setDescription(data.getDescription()); + persist.setBlueprint(data.getBlueprintId()); + persist.setAccessType(data.getAccessType()); + persist.setLanguage(data.getLanguage()); + + List dmpUserEntities = this.queryFactory.query(DmpUserQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(data.getId()).isActives(IsActive.Active).collect(); + + if (!this.conventionService.isListNullOrEmpty(dmpUserEntities)){ + persist.setUsers(new ArrayList<>()); + for (DmpUserEntity user: dmpUserEntities) { + persist.getUsers().add(this.buildDmpUserPersist(user)); + } + }; + + List dmpReferenceEntities = this.queryFactory.query(DmpReferenceQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(data.getId()).isActives(IsActive.Active).collect(); + + eu.eudat.commons.types.dmpblueprint.DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(eu.eudat.commons.types.dmpblueprint.DefinitionEntity.class, dmpBlueprintEntity.getDefinition()); + + List dmpDescriptionTemplateEntities = this.queryFactory.query(DmpDescriptionTemplateQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).dmpIds(data.getId()).isActive(IsActive.Active).collect(); + if (!this.conventionService.isListNullOrEmpty(dmpDescriptionTemplateEntities)){ + persist.setDescriptionTemplates(new ArrayList<>()); + for (DmpDescriptionTemplateEntity descriptionTemplateEntity: dmpDescriptionTemplateEntities) { + persist.getDescriptionTemplates().add(this.buildDmpDescriptionTemplatePersists(descriptionTemplateEntity, definition.getSections())); + } + }; + persist.setProperties(this.buildDmpPropertyDefinitionPersist( this.jsonHandlingService.fromJsonSafe(DmpPropertiesEntity.class, data.getProperties()), dmpReferenceEntities, definition.getSections())); + + return persist; + } + + private @NotNull DmpPropertiesPersist buildDmpPropertyDefinitionPersist(DmpPropertiesEntity data, List dmpReferenceEntities, List sectionEntities){ + DmpPropertiesPersist persist = new DmpPropertiesPersist(); + if (data == null) return persist; + if (!this.conventionService.isListNullOrEmpty(data.getContacts())){ + persist.setContacts(new ArrayList<>()); + for (DmpContactEntity contact: data.getContacts()) { + persist.getContacts().add(this.buildDmpContactPersist(contact)); + } + } + + List referencesFromAllFields = new ArrayList<>(); + if (!this.conventionService.isListNullOrEmpty(dmpReferenceEntities)) { + referencesFromAllFields = this.queryFactory.query(ReferenceQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpReferenceEntities.stream().map(x-> x.getReferenceId()).collect(Collectors.toList())).isActive(IsActive.Active).collect(); + } + + + if (!this.conventionService.isListNullOrEmpty(sectionEntities)){ + Map dmpBlueprintValues = new HashMap<>(); + for (SectionEntity sectionEntity: sectionEntities) { + if (!this.conventionService.isListNullOrEmpty(sectionEntity.getFields())){ + for (eu.eudat.commons.types.dmpblueprint.FieldEntity fieldEntity: sectionEntity.getFields()) { + + if (!this.conventionService.isListNullOrEmpty(dmpReferenceEntities)) { + List referencePersists = new ArrayList<>(); + for (DmpReferenceEntity dmpReferenceEntity : dmpReferenceEntities) { + DmpReferenceData referenceData = this.jsonHandlingService.fromJsonSafe(DmpReferenceData.class, dmpReferenceEntity.getData()); + + ReferenceEntity reference = referencesFromAllFields.stream().filter(x -> x.getId().equals(dmpReferenceEntity.getReferenceId())).collect(Collectors.toList()).getFirst(); + if (referenceData.getBlueprintFieldId().equals(fieldEntity.getId()) && reference != null) { + referencePersists.add(this.buildReferencePersist(reference)); + // found reference + dmpBlueprintValues.put(fieldEntity.getId(), this.buildDmpBlueprintValuePersist(fieldEntity.getId(), null, referencePersists)); + } + } + } + if (!this.conventionService.isListNullOrEmpty(data.getDmpBlueprintValues())) { + for (DmpBlueprintValueEntity value : data.getDmpBlueprintValues()) { + if (value.getFieldId().equals(fieldEntity.getId())) { + // found value + dmpBlueprintValues.put(fieldEntity.getId(), this.buildDmpBlueprintValuePersist(fieldEntity.getId(), value.getValue(), null)); + } + } + } else { + dmpBlueprintValues.put(fieldEntity.getId(), this.buildDmpBlueprintValuePersist(fieldEntity.getId(), null, null)); + } + } + } + + } + persist.setDmpBlueprintValues(dmpBlueprintValues); + } + + return persist; + } + + private @NotNull DmpContactPersist buildDmpContactPersist(DmpContactEntity data){ + DmpContactPersist persist = new DmpContactPersist(); + if (data == null) return persist; + + persist.setEmail(data.getEmail()); + persist.setLastName(data.getLastName()); + persist.setFirstName(data.getFirstName()); + persist.setUserId(data.getUserId()); + return persist; + } + + private @NotNull DmpUserPersist buildDmpUserPersist(DmpUserEntity data){ + DmpUserPersist persist = new DmpUserPersist(); + if (data == null) return persist; + + persist.setUser(data.getUserId()); + persist.setSectionId(data.getSectionId()); + persist.setRole(data.getRole()); + + return persist; + } + + private @NotNull DmpDescriptionTemplatePersist buildDmpDescriptionTemplatePersists(DmpDescriptionTemplateEntity data, List sectionEntities){ + DmpDescriptionTemplatePersist persist = new DmpDescriptionTemplatePersist(); + if (data == null) return persist; + + persist.setSectionId(data.getSectionId()); + persist.setDescriptionTemplateGroupId(data.getDescriptionTemplateGroupId()); + + return persist; + } + + private @NotNull ReferencePersist buildReferencePersist(ReferenceEntity data){ + ReferencePersist persist = new ReferencePersist(); + if (data == null) return persist; + + persist.setLabel(data.getLabel()); + persist.setReference(data.getReference()); + persist.setAbbreviation(data.getAbbreviation()); + persist.setDescription(data.getDescription()); + persist.setSource(data.getSource()); + persist.setSourceType(data.getSourceType()); + persist.setReference(data.getReference()); + persist.setTypeId(data.getTypeId()); + + return persist; + } + + private @NotNull DmpBlueprintValuePersist buildDmpBlueprintValuePersist(UUID fieldId, String fieldValue, List referencePersists){ + DmpBlueprintValuePersist persist = new DmpBlueprintValuePersist(); + + persist.setFieldId(fieldId); + + if (!this.conventionService.isListNullOrEmpty(referencePersists)){ + persist.setReferences(referencePersists); + }else if (fieldValue != null){ + persist.setFieldValue(fieldValue); + } + + return persist; + } + // invites public void inviteUserOrAssignUsers(UUID id, List users) throws InvalidApplicationException, JAXBException, IOException { this.inviteUserOrAssignUsers(id, users, true); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java index d302fbdc5..1e64954d4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DmpController.java @@ -202,6 +202,21 @@ public class DmpController { return true; } + @GetMapping("validate/{id}") + public DmpValidationResult validate(@PathVariable("id") UUID id) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException { + logger.debug(new MapLogEntry("validating" + Dmp.class.getSimpleName()).And("id", id)); + + this.censorFactory.censor(DmpCensor.class).censor(null, null); + + DmpValidationResult result = this.dmpService.validate(id); + + this.auditService.track(AuditableAction.Dmp_Validate, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id) + )); + + return result; + } + @PostMapping("clone") @Transactional @ValidationFilterAnnotation(validator = CloneDmpPersist.CloneDmpPersistValidator.ValidatorName, argumentName = "model") diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 7fa2ef6e7..0f6a3c315 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -19,6 +19,7 @@ import { CloneDmpPersist, Dmp, DmpPersist, DmpUser, DmpUserInvitePersist, DmpUse import { AuthService } from '../auth/auth.service'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; +import { DmpValidationResult } from '@app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component'; @Injectable() export class DmpService { @@ -110,6 +111,14 @@ export class DmpService { catchError((error: any) => throwError(error))); } + validate(id: Guid): Observable { + const url = `${this.apiBase}/validate/${id}`; + + return this.http + .get(url).pipe( + catchError((error: any) => throwError(error))); + } + clone(item: CloneDmpPersist, reqFields: string[] = []): Observable { const url = `${this.apiBase}/clone`; const options = { params: { f: reqFields } }; diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts index 1ad6f8c69..857f4a784 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts @@ -263,11 +263,10 @@ export class DmpEditorComponent extends BaseEditor implemen formSubmit(): void { this.formService.removeAllBackEndErrors(this.formGroup); this.formService.touchAllFormFields(this.formGroup); - if (!this.isFormValid()) { - return; + if (this.formGroup.get('label').valid && this.formGroup.get('blueprint').valid && this.formGroup.get('status').valid + && this.formGroup.get('descriptionTemplates').valid) { + this.persistEntity(); } - - this.persistEntity(); } public delete() { diff --git a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html index 8e3107aa5..417d18a86 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html +++ b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.html @@ -19,7 +19,7 @@ {{ dmp.description }} - + {{ 'DMP-FINALISE-DIALOG.DESCRPIPTIONS' | translate }} @@ -37,7 +37,7 @@ : {{ description.label }} - +

{{ description.label }}

@@ -56,7 +56,15 @@ - + + + {{ 'DMP-FINALISE-DIALOG.VALIDATION.INVALID-DMP' | translate }} + + + {{ dmpErrors }} + + + {{'DMP-FINALISE-DIALOG.VALIDATION.AT-LEAST-ONE-DESCRPIPTION-FINALISED' | translate}} diff --git a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts index eb4469a25..f5c3c37b9 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component.ts @@ -5,6 +5,7 @@ import { DescriptionStatus } from '@app/core/common/enum/description-status'; import { DmpAccessType } from '@app/core/common/enum/dmp-access-type'; import { Dmp } from '@app/core/model/dmp/dmp'; import { DescriptionService } from '@app/core/services/description/description.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; import { BaseComponent } from '@common/base/base.component'; import { Guid } from '@common/types/guid'; import { takeUntil } from 'rxjs/operators'; @@ -18,16 +19,20 @@ import { takeUntil } from 'rxjs/operators'; export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit { dmp: Dmp; + isDmpValid: boolean; + dmpErrors: string[]; dmpAccessTypeEnum = DmpAccessType; descriptionStatusEnum = DescriptionStatus; descriptionValidationOutputEnum = DescriptionValidationOutput; validationResults: DescriptionValidationResult[] = []; descriptionsToBeFinalized: Guid[] = []; + descriptionValidationOutputMap = new Map(); constructor( public router: Router, public dialogRef: MatDialogRef, public descriptionService: DescriptionService, + private dmpService: DmpService, @Inject(MAT_DIALOG_DATA) public data: any ) { super(); @@ -35,11 +40,26 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit } ngOnInit(): void { - this.validateDescriptions(this.dmp); + this.dmpService.validate(this.dmp.id).pipe(takeUntil(this._destroyed), + ).subscribe(result => { + if (result.result === DmpValidationOutput.Valid){ + this.validateDescriptions(this.dmp); + this.isDmpValid = true; + }else{ + this.isDmpValid = false; + this.dmpErrors = result.errors; + } + }); } isDescriptionValid(descriptionId: Guid): boolean { - return this.validationResults.find(x => x.descriptionId == descriptionId)?.result === DescriptionValidationOutput.Valid; + const result = this.validationResults.find(x => x.descriptionId == descriptionId)?.result === DescriptionValidationOutput.Valid; + if (result){ + this.descriptionValidationOutputMap.set(descriptionId, DescriptionValidationOutput.Valid); + } else{ + this.descriptionValidationOutputMap.set(descriptionId, DescriptionValidationOutput.Invalid); + } + return result; } onSubmit() { @@ -48,7 +68,13 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit getFinalizedDescriptions() { if (!this.dmp.descriptions) return []; - return this.dmp.descriptions.filter(x => x.status === DescriptionStatus.Finalized); + const finalizedDescriptions = this.dmp.descriptions.filter(x => x.status === DescriptionStatus.Finalized); + if (finalizedDescriptions?.length > 0){ + finalizedDescriptions.forEach(finalize => { + this.descriptionValidationOutputMap.set(finalize.id, DescriptionValidationOutput.Valid); + }) + } + return finalizedDescriptions; } close() { @@ -57,7 +83,14 @@ export class DmpFinalizeDialogComponent extends BaseComponent implements OnInit validateDescriptions(dmp: Dmp) { if (!dmp.descriptions?.some(x => x.status == DescriptionStatus.Draft)) return; - + + const draftDescriptions = this.dmp.descriptions.filter(x => x.status == DescriptionStatus.Draft) || []; + if ( draftDescriptions.length > 0){ + draftDescriptions.forEach(draft => { + this.descriptionValidationOutputMap.set(draft.id, DescriptionValidationOutput.Pending); + }); + } + this.descriptionService.validate(dmp.descriptions.filter(x => x.status == DescriptionStatus.Draft).map(x => x.id)).pipe(takeUntil(this._destroyed), ).subscribe(result => { this.validationResults = result; @@ -76,10 +109,23 @@ export interface DescriptionValidationResult { } export enum DescriptionValidationOutput { + Valid = 1, + Invalid = 2, + Pending = 3 +} + +export interface DmpValidationResult { + id: Guid; + result: DmpValidationOutput; + errors: string[]; +} + +export enum DmpValidationOutput { Valid = 1, Invalid = 2 } + export interface DmpFinalizeDialogOutput { cancelled?: boolean; descriptionsToBeFinalized?: Guid[]; diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index ede07057d..2afdc4218 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1915,7 +1915,8 @@ "EMPTY": "No Descriptions for this Plan so far", "FINALISE-TITLE": "Do you want to finalize any of the following Draft Descriptions?", "VALIDATION": { - "AT-LEAST-ONE-DESCRPIPTION-FINALISED": "You need to have at least one Description Finalized" + "AT-LEAST-ONE-DESCRPIPTION-FINALISED": "You need to have at least one Description Finalized", + "INVALID-DMP": "This Plan can not be finalized " }, "IMPACT": "This action will finalize your Plan, and you won't be able to edit it again.", "PUBLIC-DMP-MESSAGE": "After finalizing your Plan, it'll be published and be publicly available to the {{ APP_NAME_CAPS }} tool.",