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 2d09a243d..f164c03d4 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 @@ -34,7 +34,8 @@ public class AuditableAction { public static final EventId Dmp_Clone = new EventId(5004, "Dmp_Clone"); public static final EventId Dmp_PersistNewVersion = new EventId(5005, "Dmp_PersistNewVersion"); public static final EventId Dmp_Assign_Users = new EventId(5006, "Dmp_Assign_Users"); - + public static final EventId Dmp_RemoveUser = new EventId(5007, "Dmp_RemoveUser"); + public static final EventId Description_Query = new EventId(6000, "Description_Query"); public static final EventId Description_Lookup = new EventId(6001, "Description_Lookup"); public static final EventId Description_Persist = new EventId(6002, "Description_Persist"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/DmpEntity.java b/dmp-backend/core/src/main/java/eu/eudat/data/DmpEntity.java index 9ea9e9eb1..f0ad68f03 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/data/DmpEntity.java +++ b/dmp-backend/core/src/main/java/eu/eudat/data/DmpEntity.java @@ -25,12 +25,13 @@ public class DmpEntity implements DataEntity { public static final String _id = "id"; - @Column(name = "label") + @Column(name = "label", length = DescriptionEntity._labelLength, nullable = false) private String label; public static final String _label = "label"; + public static final int _labelLength = 250; - @Column(name = "version") + @Column(name = "version", nullable = false) private Short version; public static final String _version = "version"; @@ -41,38 +42,38 @@ public class DmpEntity implements DataEntity { public static final String _versionStatus = "versionStatus"; - @Column(name = "status") + @Column(name = "status", nullable = false) @Convert(converter = DmpStatusConverter.class) private DmpStatus status; public static final String _status = "status"; - @Column(name = "properties", nullable = false) + @Column(name = "properties", nullable = true) private String properties; public static final String _properties = "properties"; - @Column(name = "group_id", columnDefinition = "BINARY(16)") + @Column(name = "group_id", nullable = false) private UUID groupId; public static final String _groupId = "groupId"; - @Column(name = "description", nullable = false) + @Column(name = "description", nullable = true) private String description; public static final String _description = "description"; - @Column(name = "created_at") + @Column(name = "created_at", nullable = false) private Instant createdAt; public static final String _createdAt = "createdAt"; - @Column(name = "updated_at") + @Column(name = "updated_at", nullable = false) private Instant updatedAt; public static final String _updatedAt = "updatedAt"; - @Column(name = "is_active") + @Column(name = "is_active", nullable = false) @Convert(converter = IsActiveConverter.class) private IsActive isActive; @@ -83,28 +84,28 @@ public class DmpEntity implements DataEntity { public static final String _finalizedAt = "finalizedAt"; - @Column(name = "creator") + @Column(name = "creator", nullable = false) private UUID creatorId; public static final String _creatorId = "creatorId"; - @Column(name = "access_type", nullable = false) + @Column(name = "access_type", nullable = true) @Convert(converter = DmpAccessTypeNullableConverter.class) private DmpAccessType accessType; public static final String _accessType = "accessType"; - @Column(name = "blueprint") + @Column(name = "blueprint", nullable = false) private UUID blueprintId; public static final String _blueprintId = "blueprintId"; - @Column(name = "language", nullable = false) + @Column(name = "language", nullable = true) private String language; public static final String _language = "language"; - @Column(name = "public_after", nullable = false) + @Column(name = "public_after", nullable = true) private Instant publicAfter; public static final String _publicAfter = "publicAfter"; 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 cc11e713e..f5b56a483 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpPersist.java @@ -2,33 +2,54 @@ package eu.eudat.model.persist; import eu.eudat.commons.enums.DmpAccessType; import eu.eudat.commons.enums.DmpStatus; +import eu.eudat.commons.validation.FieldNotNullIfOtherSet; +import eu.eudat.commons.validation.ValidEnum; import eu.eudat.commons.validation.ValidId; +import eu.eudat.data.DescriptionEntity; +import eu.eudat.data.DmpEntity; import eu.eudat.model.persist.dmpproperties.DmpPropertiesPersist; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import java.util.List; import java.util.UUID; +@FieldNotNullIfOtherSet(message = "{validation.hashempty}") public class DmpPersist { @ValidId(message = "{validation.invalidid}") private UUID id; + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + @Size(max = DmpEntity._labelLength, message = "{validation.largerthanmax}") private String label; + @ValidEnum(message = "{validation.empty}") private DmpStatus status; + @NotNull(message = "{validation.empty}") private DmpPropertiesPersist properties; private String description; private String language; + @NotNull(message = "{validation.empty}") + @ValidId(message = "{validation.invalidid}") private UUID blueprint; + private DmpAccessType accessType; + @NotNull(message = "{validation.empty}") + @Valid private List references; + @NotNull(message = "{validation.empty}") + @Valid private List descriptionTemplates; private String hash; diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpUserRemovePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpUserRemovePersist.java new file mode 100644 index 000000000..20dc12ff3 --- /dev/null +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/DmpUserRemovePersist.java @@ -0,0 +1,57 @@ +package eu.eudat.model.persist; + +import eu.eudat.commons.enums.DmpAccessType; +import eu.eudat.commons.enums.DmpStatus; +import eu.eudat.commons.enums.DmpUserRole; +import eu.eudat.commons.validation.FieldNotNullIfOtherSet; +import eu.eudat.commons.validation.ValidEnum; +import eu.eudat.commons.validation.ValidId; +import eu.eudat.data.DmpEntity; +import eu.eudat.model.persist.dmpproperties.DmpPropertiesPersist; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import java.util.List; +import java.util.UUID; + +public class DmpUserRemovePersist { + + @NotNull(message = "{validation.empty}") + @ValidId(message = "{validation.invalidid}") + private UUID id; + + @NotNull(message = "{validation.empty}") + @ValidId(message = "{validation.invalidid}") + private UUID dmpId; + + + @NotNull(message = "{validation.empty}") + @ValidEnum(message = "{validation.empty}") + private DmpUserRole role; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public UUID getDmpId() { + return dmpId; + } + + public void setDmpId(UUID dmpId) { + this.dmpId = dmpId; + } + + public DmpUserRole getRole() { + return role; + } + + public void setRole(DmpUserRole role) { + this.role = role; + } +} diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java index 8766064ba..02fb937c6 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java @@ -241,11 +241,11 @@ public class DescriptionQuery extends QueryBase { } if (this.dmpDescriptionTemplateQuery != null) { QueryContext subQuery = this.applySubQuery(this.dmpDescriptionTemplateQuery, queryContext, UUID.class, DmpDescriptionTemplate._id); - predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId)).value(subQuery)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId)).value(subQuery.Query)); } if (this.dmpQuery != null) { QueryContext subQuery = this.applySubQuery(this.dmpQuery, queryContext, UUID.class, Dmp._id); - predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpId)).value(subQuery)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpId)).value(subQuery.Query)); } if (!predicates.isEmpty()) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java index 49f741676..1460ad9b2 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DmpQuery.java @@ -289,7 +289,7 @@ public class DmpQuery extends QueryBase { if (this.dmpDescriptionTemplateQuery != null) { QueryContext subQuery = this.applySubQuery(this.dmpDescriptionTemplateQuery, queryContext, UUID.class, DmpDescriptionTemplate._dmp); - predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId)).value(subQuery)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionEntity._dmpDescriptionTemplateId)).value(subQuery.Query)); } if (!predicates.isEmpty()) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/UserQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/UserQuery.java index 02019b522..ed637a443 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/UserQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/UserQuery.java @@ -5,8 +5,10 @@ import eu.eudat.authorization.Permission; import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.scope.user.UserScope; import eu.eudat.data.*; +import eu.eudat.model.DmpDescriptionTemplate; import eu.eudat.model.User; import eu.eudat.model.PublicUser; +import eu.eudat.model.UserRole; import eu.eudat.query.utils.BuildSubQueryInput; import eu.eudat.query.utils.QueryUtilsService; import gr.cite.commons.web.authz.service.AuthorizationService; @@ -194,9 +196,8 @@ public class UserQuery extends QueryBase { predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(userContactInfoSubquery)); } if (this.userRoleQuery != null) { - Subquery subQuery = queryContext.Query.subquery(this.userRoleQuery.entityClass()); - this.applySubQuery(this.userRoleQuery, queryContext.CriteriaBuilder, subQuery); - predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(subQuery)); + QueryContext subQuery = this.applySubQuery(this.userRoleQuery, queryContext, UUID.class, UserRole._user); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(subQuery.Query)); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java index 406413bd3..6927628bf 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/UserRoleQuery.java @@ -175,6 +175,7 @@ public class UserRoleQuery extends QueryBase { if (item.match(UserRole._id)) return UserRoleEntity._id; else if (item.match(UserRole._role)) return UserRoleEntity._role; else if (item.prefix(UserRole._user)) return UserRoleEntity._userId; + else if (item.match(UserRole._user)) return UserRoleEntity._userId; else if (item.match(UserRole._createdAt) ) return UserRoleEntity._createdAt; else return null; } 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 48fcc4de5..4c320d1db 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 @@ -3,10 +3,8 @@ package eu.eudat.service.dmp; import com.fasterxml.jackson.core.JsonProcessingException; import eu.eudat.data.DmpUserEntity; import eu.eudat.model.Dmp; -import eu.eudat.model.persist.CloneDmpPersist; -import eu.eudat.model.persist.DmpPersist; -import eu.eudat.model.persist.DmpUserPersist; -import eu.eudat.model.persist.NewVersionDmpPersist; +import eu.eudat.model.DmpUser; +import eu.eudat.model.persist.*; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; @@ -36,7 +34,8 @@ public interface DmpService { Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException; - List assignUsers(UUID dmp, List model, FieldSet fields) throws InvalidApplicationException; + List assignUsers(UUID dmp, List model, FieldSet fields) throws InvalidApplicationException; + Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException; ResponseEntity export(UUID id, DmpExportType exportType); 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 f2a2acf65..2f8d86628 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 @@ -18,8 +18,10 @@ import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.event.DmpTouchedEvent; import eu.eudat.event.EventBroker; import eu.eudat.model.Dmp; +import eu.eudat.model.DmpUser; import eu.eudat.model.Reference; import eu.eudat.model.builder.DmpBuilder; +import eu.eudat.model.builder.DmpUserBuilder; import eu.eudat.model.deleter.*; import eu.eudat.model.persist.*; import eu.eudat.model.persist.dmpproperties.DmpBlueprintValuePersist; @@ -319,7 +321,7 @@ public class DmpServiceImpl implements DmpService { } @Override - public List assignUsers(UUID dmp, List model, FieldSet fieldSet) throws InvalidApplicationException { + public List assignUsers(UUID dmp, List model, FieldSet fieldSet) throws InvalidApplicationException { this.authorizationService.authorizeForce(Permission.AssignDmpUsers); List existingUsers = this.queryFactory.query(DmpUserQuery.class) @@ -327,32 +329,50 @@ public class DmpServiceImpl implements DmpService { .isActives(IsActive.Active) .collect(); + List updatedCreatedIds = new ArrayList<>(); for (DmpUserPersist dmpUser : model) { - if (checkUserRoleIfExists(existingUsers, dmp, dmpUser.getUser(), dmpUser.getRole())) - continue; - - DmpUserEntity newUser = new DmpUserEntity(); - newUser.setId(UUID.randomUUID()); - newUser.setDmp(dmp); - newUser.setUserId(dmpUser.getUser()); - newUser.setRole(dmpUser.getRole()); - newUser.setCreatedAt(Instant.now()); - newUser.setUpdatedAt(Instant.now()); - newUser.setIsActive(IsActive.Active); - - this.entityManager.persist(newUser); + DmpUserEntity dmpUserEntity = existingUsers.stream().filter(x-> x.getDmp().equals(dmp) && x.getUserId().equals(dmpUser.getUser()) && x.getRole().equals(dmpUser.getRole())).findFirst().orElse(null); + if (dmpUserEntity == null){ + dmpUserEntity = new DmpUserEntity(); + dmpUserEntity.setId(UUID.randomUUID()); + dmpUserEntity.setDmp(dmp); + dmpUserEntity.setUserId(dmpUser.getUser()); + dmpUserEntity.setRole(dmpUser.getRole()); + dmpUserEntity.setCreatedAt(Instant.now()); + dmpUserEntity.setUpdatedAt(Instant.now()); + dmpUserEntity.setIsActive(IsActive.Active); + this.entityManager.persist(dmpUserEntity); + } + updatedCreatedIds.add(dmpUserEntity.getUserId()); } - //If there are still dmp user associations here this means that they were not included in the persist model, so they have to be deleted - if (!existingUsers.isEmpty()) - this.deleterFactory.deleter(DmpUserDeleter.class).delete(existingUsers); + List toDelete = existingUsers.stream().filter(x-> updatedCreatedIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList()); + if (!toDelete.isEmpty()) this.deleterFactory.deleter(DmpUserDeleter.class).delete(toDelete); this.entityManager.flush(); - return this.queryFactory.query(DmpUserQuery.class) + List persisted = this.queryFactory.query(DmpUserQuery.class) .dmpIds(dmp) .isActives(IsActive.Active) .collect(); + return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted); + } + + @Override + public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException { + this.authorizationService.authorizeForce(Permission.AssignDmpUsers); + DmpEntity data = this.entityManager.find(DmpEntity.class, model.getId()); + if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + List existingUsers = this.queryFactory.query(DmpUserQuery.class) + .dmpIds(model.getDmpId()).ids(model.getId()).userRoles(model.getRole()) + .collect(); + + if (!existingUsers.isEmpty()) this.deleterFactory.deleter(DmpUserDeleter.class).delete(existingUsers); + + this.entityManager.flush(); + + return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data); } @Override @@ -582,14 +602,13 @@ public class DmpServiceImpl implements DmpService { return data; } - private boolean checkUserRoleIfExists(List dmpUserEntities, UUID dmp, UUID user, DmpUserRole role) { + private DmpUserEntity checkUserRoleIfExists(List dmpUserEntities, UUID dmp, UUID user, DmpUserRole role) { for (DmpUserEntity dmpUser : dmpUserEntities) { if (dmpUser.getDmp().equals(dmp) && dmpUser.getUserId().equals(user) && dmpUser.getRole() == role) { - dmpUserEntities.remove(dmpUser); - return true; + return dmpUser; }; } - return false; + return null; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpController.java index 68eb876b3..7f45e6b5f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/v2/DmpController.java @@ -10,10 +10,7 @@ import eu.eudat.model.DmpUser; import eu.eudat.model.builder.DmpBuilder; import eu.eudat.model.builder.DmpUserBuilder; import eu.eudat.model.censorship.DmpCensor; -import eu.eudat.model.persist.CloneDmpPersist; -import eu.eudat.model.persist.DmpPersist; -import eu.eudat.model.persist.DmpUserPersist; -import eu.eudat.model.persist.NewVersionDmpPersist; +import eu.eudat.model.persist.*; import eu.eudat.model.result.QueryResult; import eu.eudat.query.DmpQuery; import eu.eudat.query.lookup.DmpLookup; @@ -173,16 +170,29 @@ public class DmpController { public QueryResult assignUsers(@PathVariable("id") UUID id, @MyValidate @RequestBody List model, FieldSet fieldSet) throws InvalidApplicationException { logger.debug(new MapLogEntry("assigning users to dmp").And("model", model).And("fieldSet", fieldSet)); - List persisted = this.dmpService.assignUsers(id, model, fieldSet); + List persisted = this.dmpService.assignUsers(id, model, fieldSet); this.auditService.track(AuditableAction.Dmp_Assign_Users, Map.ofEntries( - new AbstractMap.SimpleEntry("model", model), - new AbstractMap.SimpleEntry("fields", fieldSet) + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) )); - List models = this.builderFactory.builder(DmpUserBuilder.class).build(fieldSet, persisted); + return new QueryResult<>(persisted); + } - return new QueryResult<>(models); + @PostMapping("remove-user") + @Transactional + public QueryResult removeUser(@MyValidate @RequestBody DmpUserRemovePersist model, FieldSet fieldSet) throws InvalidApplicationException { + logger.debug(new MapLogEntry("remove user from dmp").And("model", model).And("fieldSet", fieldSet)); + + Dmp persisted = this.dmpService.removeUser(model, fieldSet); + + this.auditService.track(AuditableAction.Dmp_RemoveUser, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + + return new QueryResult<>(persisted); } @GetMapping("{id}/export/{type}")