From e4c1efe98c0bdcd821ef880262c5775844531a17 Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Fri, 2 Feb 2024 14:59:11 +0200 Subject: [PATCH] Added versioning fields on DmpBlueprintEntity and stack --- .../java/eu/eudat/audit/AuditableAction.java | 6 +- .../eu/eudat/authorization/Permission.java | 1 + .../DescriptionTemplateVersionStatus.java | 4 +- .../enums/DmpBlueprintVersionStatus.java | 32 ++++ .../eu/eudat/data/DmpBlueprintEntity.java | 69 ++++++-- .../DmpBlueprintVersionStatusConverter.java | 15 ++ .../errorcode/ErrorThesaurusProperties.java | 10 ++ .../java/eu/eudat/model/DmpBlueprint.java | 103 ++++++++---- .../builder/DescriptionTemplateBuilder.java | 55 ++++--- .../model/builder/DmpBlueprintBuilder.java | 33 ++-- .../NewVersionDmpBlueprintPersist.java | 132 ++++++++++++++++ .../eu/eudat/query/DmpBlueprintQuery.java | 147 ++++++++++++++---- .../DescriptionTemplateServiceImpl.java | 78 ++++++---- .../dmpblueprint/DmpBlueprintService.java | 3 + .../dmpblueprint/DmpBlueprintServiceImpl.java | 110 ++++++++++++- .../v2/DmpBlueprintController.java | 16 ++ .../web/src/main/resources/config/errors.yml | 3 + .../src/main/resources/config/permissions.yml | 6 + 18 files changed, 693 insertions(+), 130 deletions(-) create mode 100644 dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpBlueprintVersionStatus.java create mode 100644 dmp-backend/core/src/main/java/eu/eudat/data/converters/DmpBlueprintVersionStatusConverter.java create mode 100644 dmp-backend/core/src/main/java/eu/eudat/model/persist/NewVersionDmpBlueprintPersist.java 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 8297c8d42..f4e9e1545 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 @@ -19,8 +19,10 @@ public class AuditableAction { public static final EventId DmpBlueprint_Persist = new EventId(3002, "DmpBlueprint_Persist"); public static final EventId DmpBlueprint_Delete = new EventId(3003, "DmpBlueprint_Delete"); public static final EventId DmpBlueprint_Clone = new EventId(3004, "DmpBlueprint_Clone"); - public static final EventId DmpBlueprint_GetXml = new EventId(3005, "DmpBlueprint_GetXml"); - public static final EventId DmpBlueprint_Import = new EventId(3006, "DmpBlueprint_Import"); + + public static final EventId DmpBlueprint_PersistNewVersion = new EventId(3005, "DmpBlueprint_PersistNewVersion"); + public static final EventId DmpBlueprint_GetXml = new EventId(3006, "DmpBlueprint_GetXml"); + public static final EventId DmpBlueprint_Import = new EventId(3007, "DmpBlueprint_Import"); public static final EventId User_Settings_Query = new EventId(4000, "User_Settings_Query"); public static final EventId User_Settings_Lookup = new EventId(4001, "User_Settings_Lookup"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java index 869f36ef1..754abd244 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java +++ b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java @@ -85,6 +85,7 @@ public final class Permission { public static String EditDmpBlueprint = "EditDmpBlueprint"; public static String DeleteDmpBlueprint = "DeleteDmpBlueprint"; public static String CloneDmpBlueprint = "CloneDmpBlueprint"; + public static String CreateNewVersionDmpBlueprint = "CreateNewVersionDmpBlueprint"; public static String ExportDmpBlueprint = "ExportDmpBlueprint"; public static String ImportDmpBlueprint = "ImportDmpBlueprint"; diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionTemplateVersionStatus.java b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionTemplateVersionStatus.java index 4deed73e9..0922c9cad 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionTemplateVersionStatus.java +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DescriptionTemplateVersionStatus.java @@ -7,7 +7,9 @@ import java.util.Map; public enum DescriptionTemplateVersionStatus implements DatabaseEnum { - Current((short) 0), Previous ((short) 1), NotFinalized ((short) 2); + Current((short) 0), + Previous((short) 1), + NotFinalized((short) 2); private final Short value; diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpBlueprintVersionStatus.java b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpBlueprintVersionStatus.java new file mode 100644 index 000000000..6189868c2 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/enums/DmpBlueprintVersionStatus.java @@ -0,0 +1,32 @@ +package eu.eudat.commons.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import eu.eudat.data.converters.enums.DatabaseEnum; + +import java.util.Map; + +public enum DmpBlueprintVersionStatus implements DatabaseEnum { + + Current((short) 0), + Previous((short) 1), + NotFinalized((short) 2); + + private final Short value; + + DmpBlueprintVersionStatus(Short value) { + this.value = value; + } + + @Override + @JsonValue + public Short getValue() { + return value; + } + + private static final Map map = EnumUtils.getEnumValueMap(DmpBlueprintVersionStatus.class); + + public static DmpBlueprintVersionStatus of(Short i) { + return map.get(i); + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/DmpBlueprintEntity.java b/dmp-backend/core/src/main/java/eu/eudat/data/DmpBlueprintEntity.java index e205dfea8..e31fb9bb2 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/data/DmpBlueprintEntity.java +++ b/dmp-backend/core/src/main/java/eu/eudat/data/DmpBlueprintEntity.java @@ -1,7 +1,9 @@ package eu.eudat.data; import eu.eudat.commons.enums.DmpBlueprintStatus; +import eu.eudat.commons.enums.DmpBlueprintVersionStatus; import eu.eudat.commons.enums.IsActive; +import eu.eudat.data.converters.DmpBlueprintVersionStatusConverter; import eu.eudat.data.converters.enums.DmpBlueprintStatusConverter; import eu.eudat.data.converters.enums.IsActiveConverter; import eu.eudat.data.tenant.TenantScopedBaseEntity; @@ -15,41 +17,64 @@ import java.util.UUID; @Entity @Table(name = "\"DmpBlueprint\"") public class DmpBlueprintEntity extends TenantScopedBaseEntity { + @Id @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) private UUID id; + public static final String _id = "id"; @Column(name = "label", length = DmpBlueprintEntity._labelLength, nullable = false) private String label; + public static final String _label = "label"; + public static final int _labelLength = 250; @Type(value = SQLXMLType.class) - @Column(name = "definition", nullable = true, columnDefinition = "xml") + @Column(name = "definition", columnDefinition = "xml") private String definition; - public static final String _definition = "definition"; + public static final String _definition = "definition"; @Column(name = "status", nullable = false) @Convert(converter = DmpBlueprintStatusConverter.class) private DmpBlueprintStatus status; + public static final String _status = "status"; - @Column(name = "is_active", nullable = false) - @Convert(converter = IsActiveConverter.class) - private IsActive isActive; - public static final String _isActive = "isActive"; + @Column(name = "group_id", columnDefinition = "uuid", nullable = false) + private UUID groupId; + public static final String _groupId = "groupId"; + + @Column(name = "version", nullable = false) + private Short version; + + public static final String _version = "version"; + + @Column(name = "version_status", nullable = false) + @Convert(converter = DmpBlueprintVersionStatusConverter.class) + private DmpBlueprintVersionStatus versionStatus; + + public static final String _versionStatus = "versionStatus"; @Column(name = "created_at", nullable = false) private Instant createdAt = null; + public static final String _createdAt = "createdAt"; @Column(name = "updated_at", nullable = false) private Instant updatedAt; + public static final String _updatedAt = "updatedAt"; + @Column(name = "is_active", nullable = false) + @Convert(converter = IsActiveConverter.class) + private IsActive isActive; + + public static final String _isActive = "isActive"; + public UUID getId() { return id; } @@ -82,12 +107,28 @@ public class DmpBlueprintEntity extends TenantScopedBaseEntity { this.status = status; } - public IsActive getIsActive() { - return isActive; + public UUID getGroupId() { + return groupId; } - public void setIsActive(IsActive isActive) { - this.isActive = isActive; + public void setGroupId(UUID groupId) { + this.groupId = groupId; + } + + public Short getVersion() { + return version; + } + + public void setVersion(Short version) { + this.version = version; + } + + public DmpBlueprintVersionStatus getVersionStatus() { + return versionStatus; + } + + public void setVersionStatus(DmpBlueprintVersionStatus versionStatus) { + this.versionStatus = versionStatus; } public Instant getCreatedAt() { @@ -105,4 +146,12 @@ public class DmpBlueprintEntity extends TenantScopedBaseEntity { public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; } + + public IsActive getIsActive() { + return isActive; + } + + public void setIsActive(IsActive isActive) { + this.isActive = isActive; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/converters/DmpBlueprintVersionStatusConverter.java b/dmp-backend/core/src/main/java/eu/eudat/data/converters/DmpBlueprintVersionStatusConverter.java new file mode 100644 index 000000000..d737d7db8 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/data/converters/DmpBlueprintVersionStatusConverter.java @@ -0,0 +1,15 @@ +package eu.eudat.data.converters; + +import eu.eudat.commons.enums.DmpBlueprintVersionStatus; +import eu.eudat.data.converters.enums.DatabaseEnumConverter; +import jakarta.persistence.Converter; + +@Converter +public class DmpBlueprintVersionStatusConverter extends DatabaseEnumConverter { + + @Override + protected DmpBlueprintVersionStatus of(Short i) { + return DmpBlueprintVersionStatus.of(i); + } + +} 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 b4d1135e2..ef748e124 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 @@ -65,6 +65,16 @@ public class ErrorThesaurusProperties { this.dmpNewVersionConflict = dmpNewVersionConflict; } + public ErrorDescription dmpBlueprintNewVersionConflict; + + public ErrorDescription getDmpBlueprintNewVersionConflict() { + return dmpBlueprintNewVersionConflict; + } + + public void setDmpBlueprintNewVersionConflict(ErrorDescription dmpBlueprintNewVersionConflict) { + this.dmpBlueprintNewVersionConflict = dmpBlueprintNewVersionConflict; + } + private ErrorDescription dmpIsFinalized; public ErrorDescription getDmpIsFinalized() { diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/DmpBlueprint.java b/dmp-backend/core/src/main/java/eu/eudat/model/DmpBlueprint.java index 89f67ad50..aa81745df 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/DmpBlueprint.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/DmpBlueprint.java @@ -1,39 +1,59 @@ package eu.eudat.model; import eu.eudat.commons.enums.DmpBlueprintStatus; +import eu.eudat.commons.enums.DmpBlueprintVersionStatus; import eu.eudat.commons.enums.IsActive; import eu.eudat.model.dmpblueprintdefinition.Definition; import java.time.Instant; -import java.util.List; import java.util.UUID; public class DmpBlueprint { - public final static String _id = "id"; private UUID id; - public final static String _label= "label"; + public static final String _id = "id"; + private String label; - public final static String _createdAt = "createdAt"; + public static final String _label = "label"; + private Definition definition; - public final static String _definition = "definition"; - private Instant createdAt; + public static final String _definition = "definition"; - public final static String _updatedAt = "updatedAt"; - private Instant updatedAt; - - public final static String _isActive = "isActive"; - private IsActive isActive; - - public final static String _status = "status"; private DmpBlueprintStatus status; - public final static String _hash = "hash"; + public static final String _status = "status"; + + private UUID groupId; + + public static final String _groupId = "groupId"; + + private Short version; + + public static final String _version = "version"; + + private DmpBlueprintVersionStatus versionStatus; + + public static final String _versionStatus = "versionStatus"; + + private Instant createdAt; + + public static final String _createdAt = "createdAt"; + + private Instant updatedAt; + + public static final String _updatedAt = "updatedAt"; + + private IsActive isActive; + + public static final String _isActive = "isActive"; + private String hash; + public static final String _hash = "hash"; + public UUID getId() { return id; } @@ -50,6 +70,46 @@ public class DmpBlueprint { this.label = label; } + public Definition getDefinition() { + return definition; + } + + public void setDefinition(Definition definition) { + this.definition = definition; + } + + public DmpBlueprintStatus getStatus() { + return status; + } + + public void setStatus(DmpBlueprintStatus status) { + this.status = status; + } + + public UUID getGroupId() { + return groupId; + } + + public void setGroupId(UUID groupId) { + this.groupId = groupId; + } + + public Short getVersion() { + return version; + } + + public void setVersion(Short version) { + this.version = version; + } + + public DmpBlueprintVersionStatus getVersionStatus() { + return versionStatus; + } + + public void setVersionStatus(DmpBlueprintVersionStatus versionStatus) { + this.versionStatus = versionStatus; + } + public Instant getCreatedAt() { return createdAt; } @@ -74,14 +134,6 @@ public class DmpBlueprint { this.isActive = isActive; } - public DmpBlueprintStatus getStatus() { - return status; - } - - public void setStatus(DmpBlueprintStatus status) { - this.status = status; - } - public String getHash() { return hash; } @@ -90,13 +142,6 @@ public class DmpBlueprint { this.hash = hash; } - public Definition getDefinition() { - return definition; - } - - public void setDefinition(Definition definition) { - this.definition = definition; - } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DescriptionTemplateBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DescriptionTemplateBuilder.java index 6b3dc377b..fa6e79155 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DescriptionTemplateBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DescriptionTemplateBuilder.java @@ -32,10 +32,13 @@ import java.util.stream.Collectors; public class DescriptionTemplateBuilder extends BaseBuilder { private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + private final QueryFactory queryFactory; + private final BuilderFactory builderFactory; + private final XmlHandlingService xmlHandlingService; - + @Autowired public DescriptionTemplateBuilder( ConventionService conventionService, QueryFactory queryFactory, BuilderFactory builderFactory, XmlHandlingService xmlHandlingService) { @@ -67,24 +70,38 @@ public class DescriptionTemplateBuilder extends BaseBuilder models = new ArrayList<>(); for (DescriptionTemplateEntity d : data) { DescriptionTemplate m = new DescriptionTemplate(); - if (fields.hasField(this.asIndexer(DescriptionTemplate._id))) m.setId(d.getId()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._label))) m.setLabel(d.getLabel()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._description))) m.setDescription(d.getDescription()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._groupId))) m.setGroupId(d.getGroupId()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._version))) m.setVersion(d.getVersion()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._language))) m.setLanguage(d.getLanguage()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._createdAt))) m.setCreatedAt(d.getCreatedAt()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._isActive))) m.setIsActive(d.getIsActive()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._versionStatus))) m.setVersionStatus(d.getVersionStatus()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._status))) m.setStatus(d.getStatus()); - if (fields.hasField(this.asIndexer(DescriptionTemplate._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); - if (!definitionFields.isEmpty() && d.getDefinition() != null){ + if (fields.hasField(this.asIndexer(DescriptionTemplate._id))) + m.setId(d.getId()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._label))) + m.setLabel(d.getLabel()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._description))) + m.setDescription(d.getDescription()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._groupId))) + m.setGroupId(d.getGroupId()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._version))) + m.setVersion(d.getVersion()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._language))) + m.setLanguage(d.getLanguage()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._createdAt))) + m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._updatedAt))) + m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._isActive))) + m.setIsActive(d.getIsActive()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._versionStatus))) + m.setVersionStatus(d.getVersionStatus()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._status))) + m.setStatus(d.getStatus()); + if (fields.hasField(this.asIndexer(DescriptionTemplate._hash))) + m.setHash(this.hashValue(d.getUpdatedAt())); + if (!definitionFields.isEmpty() && d.getDefinition() != null) { DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, d.getDefinition()); m.setDefinition(this.builderFactory.builder(DefinitionBuilder.class).authorize(this.authorize).build(definitionFields, definition)); } - if (!usersFields.isEmpty() && usersMap != null && usersMap.containsKey(d.getId())) m.setUsers(usersMap.get(d.getId())); - if (!descriptionTemplateTypeFields.isEmpty() && descriptionTemplateTypeMap != null && descriptionTemplateTypeMap.containsKey(d.getTypeId())) m.setType(descriptionTemplateTypeMap.get(d.getTypeId())); + if (!usersFields.isEmpty() && usersMap != null && usersMap.containsKey(d.getId())) + m.setUsers(usersMap.get(d.getId())); + if (!descriptionTemplateTypeFields.isEmpty() && descriptionTemplateTypeMap != null && descriptionTemplateTypeMap.containsKey(d.getTypeId())) + m.setType(descriptionTemplateTypeMap.get(d.getTypeId())); models.add(m); } this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); @@ -92,7 +109,8 @@ public class DescriptionTemplateBuilder extends BaseBuilder collectDescriptionTemplateTypes(FieldSet fields, List data) throws MyApplicationException { - if (fields.isEmpty() || data.isEmpty()) return null; + if (fields.isEmpty() || data.isEmpty()) + return null; this.logger.debug("checking related - {}", DescriptionTemplateType.class.getSimpleName()); Map itemMap = null; @@ -121,7 +139,8 @@ public class DescriptionTemplateBuilder extends BaseBuilder> collectUserDescriptionTemplates(FieldSet fields, List data) throws MyApplicationException { - if (fields.isEmpty() || data.isEmpty()) return null; + if (fields.isEmpty() || data.isEmpty()) + return null; this.logger.debug("checking related - {}", UserDescriptionTemplate.class.getSimpleName()); Map> itemMap = null; diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBlueprintBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBlueprintBuilder.java index 8e2ad34e3..fd602e18b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBlueprintBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBlueprintBuilder.java @@ -25,7 +25,9 @@ import java.util.*; public class DmpBlueprintBuilder extends BaseBuilder { private final BuilderFactory builderFactory; + private final XmlHandlingService xmlHandlingService; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); @Autowired @@ -48,19 +50,32 @@ public class DmpBlueprintBuilder extends BaseBuilder(); - + FieldSet definitionFields = fields.extractPrefixed(this.asPrefix(DmpBlueprint._definition)); List models = new ArrayList<>(); for (DmpBlueprintEntity d : data) { DmpBlueprint m = new DmpBlueprint(); - if (fields.hasField(this.asIndexer(DmpBlueprint._id))) m.setId(d.getId()); - if (fields.hasField(this.asIndexer(DmpBlueprint._label))) m.setLabel(d.getLabel()); - if (fields.hasField(this.asIndexer(DmpBlueprint._createdAt))) m.setCreatedAt(d.getCreatedAt()); - if (fields.hasField(this.asIndexer(DmpBlueprint._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); - if (fields.hasField(this.asIndexer(DmpBlueprint._isActive))) m.setIsActive(d.getIsActive()); - if (fields.hasField(this.asIndexer(DmpBlueprint._status))) m.setStatus(d.getStatus()); - if (fields.hasField(this.asIndexer(DmpBlueprint._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); - if (!definitionFields.isEmpty() && d.getDefinition() != null){ + if (fields.hasField(this.asIndexer(DmpBlueprint._id))) + m.setId(d.getId()); + if (fields.hasField(this.asIndexer(DmpBlueprint._label))) + m.setLabel(d.getLabel()); + if (fields.hasField(this.asIndexer(DmpBlueprint._status))) + m.setStatus(d.getStatus()); + if (fields.hasField(this.asIndexer(DmpBlueprint._groupId))) + m.setGroupId(d.getGroupId()); + if (fields.hasField(this.asIndexer(DmpBlueprint._version))) + m.setVersion(d.getVersion()); + if (fields.hasField(this.asIndexer(DmpBlueprint._versionStatus))) + m.setVersionStatus(d.getVersionStatus()); + if (fields.hasField(this.asIndexer(DmpBlueprint._createdAt))) + m.setCreatedAt(d.getCreatedAt()); + if (fields.hasField(this.asIndexer(DmpBlueprint._updatedAt))) + m.setUpdatedAt(d.getUpdatedAt()); + if (fields.hasField(this.asIndexer(DmpBlueprint._isActive))) + m.setIsActive(d.getIsActive()); + if (fields.hasField(this.asIndexer(DmpBlueprint._hash))) + m.setHash(this.hashValue(d.getUpdatedAt())); + if (!definitionFields.isEmpty() && d.getDefinition() != null) { DefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DefinitionEntity.class, d.getDefinition()); m.setDefinition(this.builderFactory.builder(DefinitionBuilder.class).authorize(this.authorize).build(definitionFields, definition)); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/NewVersionDmpBlueprintPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/NewVersionDmpBlueprintPersist.java new file mode 100644 index 000000000..f33dad0ab --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/NewVersionDmpBlueprintPersist.java @@ -0,0 +1,132 @@ +package eu.eudat.model.persist; + +import eu.eudat.commons.enums.DmpBlueprintStatus; +import eu.eudat.commons.validation.BaseValidator; +import eu.eudat.convention.ConventionService; +import eu.eudat.data.DmpBlueprintEntity; +import eu.eudat.errorcode.ErrorThesaurusProperties; +import eu.eudat.model.persist.dmpblueprintdefinition.DefinitionPersist; +import gr.cite.tools.validation.ValidatorFactory; +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 NewVersionDmpBlueprintPersist { + + private UUID id = null; + + private String label = null; + + public static final String _label = "label"; + + private DefinitionPersist definition = null; + + public static final String _definition = "definition"; + + private DmpBlueprintStatus status; + + public static final String _status = "status"; + + private String hash; + + public static final String _hash = "hash"; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public DefinitionPersist getDefinition() { + return definition; + } + + public void setDefinition(DefinitionPersist definition) { + this.definition = definition; + } + + public DmpBlueprintStatus getStatus() { + return status; + } + + public void setStatus(DmpBlueprintStatus status) { + this.status = status; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + @Component(NewVersionDmpBlueprintPersistValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class NewVersionDmpBlueprintPersistValidator extends BaseValidator { + + public static final String ValidatorName = "NewVersionDmpBlueprintPersistValidator"; + + private final MessageSource messageSource; + + private final ValidatorFactory validatorFactory; + + public NewVersionDmpBlueprintPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { + super(conventionService, errors); + this.messageSource = messageSource; + this.validatorFactory = validatorFactory; + } + + @Override + protected Class modelClass() { + return NewVersionDmpBlueprintPersist.class; + } + + @Override + protected List specifications(NewVersionDmpBlueprintPersist item) { + return Arrays.asList( + this.spec() + .iff(() -> this.isValidGuid(item.getId())) + .must(() -> this.isValidHash(item.getHash())) + .failOn(NewVersionDmpBlueprintPersist._hash).failWith(messageSource.getMessage("Validation_Required", new Object[]{NewVersionDmpBlueprintPersist._hash}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isEmpty(item.getLabel())) + .failOn(NewVersionDmpBlueprintPersist._label).failWith(messageSource.getMessage("Validation_Required", new Object[]{NewVersionDmpBlueprintPersist._label}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getLabel())) + .must(() -> this.lessEqualLength(item.getLabel(), DmpBlueprintEntity._labelLength)) + .failOn(NewVersionDmpBlueprintPersist._label).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{NewVersionDmpBlueprintPersist._label}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getStatus())) + .failOn(NewVersionDmpBlueprintPersist._status).failWith(messageSource.getMessage("Validation_Required", new Object[]{NewVersionDmpBlueprintPersist._status}, LocaleContextHolder.getLocale())), + + this.spec() + .must(() -> !this.isNull(item.getDefinition())) + .failOn(NewVersionDmpBlueprintPersist._definition).failWith(messageSource.getMessage("Validation_Required", new Object[]{NewVersionDmpBlueprintPersist._definition}, LocaleContextHolder.getLocale())), + this.refSpec() + .iff(() -> !this.isNull(item.getDefinition())) + .on(NewVersionDmpBlueprintPersist._definition) + .over(item.getDefinition()) + .using(() -> this.validatorFactory.validator(DefinitionPersist.DefinitionPersistValidator.class)) + ); + } + } + +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DmpBlueprintQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DmpBlueprintQuery.java index 1a5205a7b..1fe12498d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DmpBlueprintQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DmpBlueprintQuery.java @@ -2,11 +2,10 @@ package eu.eudat.query; import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.commons.enums.DmpBlueprintStatus; +import eu.eudat.commons.enums.DmpBlueprintVersionStatus; import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.scope.user.UserScope; -import eu.eudat.data.DescriptionTemplateEntity; import eu.eudat.data.DmpBlueprintEntity; -import eu.eudat.model.DescriptionTemplateType; import eu.eudat.model.DmpBlueprint; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.query.FieldResolver; @@ -30,11 +29,17 @@ public class DmpBlueprintQuery extends QueryBase { private Collection ids; + private Collection excludedIds; + private Collection isActives; private Collection statuses; - private Collection excludedIds; + private Collection groupIds; + + private Collection versions; + + private Collection versionStatuses; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); @@ -58,6 +63,21 @@ public class DmpBlueprintQuery extends QueryBase { return this; } + public DmpBlueprintQuery excludedIds(Collection values) { + this.excludedIds = values; + return this; + } + + public DmpBlueprintQuery excludedIds(UUID value) { + this.excludedIds = List.of(value); + return this; + } + + public DmpBlueprintQuery excludedIds(UUID... value) { + this.excludedIds = Arrays.asList(value); + return this; + } + public DmpBlueprintQuery isActive(IsActive value) { this.isActives = List.of(value); return this; @@ -88,18 +108,48 @@ public class DmpBlueprintQuery extends QueryBase { return this; } - public DmpBlueprintQuery excludedIds(Collection values) { - this.excludedIds = values; + public DmpBlueprintQuery groupIds(UUID value) { + this.groupIds = List.of(value); return this; } - public DmpBlueprintQuery excludedIds(UUID value) { - this.excludedIds = List.of(value); + public DmpBlueprintQuery groupIds(UUID... value) { + this.groupIds = Arrays.asList(value); return this; } - public DmpBlueprintQuery excludedIds(UUID... value) { - this.excludedIds = Arrays.asList(value); + public DmpBlueprintQuery groupIds(Collection values) { + this.groupIds = values; + return this; + } + + public DmpBlueprintQuery versions(Short value) { + this.versions = List.of(value); + return this; + } + + public DmpBlueprintQuery versions(Short... value) { + this.versions = Arrays.asList(value); + return this; + } + + public DmpBlueprintQuery versions(Collection values) { + this.versions = values; + return this; + } + + public DmpBlueprintQuery versionStatuses(DmpBlueprintVersionStatus value) { + this.versionStatuses = List.of(value); + return this; + } + + public DmpBlueprintQuery versionStatuses(DmpBlueprintVersionStatus... value) { + this.versionStatuses = Arrays.asList(value); + return this; + } + + public DmpBlueprintQuery versionStatuses(Collection values) { + this.versionStatuses = values; return this; } @@ -127,15 +177,25 @@ public class DmpBlueprintQuery extends QueryBase { @Override protected Predicate applyFilters(QueryContext queryContext) { List predicates = new ArrayList<>(); + if (this.ids != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._id)); for (UUID item : this.ids) inClause.value(item); predicates.add(inClause); } + + if (this.excludedIds != null) { + CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._id)); + for (UUID item : this.excludedIds) + notInClause.value(item); + predicates.add(notInClause.not()); + } + if (this.like != null && !this.like.isEmpty()) { predicates.add(queryContext.CriteriaBuilder.like(queryContext.Root.get(DmpBlueprintEntity._label), this.like)); } + if (this.isActives != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._isActive)); for (IsActive item : this.isActives) @@ -149,12 +209,28 @@ public class DmpBlueprintQuery extends QueryBase { inClause.value(item); predicates.add(inClause); } - if (this.excludedIds != null) { - CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._id)); - for (UUID item : this.excludedIds) - notInClause.value(item); - predicates.add(notInClause.not()); + + if (this.groupIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._groupId)); + for (UUID item : this.groupIds) + inClause.value(item); + predicates.add(inClause); } + + if (this.versions != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._version)); + for (Short item : this.versions) + inClause.value(item); + predicates.add(inClause); + } + + if (this.versionStatuses != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpBlueprintEntity._versionStatus)); + for (DmpBlueprintVersionStatus item : this.versionStatuses) + inClause.value(item); + predicates.add(inClause); + } + if (!predicates.isEmpty()) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); return queryContext.CriteriaBuilder.and(predicatesArray); @@ -168,26 +244,45 @@ public class DmpBlueprintQuery extends QueryBase { DmpBlueprintEntity item = new DmpBlueprintEntity(); item.setId(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._id, UUID.class)); item.setLabel(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._label, String.class)); + item.setDefinition(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._definition, String.class)); + item.setStatus(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._status, DmpBlueprintStatus.class)); + item.setGroupId(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._groupId, UUID.class)); + item.setVersion(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._version, Short.class)); + item.setVersionStatus(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._versionStatus, DmpBlueprintVersionStatus.class)); item.setCreatedAt(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._createdAt, Instant.class)); item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._updatedAt, Instant.class)); item.setIsActive(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._isActive, IsActive.class)); - item.setDefinition(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._definition, String.class)); - item.setStatus(QueryBase.convertSafe(tuple, columns, DmpBlueprintEntity._status, DmpBlueprintStatus.class)); return item; } @Override protected String fieldNameOf(FieldResolver item) { - if (item.match(DmpBlueprint._id)) return DmpBlueprintEntity._id; - else if (item.match(DmpBlueprint._label)) return DmpBlueprintEntity._label; - else if (item.match(DmpBlueprint._definition)) return DmpBlueprintEntity._definition; - else if (item.prefix(DmpBlueprint._definition)) return DmpBlueprintEntity._definition; - else if (item.match(DmpBlueprint._createdAt)) return DmpBlueprintEntity._createdAt; - else if (item.match(DmpBlueprint._updatedAt)) return DmpBlueprintEntity._updatedAt; - else if (item.match(DmpBlueprint._hash)) return DmpBlueprintEntity._updatedAt; - else if (item.match(DmpBlueprint._isActive)) return DmpBlueprintEntity._isActive; - else if (item.match(DmpBlueprint._status)) return DmpBlueprintEntity._status; - else return null; + if (item.match(DmpBlueprint._id)) + return DmpBlueprintEntity._id; + else if (item.match(DmpBlueprint._label)) + return DmpBlueprintEntity._label; + else if (item.match(DmpBlueprint._definition)) + return DmpBlueprintEntity._definition; + else if (item.prefix(DmpBlueprint._definition)) + return DmpBlueprintEntity._definition; + else if (item.match(DmpBlueprint._status)) + return DmpBlueprintEntity._status; + else if (item.match(DmpBlueprint._groupId)) + return DmpBlueprintEntity._groupId; + else if (item.match(DmpBlueprint._version)) + return DmpBlueprintEntity._version; + else if (item.match(DmpBlueprint._versionStatus)) + return DmpBlueprintEntity._versionStatus; + else if (item.match(DmpBlueprint._createdAt)) + return DmpBlueprintEntity._createdAt; + else if (item.match(DmpBlueprint._updatedAt)) + return DmpBlueprintEntity._updatedAt; + else if (item.match(DmpBlueprint._isActive)) + return DmpBlueprintEntity._isActive; + else if (item.match(DmpBlueprint._hash)) + return DmpBlueprintEntity._updatedAt; + else + return null; } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java index 245054e0b..a6bd2b11e 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java @@ -172,9 +172,12 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic DescriptionTemplateEntity data; if (isUpdate) { data = this.entityManager.find(DescriptionTemplateEntity.class, model.getId()); - if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DescriptionTemplate.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 (data.getStatus().equals(DescriptionTemplateStatus.Finalized)) throw new MyForbiddenException("Can not update finalized template"); + if (data == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DescriptionTemplate.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 (data.getStatus().equals(DescriptionTemplateStatus.Finalized)) + throw new MyForbiddenException("Can not update finalized template"); } else { data = new DescriptionTemplateEntity(); data.setId(UUID.randomUUID()); @@ -187,7 +190,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic } DescriptionTemplateStatus previousStatus = data.getStatus(); - + data.setDescription(model.getDescription()); data.setLabel(model.getLabel()); data.setTypeId(model.getType()); @@ -205,37 +208,40 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic this.addOwner(data); this.entityManager.flush(); - + this.updateVersionStatusAndSave(data, previousStatus, data.getStatus()); this.entityManager.flush(); - + return this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, DescriptionTemplate._id), data); } - - private void updateVersionStatusAndSave(DescriptionTemplateEntity data, DescriptionTemplateStatus previousStatus, DescriptionTemplateStatus newStatus){ - if (previousStatus.equals(newStatus)) return; - if (previousStatus.equals(DescriptionTemplateStatus.Finalized)) throw new MyForbiddenException("Can not update finalized template"); + + private void updateVersionStatusAndSave(DescriptionTemplateEntity data, DescriptionTemplateStatus previousStatus, DescriptionTemplateStatus newStatus) { + if (previousStatus.equals(newStatus)) + return; + if (previousStatus.equals(DescriptionTemplateStatus.Finalized)) + throw new MyForbiddenException("Can not update finalized template"); if (newStatus.equals(DescriptionTemplateStatus.Finalized)) { List latestVersionDescriptionTemplates = this.queryFactory.query(DescriptionTemplateQuery.class).versionStatuses(DescriptionTemplateVersionStatus.Current).isActive(IsActive.Active).groupIds(data.getGroupId()).collect(); - if (latestVersionDescriptionTemplates.size() > 1) throw new MyValidationException("Multiple previous template found"); + if (latestVersionDescriptionTemplates.size() > 1) + throw new MyValidationException("Multiple previous template found"); DescriptionTemplateEntity oldDescriptionTemplateEntity = latestVersionDescriptionTemplates.stream().findFirst().orElse(null); data.setVersionStatus(DescriptionTemplateVersionStatus.Current); - - if (oldDescriptionTemplateEntity != null){ + + if (oldDescriptionTemplateEntity != null) { data.setVersion((short) (oldDescriptionTemplateEntity.getVersion() + 1)); - + oldDescriptionTemplateEntity.setVersionStatus(DescriptionTemplateVersionStatus.Previous); this.entityManager.merge(oldDescriptionTemplateEntity); } else { - data.setVersion((short)1); + data.setVersion((short) 1); } } - + } - + private void persistUsers(UUID id, List users) throws InvalidApplicationException { if (users == null) users = new ArrayList<>(); @@ -306,7 +312,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic DefinitionEntity data = new DefinitionEntity(); if (persist == null) return data; - + if (!this.conventionService.isListNullOrEmpty(persist.getPages())) { data.setPages(new ArrayList<>()); for (PagePersist pagePersist : persist.getPages()) { @@ -544,21 +550,35 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic //region NewVersion public DescriptionTemplate createNewVersion(NewVersionDescriptionTemplatePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException { - logger.debug(new MapLogEntry("persisting data descriptionTemplateType").And("model", model).And("fields", fields)); + logger.debug(new MapLogEntry("persisting data descriptionTemplate").And("model", model).And("fields", fields)); this.authorizationService.authorizeForce(Permission.CreateNewVersionDescriptionTemplate); DescriptionTemplateEntity oldDescriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, model.getId()); - if (oldDescriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); - if (!this.conventionService.hashValue(oldDescriptionTemplateEntity.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); + if (oldDescriptionTemplateEntity == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (!this.conventionService.hashValue(oldDescriptionTemplateEntity.getUpdatedAt()).equals(model.getHash())) + throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); + + List latestVersionDescriptionTemplates = this.queryFactory.query(DescriptionTemplateQuery.class) + .versionStatuses(DescriptionTemplateVersionStatus.Current) + .isActive(IsActive.Active) + .groupIds(oldDescriptionTemplateEntity.getGroupId()) + .collect(); + if (latestVersionDescriptionTemplates.isEmpty()) + throw new MyValidationException("Previous template not found"); + if (latestVersionDescriptionTemplates.size() > 1) + throw new MyValidationException("Multiple previous template found"); + if (!latestVersionDescriptionTemplates.getFirst().getVersion().equals(oldDescriptionTemplateEntity.getVersion())) + throw new MyValidationException(this.errors.getDescriptionTemplateNewVersionConflict().getCode(), this.errors.getDescriptionTemplateNewVersionConflict().getMessage()); + Long notFinalizedCount = this.queryFactory.query(DescriptionTemplateQuery.class) + .versionStatuses(DescriptionTemplateVersionStatus.NotFinalized) + .groupIds(oldDescriptionTemplateEntity.getGroupId()) + .isActive(IsActive.Active) + .count(); + if (notFinalizedCount > 0) + throw new MyValidationException("Already created draft for this template"); - List latestVersionDescriptionTemplates = this.queryFactory.query(DescriptionTemplateQuery.class).versionStatuses(DescriptionTemplateVersionStatus.Current).isActive(IsActive.Active).groupIds(oldDescriptionTemplateEntity.getGroupId()).collect(); - if (latestVersionDescriptionTemplates.isEmpty()) throw new MyValidationException("Previous template not found"); - if (latestVersionDescriptionTemplates.size() > 1) throw new MyValidationException("Multiple previous template found"); - if (!latestVersionDescriptionTemplates.getFirst().getVersion().equals(oldDescriptionTemplateEntity.getVersion())) throw new MyValidationException(this.errors.getDescriptionTemplateNewVersionConflict().getCode(), this.errors.getDescriptionTemplateNewVersionConflict().getMessage()); - Long notFinalizedCount = this.queryFactory.query(DescriptionTemplateQuery.class).versionStatuses(DescriptionTemplateVersionStatus.NotFinalized).groupIds(oldDescriptionTemplateEntity.getGroupId()).isActive(IsActive.Active).count(); - if (notFinalizedCount > 0) throw new MyValidationException("Already created draft for this template"); - DescriptionTemplateEntity data = new DescriptionTemplateEntity(); data.setId(UUID.randomUUID()); data.setIsActive(IsActive.Active); @@ -582,7 +602,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic this.entityManager.flush(); this.updateVersionStatusAndSave(data, DescriptionTemplateStatus.Draft, data.getStatus()); - + this.entityManager.flush(); return this.builderFactory.builder(DescriptionTemplateBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, DescriptionTemplate._id), data); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintService.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintService.java index 84709a41f..e7c7b9f0a 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintService.java @@ -5,6 +5,7 @@ import eu.eudat.commons.enums.DmpBlueprintSystemFieldType; import eu.eudat.data.DmpBlueprintEntity; import eu.eudat.model.DmpBlueprint; import eu.eudat.model.persist.DmpBlueprintPersist; +import eu.eudat.model.persist.NewVersionDmpBlueprintPersist; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; @@ -31,6 +32,8 @@ public interface DmpBlueprintService { DmpBlueprint buildClone(UUID id, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException; + DmpBlueprint createNewVersion(NewVersionDmpBlueprintPersist model, FieldSet fieldSet) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException; + ResponseEntity exportXml(UUID id) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, TransformerException, InvalidApplicationException; DmpBlueprint importXml(byte[] bytes, String label, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintServiceImpl.java index 6788ce75e..75e286f56 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/dmpblueprint/DmpBlueprintServiceImpl.java @@ -4,10 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.Permission; import eu.eudat.commons.XmlHandlingService; -import eu.eudat.commons.enums.DmpBlueprintFieldCategory; -import eu.eudat.commons.enums.DmpBlueprintStatus; -import eu.eudat.commons.enums.DmpBlueprintSystemFieldType; -import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.enums.*; import eu.eudat.commons.types.dmpblueprint.*; import eu.eudat.commons.types.dmpblueprint.importexport.*; import eu.eudat.convention.ConventionService; @@ -17,11 +14,12 @@ import eu.eudat.model.DmpBlueprint; import eu.eudat.model.builder.DmpBlueprintBuilder; import eu.eudat.model.deleter.DmpBlueprintDeleter; import eu.eudat.model.dmpblueprintdefinition.Definition; -import eu.eudat.model.dmpblueprintdefinition.DescriptionTemplate; import eu.eudat.model.dmpblueprintdefinition.Field; import eu.eudat.model.dmpblueprintdefinition.Section; import eu.eudat.model.persist.DmpBlueprintPersist; +import eu.eudat.model.persist.NewVersionDmpBlueprintPersist; import eu.eudat.model.persist.dmpblueprintdefinition.*; +import eu.eudat.query.DescriptionTemplateQuery; import eu.eudat.query.DmpBlueprintQuery; import eu.eudat.service.responseutils.ResponseUtilsService; import gr.cite.commons.web.authz.service.AuthorizationService; @@ -39,6 +37,7 @@ import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidatorFactory; import jakarta.persistence.EntityManager; import jakarta.xml.bind.JAXBException; +import org.apache.bcel.generic.DADD; import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -127,13 +126,21 @@ public class DmpBlueprintServiceImpl implements DmpBlueprintService { throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DmpBlueprint.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 (data.getStatus().equals(DmpBlueprintStatus.Finalized)) + throw new MyForbiddenException("Cannot update finalized blueprint"); } else { data = new DmpBlueprintEntity(); data.setId(UUID.randomUUID()); - data.setIsActive(IsActive.Active); + data.setStatus(DmpBlueprintStatus.Draft); + data.setGroupId(UUID.randomUUID()); + data.setVersion((short) 1); + data.setVersionStatus(DmpBlueprintVersionStatus.NotFinalized); data.setCreatedAt(Instant.now()); + data.setIsActive(IsActive.Active); } + DmpBlueprintStatus previousStatus = data.getStatus(); + if (model.getDefinition() != null && !model.getDefinition().getSections().stream().anyMatch(x -> x.getHasTemplates())) { throw new MyValidationException(this.errors.getDmpBlueprintHasNoDescriptionTemplates().getCode(), this.errors.getDmpBlueprintHasNoDescriptionTemplates().getMessage()); } @@ -150,9 +157,43 @@ public class DmpBlueprintServiceImpl implements DmpBlueprintService { this.entityManager.flush(); + this.updateVersionStatusAndSave(data, previousStatus, data.getStatus()); + + this.entityManager.flush(); + return this.builderFactory.builder(DmpBlueprintBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, DmpBlueprint._id), data); } + private void updateVersionStatusAndSave(DmpBlueprintEntity data, DmpBlueprintStatus previousStatus, DmpBlueprintStatus newStatus) { + if (previousStatus.equals(newStatus)) + return; + if (previousStatus.equals(DmpBlueprintStatus.Finalized)) + throw new MyForbiddenException("Can not update finalized blueprint"); + + if (newStatus.equals(DmpBlueprintStatus.Finalized)) { + List latestVersionDmpBlueprints = this.queryFactory.query(DmpBlueprintQuery.class) + .versionStatuses(DmpBlueprintVersionStatus.Current) + .isActive(IsActive.Active) + .groupIds(data.getGroupId()) + .collect(); + if (latestVersionDmpBlueprints.size() > 1) + throw new MyValidationException("Multiple previous blueprints found"); + DmpBlueprintEntity oldDmpBlueprintEntity = latestVersionDmpBlueprints.stream().findFirst().orElse(null); + + data.setVersionStatus(DmpBlueprintVersionStatus.Current); + + if (oldDmpBlueprintEntity != null) { + data.setVersion((short) (oldDmpBlueprintEntity.getVersion() + 1)); + + oldDmpBlueprintEntity.setVersionStatus(DmpBlueprintVersionStatus.Previous); + this.entityManager.merge(oldDmpBlueprintEntity); + } else { + data.setVersion((short) 1); + } + } + + } + private @NotNull DefinitionEntity buildDefinitionEntity(DefinitionPersist persist) { DefinitionEntity data = new DefinitionEntity(); if (persist == null) @@ -327,6 +368,63 @@ public class DmpBlueprintServiceImpl implements DmpBlueprintService { model.setId(UUID.randomUUID()); } + //endregion + + //region NewVersion + + @Override + public DmpBlueprint createNewVersion(NewVersionDmpBlueprintPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, JsonProcessingException, TransformerException { + logger.debug(new MapLogEntry("persisting data dmpBlueprint").And("model", model).And("fields", fields)); + + this.authorizationService.authorizeForce(Permission.CreateNewVersionDmpBlueprint); + + DmpBlueprintEntity oldDmpBlueprintEntity = this.entityManager.find(DmpBlueprintEntity.class, model.getId()); + if (oldDmpBlueprintEntity == null) + throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), DmpBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (!this.conventionService.hashValue(oldDmpBlueprintEntity.getUpdatedAt()).equals(model.getHash())) + throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); + + List latestVersionDmpBlueprints = this.queryFactory.query(DmpBlueprintQuery.class) + .versionStatuses(DmpBlueprintVersionStatus.Current) + .isActive(IsActive.Active) + .groupIds(oldDmpBlueprintEntity.getGroupId()) + .collect(); + if (latestVersionDmpBlueprints.isEmpty()) + throw new MyValidationException("Previous blueprint not found"); + if (latestVersionDmpBlueprints.size() > 1) + throw new MyValidationException("Multiple previous blueprints found"); + if (!latestVersionDmpBlueprints.getFirst().getVersion().equals(oldDmpBlueprintEntity.getVersion())) + throw new MyValidationException(this.errors.getDmpBlueprintNewVersionConflict().getCode(), this.errors.getDmpBlueprintNewVersionConflict().getMessage()); + Long notFinalizedCount = this.queryFactory.query(DmpBlueprintQuery.class) + .versionStatuses(DmpBlueprintVersionStatus.NotFinalized) + .groupIds(oldDmpBlueprintEntity.getGroupId()) + .isActive(IsActive.Active) + .count(); + if (notFinalizedCount > 0) + throw new MyValidationException("Already created draft for this blueprint"); + + DmpBlueprintEntity data = new DmpBlueprintEntity(); + data.setId(UUID.randomUUID()); + data.setLabel(model.getLabel()); + data.setStatus(model.getStatus()); + data.setDefinition(this.xmlHandlingService.toXml(this.buildDefinitionEntity(model.getDefinition()))); + data.setGroupId(oldDmpBlueprintEntity.getGroupId()); + data.setVersion((short) (oldDmpBlueprintEntity.getVersion() + 1)); + data.setVersionStatus(DmpBlueprintVersionStatus.NotFinalized); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + + this.entityManager.persist(data); + + this.entityManager.flush(); + + this.updateVersionStatusAndSave(data, DmpBlueprintStatus.Draft, data.getStatus()); + + this.entityManager.flush(); + + return this.builderFactory.builder(DmpBlueprintBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, DmpBlueprint._id), data); + } //endregion diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpBlueprintController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpBlueprintController.java index 3f3891810..3c333b810 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpBlueprintController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpBlueprintController.java @@ -3,6 +3,7 @@ package eu.eudat.controllers.v2; import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.audit.AuditableAction; import eu.eudat.authorization.AuthorizationFlags; +import eu.eudat.model.persist.NewVersionDmpBlueprintPersist; import gr.cite.tools.validation.ValidationFilterAnnotation; import eu.eudat.data.DmpBlueprintEntity; import eu.eudat.model.DmpBlueprint; @@ -153,6 +154,21 @@ public class DmpBlueprintController { return model; } + @PostMapping("new-version") + @Transactional + @ValidationFilterAnnotation(validator = NewVersionDmpBlueprintPersist.NewVersionDmpBlueprintPersistValidator.ValidatorName, argumentName = "model") + public DmpBlueprint createNewVersion(@RequestBody NewVersionDmpBlueprintPersist model, FieldSet fieldSet) throws JAXBException, InvalidApplicationException, ParserConfigurationException, JsonProcessingException, TransformerException { + logger.debug(new MapLogEntry("persisting" + NewVersionDmpBlueprintPersist.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet)); + DmpBlueprint persisted = this.dmpBlueprintService.createNewVersion(model, fieldSet); + + this.auditService.track(AuditableAction.DmpBlueprint_PersistNewVersion, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + + return persisted; + } + @RequestMapping(method = RequestMethod.GET, value = {"/xml/export/{id}"}, produces = "application/xml") public @ResponseBody ResponseEntity getXml(@PathVariable UUID id) throws JAXBException, ParserConfigurationException, IOException, TransformerException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { logger.debug(new MapLogEntry("export" + DmpBlueprint.class.getSimpleName()).And("id", id)); diff --git a/dmp-backend/web/src/main/resources/config/errors.yml b/dmp-backend/web/src/main/resources/config/errors.yml index dd9fd1292..2e22ab92d 100644 --- a/dmp-backend/web/src/main/resources/config/errors.yml +++ b/dmp-backend/web/src/main/resources/config/errors.yml @@ -50,3 +50,6 @@ error-thesaurus: dmp-blueprint-has-no-description-templates: code: 120 message: You need to select Has Description Templates field to at least one Section. + dmp-blueprint-new-version-conflict: + code: 121 + message: version to update not the latest diff --git a/dmp-backend/web/src/main/resources/config/permissions.yml b/dmp-backend/web/src/main/resources/config/permissions.yml index 00bb8f25e..e4f2ac7dc 100644 --- a/dmp-backend/web/src/main/resources/config/permissions.yml +++ b/dmp-backend/web/src/main/resources/config/permissions.yml @@ -404,6 +404,12 @@ permissions: clients: [ ] allowAnonymous: false allowAuthenticated: false + CreateNewVersionDmpBlueprint: + roles: + - Admin + clients: [ ] + allowAnonymous: false + allowAuthenticated: false ExportDmpBlueprint: roles: - Admin