diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java index 26a315dba..45d7fdc1f 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java @@ -56,8 +56,6 @@ public class TenantByCodeCacheService extends CacheService { + private final ConventionService conventionService; public static class UserInterceptorCacheValue { @@ -43,9 +46,16 @@ public class UserInterceptorCacheService extends CacheService ids) throws InvalidApplicationException { @@ -69,6 +70,7 @@ public class TenantDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new TenantTouchedEvent(item.getId(), item.getCode())); } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java index a79a96ace..094ffaf3a 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java @@ -3,6 +3,8 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.data.TenantUserEntity; +import gr.cite.annotation.event.EventBroker; +import gr.cite.annotation.event.UserRemovedFromTenantEvent; import gr.cite.annotation.query.TenantUserQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -28,17 +30,15 @@ public class TenantUserDeleter implements Deleter { private final TenantEntityManager entityManager; private final QueryFactory queryFactory; - private final DeleterFactory deleterFactory; - + private final EventBroker eventBroker; @Autowired public TenantUserDeleter( TenantEntityManager entityManager, - QueryFactory queryFactory, - DeleterFactory deleterFactory + QueryFactory queryFactory, EventBroker eventBroker ) { this.entityManager = entityManager; this.queryFactory = queryFactory; - this.deleterFactory = deleterFactory; + this.eventBroker = eventBroker; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { @@ -69,6 +69,7 @@ public class TenantUserDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new UserRemovedFromTenantEvent(item.getUserId(), item.getTenantId())); } } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java index 8ee773d2c..7c597c661 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java @@ -2,6 +2,8 @@ package gr.cite.annotation.service.tenant; import com.fasterxml.jackson.core.JsonProcessingException; import gr.cite.annotation.data.TenantEntityManager; +import gr.cite.annotation.event.EventBroker; +import gr.cite.annotation.event.TenantTouchedEvent; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.Permission; @@ -23,6 +25,8 @@ import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; @@ -45,17 +49,21 @@ public class TenantServiceImpl implements TenantService { private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; + private final EventBroker eventBroker; + private final MessageSource messageSource; public TenantServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, TenantEntityManager entityManager, - BuilderFactory builderFactory) { + BuilderFactory builderFactory, EventBroker eventBroker, MessageSource messageSource) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; this.conventionService = conventionService; this.entityManager = entityManager; this.builderFactory = builderFactory; + this.eventBroker = eventBroker; + this.messageSource = messageSource; } @Override @@ -91,16 +99,22 @@ public class TenantServiceImpl implements TenantService { this.entityManager.flush(); + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); return this.builderFactory.builder(TenantBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, Tenant._id), data); } @Override public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting Tenant: {}", id); + + TenantEntity data = this.entityManager.find(TenantEntity.class, id); + if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); this.authorizationService.authorizeForce(Permission.DeleteTenant); this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id)); + + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java index b99a5383c..155aadc33 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java @@ -8,6 +8,10 @@ import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.data.*; +import gr.cite.annotation.event.EventBroker; +import gr.cite.annotation.event.UserAddedToTenantEvent; +import gr.cite.annotation.event.UserCredentialTouchedEvent; +import gr.cite.annotation.event.UserTouchedEvent; import gr.cite.annotation.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; import gr.cite.annotation.model.Tenant; import gr.cite.annotation.model.User; @@ -62,6 +66,7 @@ public class UserServiceImpl implements UserService { private final TenantScope tenantScope; private final MessageSource messageSource; + private final EventBroker eventBroker; private final JsonHandlingService jsonHandlingService; @@ -70,7 +75,7 @@ public class UserServiceImpl implements UserService { DeleterFactory deleterFactory, ConventionService conventionService, TenantEntityManager entityManager, - BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource, + BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource, EventBroker eventBroker, JsonHandlingService jsonHandlingService) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -80,6 +85,7 @@ public class UserServiceImpl implements UserService { this.queryFactory = queryFactory; this.tenantScope = tenantScope; this.messageSource = messageSource; + this.eventBroker = eventBroker; this.jsonHandlingService = jsonHandlingService; } @@ -124,6 +130,7 @@ public class UserServiceImpl implements UserService { this.persistTenantUser(model.getTenantUsers(), data.getId()); this.entityManager.flush(); + this.eventBroker.emit(new UserTouchedEvent(data.getId())); return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data); } @@ -135,6 +142,7 @@ public class UserServiceImpl implements UserService { this.authorizationService.authorizeForce(Permission.DeleteUser); this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); + this.eventBroker.emit(new UserTouchedEvent(id)); } private void persistUserCredential(List models, UUID userId) throws InvalidApplicationException { @@ -155,6 +163,7 @@ public class UserServiceImpl implements UserService { data.setUpdatedAt(Instant.now()); entityManager.persist(data); } + this.eventBroker.emit(new UserCredentialTouchedEvent(data.getId(), data.getExternalId())); updatedCreatedIds.add(data.getId()); } } @@ -191,6 +200,7 @@ public class UserServiceImpl implements UserService { data.setUpdatedAt(Instant.now()); data.setIsActive(IsActive.Active); entityManager.persist(data); + this.eventBroker.emit(new UserAddedToTenantEvent(data.getUserId(), data.getTenantId())); } finally { this.tenantScope.removeTempTenant(this.entityManager); } diff --git a/backend/core/pom.xml b/backend/core/pom.xml index e5dcf843a..ed69c9b3c 100644 --- a/backend/core/pom.xml +++ b/backend/core/pom.xml @@ -66,7 +66,7 @@ org.opencdmp file-transformer-base - 0.0.22 + 0.0.23 gr.cite diff --git a/backend/core/src/main/java/org/opencdmp/authorization/Permission.java b/backend/core/src/main/java/org/opencdmp/authorization/Permission.java index fbf7f65c5..c07c2d3a7 100644 --- a/backend/core/src/main/java/org/opencdmp/authorization/Permission.java +++ b/backend/core/src/main/java/org/opencdmp/authorization/Permission.java @@ -131,6 +131,7 @@ public final class Permission { //Reference public static String BrowseReference = "BrowseReference"; + public static String BrowseExternalReference = "BrowseExternalReference"; public static String EditReference = "EditReference"; public static String DeleteReference = "DeleteReference"; diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/user/AdditionalInfoEntity.java b/backend/core/src/main/java/org/opencdmp/commons/types/user/AdditionalInfoEntity.java index 8c453b1b6..ff53b312b 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/user/AdditionalInfoEntity.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/user/AdditionalInfoEntity.java @@ -11,7 +11,7 @@ public class AdditionalInfoEntity { private UUID organizationId; public String getAvatarUrl() { - return avatarUrl; + return this.avatarUrl; } public void setAvatarUrl(String avatarUrl) { @@ -19,7 +19,7 @@ public class AdditionalInfoEntity { } public String getTimezone() { - return timezone; + return this.timezone; } public void setTimezone(String timezone) { @@ -27,7 +27,7 @@ public class AdditionalInfoEntity { } public String getCulture() { - return culture; + return this.culture; } public void setCulture(String culture) { @@ -35,7 +35,7 @@ public class AdditionalInfoEntity { } public String getLanguage() { - return language; + return this.language; } public void setLanguage(String language) { @@ -43,7 +43,7 @@ public class AdditionalInfoEntity { } public UUID getOrganizationId() { - return organizationId; + return this.organizationId; } public void setOrganizationId(UUID organizationId) { @@ -51,7 +51,7 @@ public class AdditionalInfoEntity { } public String getRoleOrganization() { - return roleOrganization; + return this.roleOrganization; } public void setRoleOrganization(String roleOrganization) { diff --git a/backend/core/src/main/java/org/opencdmp/event/EventBroker.java b/backend/core/src/main/java/org/opencdmp/event/EventBroker.java index 57131e304..80c75baa7 100644 --- a/backend/core/src/main/java/org/opencdmp/event/EventBroker.java +++ b/backend/core/src/main/java/org/opencdmp/event/EventBroker.java @@ -57,4 +57,8 @@ public class EventBroker { this.applicationEventPublisher.publishEvent(event); } + public void emit(UserCredentialTouchedEvent event) { + this.applicationEventPublisher.publishEvent(event); + } + } diff --git a/backend/core/src/main/java/org/opencdmp/event/TenantTouchedEvent.java b/backend/core/src/main/java/org/opencdmp/event/TenantTouchedEvent.java index ee47646f0..36187dc03 100644 --- a/backend/core/src/main/java/org/opencdmp/event/TenantTouchedEvent.java +++ b/backend/core/src/main/java/org/opencdmp/event/TenantTouchedEvent.java @@ -6,18 +6,16 @@ public class TenantTouchedEvent { public TenantTouchedEvent() { } - public TenantTouchedEvent(UUID tenantId, String tenantCode, String previousTenantCode) { + public TenantTouchedEvent(UUID tenantId, String tenantCode) { this.tenantId = tenantId; this.tenantCode = tenantCode; - this.previousTenantCode = previousTenantCode; } private UUID tenantId; private String tenantCode; - private String previousTenantCode; public UUID getTenantId() { - return tenantId; + return this.tenantId; } public void setTenantId(UUID tenantId) { @@ -25,18 +23,11 @@ public class TenantTouchedEvent { } public String getTenantCode() { - return tenantCode; + return this.tenantCode; } public void setTenantCode(String tenantCode) { this.tenantCode = tenantCode; } - public String getPreviousTenantCode() { - return previousTenantCode; - } - - public void setPreviousTenantCode(String previousTenantCode) { - this.previousTenantCode = previousTenantCode; - } } diff --git a/backend/core/src/main/java/org/opencdmp/event/UserCredentialTouchedEvent.java b/backend/core/src/main/java/org/opencdmp/event/UserCredentialTouchedEvent.java new file mode 100644 index 000000000..d34048834 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/event/UserCredentialTouchedEvent.java @@ -0,0 +1,33 @@ +package org.opencdmp.event; + +import java.util.UUID; + +public class UserCredentialTouchedEvent { + public UserCredentialTouchedEvent() { + } + + public UserCredentialTouchedEvent(UUID id, String subjectId) { + this.id = id; + this.subjectId = subjectId; + } + + private UUID id; + + public UUID getId() { + return this.id; + } + + public void setId(UUID id) { + this.id = id; + } + + private String subjectId; + + public String getSubjectId() { + return this.subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } +} diff --git a/backend/core/src/main/java/org/opencdmp/event/UserTouchedEvent.java b/backend/core/src/main/java/org/opencdmp/event/UserTouchedEvent.java index 638ee8060..42fd0f950 100644 --- a/backend/core/src/main/java/org/opencdmp/event/UserTouchedEvent.java +++ b/backend/core/src/main/java/org/opencdmp/event/UserTouchedEvent.java @@ -13,7 +13,7 @@ public class UserTouchedEvent { private UUID userId; public UUID getUserId() { - return userId; + return this.userId; } public void setUserId(UUID userId) { diff --git a/backend/core/src/main/java/org/opencdmp/model/deleter/TenantDeleter.java b/backend/core/src/main/java/org/opencdmp/model/deleter/TenantDeleter.java index b4ef6bcde..5f2563179 100644 --- a/backend/core/src/main/java/org/opencdmp/model/deleter/TenantDeleter.java +++ b/backend/core/src/main/java/org/opencdmp/model/deleter/TenantDeleter.java @@ -1,15 +1,18 @@ package org.opencdmp.model.deleter; -import org.opencdmp.commons.enums.IsActive; -import org.opencdmp.data.*; -import org.opencdmp.query.DescriptionTagQuery; -import org.opencdmp.query.TenantConfigurationQuery; -import org.opencdmp.query.TenantQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.data.TenantConfigurationEntity; +import org.opencdmp.data.TenantEntity; +import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.event.EventBroker; +import org.opencdmp.event.TenantTouchedEvent; +import org.opencdmp.query.TenantConfigurationQuery; +import org.opencdmp.query.TenantQuery; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -24,7 +27,7 @@ import java.util.UUID; import java.util.stream.Collectors; @Component -@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class TenantDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDeleter.class)); @@ -34,16 +37,18 @@ public class TenantDeleter implements Deleter { protected final QueryFactory queryFactory; protected final DeleterFactory deleterFactory; + private final EventBroker eventBroker; @Autowired public TenantDeleter( - TenantEntityManager entityManager, - QueryFactory queryFactory, - DeleterFactory deleterFactory + TenantEntityManager entityManager, + QueryFactory queryFactory, + DeleterFactory deleterFactory, EventBroker eventBroker ) { this.entityManager = entityManager; this.queryFactory = queryFactory; this.deleterFactory = deleterFactory; + this.eventBroker = eventBroker; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { @@ -83,6 +88,7 @@ public class TenantDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new TenantTouchedEvent(item.getId(), item.getCode())); } } diff --git a/backend/core/src/main/java/org/opencdmp/model/deleter/TenantUserDeleter.java b/backend/core/src/main/java/org/opencdmp/model/deleter/TenantUserDeleter.java index 48db5daa2..209666bf4 100644 --- a/backend/core/src/main/java/org/opencdmp/model/deleter/TenantUserDeleter.java +++ b/backend/core/src/main/java/org/opencdmp/model/deleter/TenantUserDeleter.java @@ -1,14 +1,15 @@ package org.opencdmp.model.deleter; -import org.opencdmp.commons.enums.IsActive; -import org.opencdmp.data.TenantEntityManager; -import org.opencdmp.data.TenantUserEntity; -import org.opencdmp.query.TenantUserQuery; import gr.cite.tools.data.deleter.Deleter; -import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.data.TenantEntityManager; +import org.opencdmp.data.TenantUserEntity; +import org.opencdmp.event.EventBroker; +import org.opencdmp.event.UserRemovedFromTenantEvent; +import org.opencdmp.query.TenantUserQuery; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -22,23 +23,22 @@ import java.util.Optional; import java.util.UUID; @Component -@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class TenantUserDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantUserDeleter.class)); private final TenantEntityManager entityManager; private final QueryFactory queryFactory; - private final DeleterFactory deleterFactory; + private final EventBroker eventBroker; @Autowired public TenantUserDeleter( TenantEntityManager entityManager, - QueryFactory queryFactory, - DeleterFactory deleterFactory + QueryFactory queryFactory, EventBroker eventBroker ) { this.entityManager = entityManager; this.queryFactory = queryFactory; - this.deleterFactory = deleterFactory; + this.eventBroker = eventBroker; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { @@ -69,6 +69,7 @@ public class TenantUserDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new UserRemovedFromTenantEvent(item.getUserId(), item.getTenantId())); } } } diff --git a/backend/core/src/main/java/org/opencdmp/model/deleter/UserCredentialDeleter.java b/backend/core/src/main/java/org/opencdmp/model/deleter/UserCredentialDeleter.java index dcef4d219..a7742bff3 100644 --- a/backend/core/src/main/java/org/opencdmp/model/deleter/UserCredentialDeleter.java +++ b/backend/core/src/main/java/org/opencdmp/model/deleter/UserCredentialDeleter.java @@ -6,6 +6,8 @@ import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.UserCredentialEntity; +import org.opencdmp.event.EventBroker; +import org.opencdmp.event.UserCredentialTouchedEvent; import org.opencdmp.query.UserCredentialQuery; import org.opencdmp.service.keycloak.KeycloakService; import org.slf4j.LoggerFactory; @@ -28,16 +30,18 @@ public class UserCredentialDeleter implements Deleter { protected final QueryFactory queryFactory; private final KeycloakService keycloakService; + private final EventBroker eventBroker; @Autowired public UserCredentialDeleter( TenantEntityManager entityManager, - QueryFactory queryFactory, KeycloakService keycloakService + QueryFactory queryFactory, KeycloakService keycloakService, EventBroker eventBroker ) { this.entityManager = entityManager; this.queryFactory = queryFactory; this.keycloakService = keycloakService; + this.eventBroker = eventBroker; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { @@ -67,6 +71,8 @@ public class UserCredentialDeleter implements Deleter { logger.trace("deleted item"); this.keycloakService.removeFromAllGroups(item.getExternalId()); + + this.eventBroker.emit(new UserCredentialTouchedEvent(item.getId(), item.getExternalId())); } } diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionCommonModelConfig.java b/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionCommonModelConfig.java new file mode 100644 index 000000000..63f7a57ac --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/DescriptionCommonModelConfig.java @@ -0,0 +1,88 @@ +package org.opencdmp.model.persist; + +import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.validation.BaseValidator; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +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 DescriptionCommonModelConfig { + + private String id; + public static final String _id = "id"; + private UUID sectionId; + public static final String _sectionId = "_sectionId"; + private UUID templateId; + public static final String _templateId = "templateId"; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public UUID getSectionId() { + return sectionId; + } + + public void setSectionId(UUID sectionId) { + this.sectionId = sectionId; + } + + public UUID getTemplateId() { + return templateId; + } + + public void setTemplateId(UUID templateId) { + this.templateId = templateId; + } + + + @Component(DescriptionCommonModelConfigValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class DescriptionCommonModelConfigValidator extends BaseValidator { + + public static final String ValidatorName = "DescriptionCommonModelConfigValidator"; + + private final MessageSource messageSource; + + protected DescriptionCommonModelConfigValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return DescriptionCommonModelConfig.class; + } + + @Override + protected List specifications(DescriptionCommonModelConfig item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isEmpty(item.getId())) + .failOn(DescriptionCommonModelConfig._id).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionCommonModelConfig._id}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getId())) + .must(() -> this.isValidGuid(UUID.fromString(item.getId()))) + .failOn(DescriptionCommonModelConfig._id).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionCommonModelConfig._id}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> this.isValidGuid(item.getSectionId())) + .failOn(DescriptionCommonModelConfig._sectionId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionCommonModelConfig._sectionId}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> this.isValidGuid(item.getTemplateId())) + .failOn(DescriptionCommonModelConfig._templateId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DescriptionCommonModelConfig._templateId}, LocaleContextHolder.getLocale())) + ); + } + } +} diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/DmpCommonModelConfig.java b/backend/core/src/main/java/org/opencdmp/model/persist/DmpCommonModelConfig.java new file mode 100644 index 000000000..e7f26a78f --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/model/persist/DmpCommonModelConfig.java @@ -0,0 +1,127 @@ +package org.opencdmp.model.persist; + +import gr.cite.tools.validation.ValidatorFactory; +import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.validation.BaseValidator; +import org.opencdmp.convention.ConventionService; +import org.opencdmp.data.DmpEntity; +import org.opencdmp.errorcode.ErrorThesaurusProperties; +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 DmpCommonModelConfig { + + private UUID fileId; + public static final String _fileId = "fileId"; + + private String label; + public static final String _label = "label"; + + private String repositoryId; + public static final String _repositoryId = "repositoryId"; + + private UUID blueprintId; + public static final String _blueprintId = "blueprintId"; + + private List descriptions; + public static final String _descriptions = "descriptions"; + + public UUID getFileId() { + return fileId; + } + + public void setFileId(UUID fileId) { + this.fileId = fileId; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getRepositoryId() { + return repositoryId; + } + + public void setRepositoryId(String repositoryId) { + this.repositoryId = repositoryId; + } + + public UUID getBlueprintId() { + return blueprintId; + } + + public void setBlueprintId(UUID blueprintId) { + this.blueprintId = blueprintId; + } + + public List getDescriptions() { + return descriptions; + } + + public void setDescriptions(List descriptions) { + this.descriptions = descriptions; + } + + @Component(DmpCommonModelConfigValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class DmpCommonModelConfigValidator extends BaseValidator { + + public static final String ValidatorName = "DmpCommonModelConfigValidator"; + + private final MessageSource messageSource; + private final ValidatorFactory validatorFactory; + + protected DmpCommonModelConfigValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { + super(conventionService, errors); + this.messageSource = messageSource; + this.validatorFactory = validatorFactory; + } + + @Override + protected Class modelClass() { + return DmpCommonModelConfig.class; + } + + @Override + protected List specifications(DmpCommonModelConfig item) { + return Arrays.asList( + this.spec() + .must(() -> this.isValidGuid(item.getFileId())) + .failOn(DmpCommonModelConfig._fileId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DmpCommonModelConfig._fileId}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isEmpty(item.getLabel())) + .failOn(DmpCommonModelConfig._label).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DmpCommonModelConfig._label}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getLabel())) + .must(() -> this.lessEqualLength(item.getLabel(), DmpEntity._labelLength)) + .failOn(DmpCommonModelConfig._label).failWith(this.messageSource.getMessage("Validation_MaxLength", new Object[]{DmpCommonModelConfig._label}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isEmpty(item.getRepositoryId())) + .failOn(DmpCommonModelConfig._repositoryId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DmpCommonModelConfig._repositoryId}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getRepositoryId())) + .must(() -> this.isValidGuid(UUID.fromString(item.getRepositoryId()))) + .failOn(DmpCommonModelConfig._repositoryId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DmpCommonModelConfig._repositoryId}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> this.isValidGuid(item.getBlueprintId())) + .failOn(DmpCommonModelConfig._blueprintId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DmpCommonModelConfig._blueprintId}, LocaleContextHolder.getLocale())), + this.navSpec() + .iff(() -> !this.isListNullOrEmpty(item.getDescriptions())) + .on(DmpCommonModelConfig._descriptions) + .over(item.getDescriptions()) + .using((itm) -> this.validatorFactory.validator(DescriptionCommonModelConfig.DescriptionCommonModelConfigValidator.class)) + ); + } + } +} diff --git a/backend/core/src/main/java/org/opencdmp/query/ReferenceQuery.java b/backend/core/src/main/java/org/opencdmp/query/ReferenceQuery.java index 2dedd72ab..42be7a255 100644 --- a/backend/core/src/main/java/org/opencdmp/query/ReferenceQuery.java +++ b/backend/core/src/main/java/org/opencdmp/query/ReferenceQuery.java @@ -232,6 +232,7 @@ public class ReferenceQuery extends QueryBase { List predicates = new ArrayList<>(); if (userId != null || usePublic) { predicates.add(queryContext.CriteriaBuilder.or( + this.authService.authorize(Permission.BrowseExternalReference) ? queryContext.CriteriaBuilder.equal(queryContext.Root.get(ReferenceEntity._sourceType), ReferenceSourceType.External) : queryContext.CriteriaBuilder.or(), userId != null ? queryContext.CriteriaBuilder.equal(queryContext.Root.get(ReferenceEntity._createdById), userId) : queryContext.CriteriaBuilder.or(), //Creates a false query queryContext.CriteriaBuilder.in(queryContext.Root.get(ReferenceEntity._id)).value(this.queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(DmpReferenceEntity.class, UUID.class) .query(queryContext.Query) diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java index 3540bd724..b085e82a9 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java @@ -1669,15 +1669,18 @@ public class DescriptionServiceImpl implements DescriptionService { if (!referenceTypeEntity.getCode().equals(model.getType().getCode())) throw new MyApplicationException("Invalid reference for field " + model.getId()); - ReferenceEntity referenceEntity = this.queryFactory.query(ReferenceQuery.class).ids(model.getId()).first(); //TODO: optimize - if (referenceEntity == null) referenceEntity = this.queryFactory.query(ReferenceQuery.class).references(model.getReference()).typeIds(referenceTypeEntity.getId()).sources(model.getSource()).first(); + ReferenceEntity referenceEntity = model.getId() != null ? this.queryFactory.query(ReferenceQuery.class).ids(model.getId()).first(): null; //TODO: optimize + if (referenceEntity == null && !this.conventionService.isNullOrEmpty(model.getReference())) { + List referenceEntities = this.queryFactory.query(ReferenceQuery.class).references(model.getReference()).typeIds(referenceTypeEntity.getId()).collect(); + if (referenceEntities != null && referenceEntities.size() == 1) referenceEntity = referenceEntities.getFirst(); + } ReferencePersist persist = new ReferencePersist(); persist.setTypeId(referenceTypeEntity.getId()); if (referenceEntity == null) { persist.setLabel(model.getLabel()); - persist.setReference(model.getReference()); + persist.setReference(!this.conventionService.isNullOrEmpty(model.getReference()) ? model.getReference() : UUID.randomUUID().toString()); persist.setSource("internal"); persist.setSourceType(ReferenceSourceType.Internal); } else { diff --git a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java index a18a547cd..28d9244d4 100644 --- a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java +++ b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpService.java @@ -7,12 +7,12 @@ import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; import jakarta.xml.bind.JAXBException; import org.opencdmp.commons.types.dmp.importexport.DmpImportExport; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.opencdmp.model.DmpUser; import org.opencdmp.model.DmpValidationResult; import org.opencdmp.model.dmp.Dmp; import org.opencdmp.model.persist.*; import org.springframework.http.ResponseEntity; -import org.springframework.web.multipart.MultipartFile; import org.xml.sax.SAXException; import javax.crypto.BadPaddingException; @@ -59,5 +59,7 @@ public interface DmpService { Dmp importXml(byte[] bytes, String label, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; - Dmp importJson(MultipartFile file, String label, String repositoryId, String format, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException; + Dmp importJson(DmpCommonModelConfig dmpCommonModelConfig, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException; + + PreprocessingDmpModel preprocessingDmp(UUID fileId, String repositoryId) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException; } diff --git a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java index 0cb87acc1..9f4428fac 100644 --- a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java @@ -64,6 +64,7 @@ import org.opencdmp.data.*; import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.opencdmp.event.DmpTouchedEvent; import org.opencdmp.event.EventBroker; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.opencdmp.integrationevent.outbox.annotationentityremoval.AnnotationEntityRemovalIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent; @@ -107,7 +108,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; import org.xml.sax.SAXException; import javax.crypto.BadPaddingException; @@ -2051,16 +2051,19 @@ public class DmpServiceImpl implements DmpService { //region Import RDA JSON + public PreprocessingDmpModel preprocessingDmp(UUID fileId, String repositoryId) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException { + return this.fileTransformerService.preprocessingDmp(fileId, repositoryId); + } - public Dmp importJson(MultipartFile file, String label, String repositoryId, String format, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException { - DmpModel model = this.fileTransformerService.importDmp(file, repositoryId, format); + public Dmp importJson(DmpCommonModelConfig dmpCommonModelConfig, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException { + logger.debug(new MapLogEntry("import data").And("file id", dmpCommonModelConfig.getFileId()).And("label", dmpCommonModelConfig.getLabel()).And("fields", fields)); + + DmpModel model = this.fileTransformerService.importDmp(dmpCommonModelConfig); if (model == null) throw new MyNotFoundException("Plan Import Error"); - logger.debug(new MapLogEntry("import data").And("bytes", file.getBytes()).And("label", label).And("fields", fields)); - DmpPersist persist = new DmpPersist(); - persist.setLabel(label); + persist.setLabel(dmpCommonModelConfig.getLabel()); persist.setStatus(DmpStatus.Draft); persist.setDescription(model.getDescription()); switch (model.getAccessType()) { @@ -2163,7 +2166,7 @@ public class DmpServiceImpl implements DmpService { } } else { // custom fields - if (field.getCategory().equals(org.opencdmp.commonmodels.enums.DmpBlueprintFieldCategory.Extra) && commonModel.getProperties() != null && this.conventionService.isListNullOrEmpty(commonModel.getProperties().getDmpBlueprintValues())){ + if (field.getCategory().equals(org.opencdmp.commonmodels.enums.DmpBlueprintFieldCategory.Extra) && commonModel.getProperties() != null && !this.conventionService.isListNullOrEmpty(commonModel.getProperties().getDmpBlueprintValues())){ DmpBlueprintValueModel dmpBlueprintValueModel = commonModel.getProperties().getDmpBlueprintValues().stream().filter(x -> x.getFieldId().equals(field.getId())).findFirst().orElse(null); ExtraFieldModel extraFieldModel = (ExtraFieldModel) field; if (dmpBlueprintValueModel != null) dmpBlueprintValues.put(dmpBlueprintValueModel.getFieldId(), this.commonModelDmpBlueprintValueToPersist(dmpBlueprintValueModel, extraFieldModel)); @@ -2204,18 +2207,21 @@ public class DmpServiceImpl implements DmpService { private ReferencePersist commonDmpReferenceToReferencePersist(ReferenceModel model, ReferenceTypeEntity referenceTypeEntity) { if (!referenceTypeEntity.getCode().equals(model.getType().getCode())) throw new MyApplicationException("Invalid reference for field " + model.getId()); - + if (this.conventionService.isNullOrEmpty(model.getLabel()) && this.conventionService.isNullOrEmpty(model.getReference())) throw new MyApplicationException("Dmp Reference without label and reference id "); - ReferenceEntity referenceEntity = this.queryFactory.query(ReferenceQuery.class).ids(model.getId()).first(); //TODO: optimize - if (referenceEntity == null) referenceEntity = this.queryFactory.query(ReferenceQuery.class).references(model.getReference()).typeIds(referenceTypeEntity.getId()).sources(model.getSource()).first(); + ReferenceEntity referenceEntity = model.getId() != null ? this.queryFactory.query(ReferenceQuery.class).ids(model.getId()).first(): null; //TODO: optimize + if (referenceEntity == null && !this.conventionService.isNullOrEmpty(model.getReference())) { + List referenceEntities = this.queryFactory.query(ReferenceQuery.class).references(model.getReference()).typeIds(referenceTypeEntity.getId()).collect(); + if (referenceEntities != null && referenceEntities.size() == 1) referenceEntity = referenceEntities.getFirst(); + } ReferencePersist persist = new ReferencePersist(); persist.setTypeId(referenceTypeEntity.getId()); if (referenceEntity == null) { persist.setLabel(model.getLabel()); - persist.setReference(model.getReference()); + persist.setReference(!this.conventionService.isNullOrEmpty(model.getReference()) ? model.getReference() : UUID.randomUUID().toString()); persist.setSource("internal"); persist.setSourceType(ReferenceSourceType.Internal); } else { diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerRepository.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerRepository.java index 214d32831..035949b8d 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerRepository.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerRepository.java @@ -7,6 +7,10 @@ import org.opencdmp.commonmodels.models.description.DescriptionModel; import org.opencdmp.commonmodels.models.dmp.DmpModel; import org.opencdmp.filetransformerbase.interfaces.FileTransformerClient; import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration; +import org.opencdmp.filetransformerbase.models.misc.DescriptionImportModel; +import org.opencdmp.filetransformerbase.models.misc.DmpImportModel; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDescriptionModel; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.slf4j.LoggerFactory; import org.springframework.core.ParameterizedTypeReference; import org.springframework.web.reactive.function.client.WebClient; @@ -30,9 +34,9 @@ public class FileTransformerRepository implements FileTransformerClient { } @Override - public DmpModel importDmp(FileEnvelopeModel fileEnvelope) { - logger.debug(new MapLogEntry("importDmp").And("fileEnvelope", fileEnvelope)); - return this.transformerClient.post().uri("/import/dmp").bodyValue(fileEnvelope) + public DmpModel importDmp(DmpImportModel dmpImportModel) { + logger.debug(new MapLogEntry("importDmp").And("fileEnvelope", dmpImportModel.getFile())); + return this.transformerClient.post().uri("/import/dmp").bodyValue(dmpImportModel) .exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(DmpModel.class)).block(); } @@ -44,9 +48,9 @@ public class FileTransformerRepository implements FileTransformerClient { } @Override - public DescriptionModel importDescription(FileEnvelopeModel fileEnvelope) { - logger.debug(new MapLogEntry("importDescription").And("fileEnvelope", fileEnvelope)); - return this.transformerClient.post().uri("/import/description").bodyValue(fileEnvelope) + public DescriptionModel importDescription(DescriptionImportModel descriptionImportModel) { + logger.debug(new MapLogEntry("importDescription").And("fileEnvelope", descriptionImportModel.getFile())); + return this.transformerClient.post().uri("/import/description").bodyValue(descriptionImportModel) .exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(DescriptionModel.class)).block(); } @@ -57,5 +61,19 @@ public class FileTransformerRepository implements FileTransformerClient { .exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(new ParameterizedTypeReference() {})).block(); } + @Override + public PreprocessingDmpModel preprocessingDmp(FileEnvelopeModel fileEnvelopeModel) { + logger.debug(new MapLogEntry("preprocessingDmp").And("fileEnvelope", fileEnvelopeModel)); + return this.transformerClient.post().uri("/preprocessing/dmp").bodyValue(fileEnvelopeModel) + .exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(PreprocessingDmpModel.class)).block(); + } + + @Override + public PreprocessingDescriptionModel preprocessingDescription(FileEnvelopeModel fileEnvelopeModel) { + logger.debug(new MapLogEntry("preprocessingDescription").And("fileEnvelope", fileEnvelopeModel)); + return this.transformerClient.post().uri("/preprocessing/description").bodyValue(fileEnvelopeModel) + .exchangeToMono(mono -> mono.statusCode().isError() ? mono.createException().flatMap(Mono::error) : mono.bodyToMono(PreprocessingDescriptionModel.class)).block(); + } + } diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java index 62a4c1304..d86214fb7 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java @@ -2,8 +2,9 @@ package org.opencdmp.service.filetransformer; import jakarta.xml.bind.JAXBException; import org.opencdmp.commonmodels.models.dmp.DmpModel; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.opencdmp.model.file.RepositoryFileFormat; -import org.springframework.web.multipart.MultipartFile; +import org.opencdmp.model.persist.DmpCommonModelConfig; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; @@ -23,6 +24,7 @@ public interface FileTransformerService { org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; - DmpModel importDmp(MultipartFile file, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException; + DmpModel importDmp(DmpCommonModelConfig dmpCommonModelConfig) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException; + PreprocessingDmpModel preprocessingDmp(UUID fileId, String repositoryId) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException; } diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java index 82487afb1..31d0d0d41 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java @@ -1,22 +1,26 @@ package org.opencdmp.service.filetransformer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeCacheService; import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction; import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import jakarta.xml.bind.JAXBException; -import org.apache.commons.io.FilenameUtils; import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.Permission; import org.opencdmp.commonmodels.models.FileEnvelopeModel; import org.opencdmp.commonmodels.models.description.DescriptionModel; +import org.opencdmp.commonmodels.models.descriptiotemplate.DescriptionTemplateModel; import org.opencdmp.commonmodels.models.dmp.DmpModel; +import org.opencdmp.commonmodels.models.dmpblueprint.DmpBlueprintModel; import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.StorageType; @@ -26,20 +30,28 @@ import org.opencdmp.commons.scope.user.UserScope; import org.opencdmp.commons.types.filetransformer.FileTransformerSourceEntity; import org.opencdmp.commons.types.tenantconfiguration.FileTransformerTenantConfigurationEntity; import org.opencdmp.convention.ConventionService; +import org.opencdmp.data.DescriptionTemplateEntity; +import org.opencdmp.data.StorageFileEntity; import org.opencdmp.data.TenantConfigurationEntity; import org.opencdmp.event.TenantConfigurationTouchedEvent; import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration; +import org.opencdmp.filetransformerbase.models.misc.DescriptionImportModel; +import org.opencdmp.filetransformerbase.models.misc.DmpImportModel; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.opencdmp.model.StorageFile; import org.opencdmp.model.builder.commonmodels.description.DescriptionCommonModelBuilder; +import org.opencdmp.model.builder.commonmodels.descriptiontemplate.DescriptionTemplateCommonModelBuilder; import org.opencdmp.model.builder.commonmodels.dmp.DmpCommonModelBuilder; +import org.opencdmp.model.builder.commonmodels.dmpblueprint.DmpBlueprintCommonModelBuilder; import org.opencdmp.model.description.Description; import org.opencdmp.model.dmp.Dmp; +import org.opencdmp.model.dmpblueprint.DmpBlueprint; import org.opencdmp.model.file.RepositoryFileFormat; +import org.opencdmp.model.persist.DescriptionCommonModelConfig; +import org.opencdmp.model.persist.DmpCommonModelConfig; import org.opencdmp.model.persist.StorageFilePersist; import org.opencdmp.model.tenantconfiguration.TenantConfiguration; -import org.opencdmp.query.DescriptionQuery; -import org.opencdmp.query.DmpQuery; -import org.opencdmp.query.TenantConfigurationQuery; +import org.opencdmp.query.*; import org.opencdmp.service.encryption.EncryptionService; import org.opencdmp.service.storage.StorageFileService; import org.opencdmp.service.tenant.TenantProperties; @@ -48,8 +60,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.event.EventListener; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.http.MediaType; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @@ -64,6 +78,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; +import java.util.stream.Collectors; @Service public class FileTransformerServiceImpl implements FileTransformerService { @@ -119,9 +134,12 @@ public class FileTransformerServiceImpl implements FileTransformerService { exchangeFilterFunctions.add(tokenExchangeFilterFunction); exchangeFilterFunctions.add(logRequest()); exchangeFilterFunctions.add(logResponse()); - }).codecs(codecs -> codecs - .defaultCodecs() - .maxInMemorySize(source.getMaxInMemorySizeInBytes()) + }).codecs(codecs -> { + codecs.defaultCodecs().maxInMemorySize(source.getMaxInMemorySizeInBytes()); + codecs.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper().registerModule(new JavaTimeModule()), MediaType.APPLICATION_JSON)); + codecs.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper().registerModule(new JavaTimeModule()), MediaType.APPLICATION_JSON)); + } + ).build()); this.clients.put(repositoryIdByTenant, repository); return repository; @@ -295,37 +313,105 @@ public class FileTransformerServiceImpl implements FileTransformerService { } @Override - public DmpModel importDmp(MultipartFile file, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException { + public DmpModel importDmp(DmpCommonModelConfig dmpCommonModelConfig) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException { this.authorizationService.authorizeForce(Permission.NewDmp); - if (file == null) return null; + StorageFileEntity tempFile = this.queryFactory.query(StorageFileQuery.class).disableTracking().authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpCommonModelConfig.getFileId()).first(); + + if (tempFile == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpCommonModelConfig.getFileId(), StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale())); //GK: First get the right client - FileTransformerRepository repository = this.getRepository(repositoryId); - if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); + FileTransformerRepository repository = this.getRepository(dmpCommonModelConfig.getRepositoryId()); + if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpCommonModelConfig.getRepositoryId(), FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); - String name = FilenameUtils.removeExtension(file.getOriginalFilename()); - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); - String mimeType = URLConnection.guessContentTypeFromName(file.getOriginalFilename()); + DmpBlueprintQuery dmpBlueprintQuery = this.queryFactory.query(DmpBlueprintQuery.class).disableTracking().authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpCommonModelConfig.getBlueprintId()); + DmpBlueprintModel dmpBlueprintModel = this.builderFactory.builder(DmpBlueprintCommonModelBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(dmpBlueprintQuery.first()); + if (dmpBlueprintModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{dmpCommonModelConfig.getBlueprintId(), DmpBlueprint.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + DmpImportModel dmpImportModel = new DmpImportModel(); + dmpImportModel.setBlueprintModel(dmpBlueprintModel); + + if (!this.conventionService.isListNullOrEmpty(dmpCommonModelConfig.getDescriptions())){ + List descriptionTemplateEntities = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpCommonModelConfig.getDescriptions().stream().map(x -> x.getTemplateId()).distinct().collect(Collectors.toList())).collect(); + + if (descriptionTemplateEntities == null) throw new MyApplicationException("Description Templates Not Exist!"); + + List descriptionImportModels = new ArrayList<>(); + for (DescriptionCommonModelConfig descriptionCommonModelConfig : dmpCommonModelConfig.getDescriptions()) { + DescriptionTemplateEntity descriptionTemplateEntity = descriptionTemplateEntities.stream().filter(x -> x.getId().equals(descriptionCommonModelConfig.getTemplateId())).findFirst().orElse(null); + if (descriptionTemplateEntity != null){ + DescriptionTemplateModel descriptionTemplateModel = this.builderFactory.builder(DescriptionTemplateCommonModelBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(descriptionTemplateEntity); + + DescriptionImportModel descriptionImportModel = new DescriptionImportModel(); + descriptionImportModel.setId(descriptionCommonModelConfig.getId()); + descriptionImportModel.setSectionId(descriptionCommonModelConfig.getSectionId()); + descriptionImportModel.setDescriptionTemplate(descriptionTemplateModel); + + descriptionImportModels.add(descriptionImportModel); + } + } + + dmpImportModel.setDescriptions(descriptionImportModels); + } + + String originalFileName = tempFile.getName() + (tempFile.getExtension().startsWith(".") ? "" : ".") + tempFile.getExtension(); + String mimeType = URLConnection.guessContentTypeFromName(originalFileName); FileEnvelopeModel fileEnvelope = new FileEnvelopeModel(); - fileEnvelope.setFile(file.getBytes()); + fileEnvelope.setFile(this.storageFileService.readAsBytesSafe(dmpCommonModelConfig.getFileId())); fileEnvelope.setMimeType(mimeType); - fileEnvelope.setFilename(name + (extension.startsWith(".") ? "" : ".") + extension); + fileEnvelope.setFilename(originalFileName); if (repository.getConfiguration() != null && repository.getConfiguration().isUseSharedStorage()){ StorageFilePersist storageFilePersist = new StorageFilePersist(); - storageFilePersist.setName(name); - storageFilePersist.setExtension(extension); + storageFilePersist.setName(tempFile.getName()); + storageFilePersist.setExtension(tempFile.getExtension()); storageFilePersist.setMimeType(mimeType); storageFilePersist.setOwnerId(this.userScope.getUserIdSafe()); storageFilePersist.setStorageType(StorageType.Transformer); - StorageFile storageFile = this.storageFileService.persistBytes(storageFilePersist, file.getBytes(), new BaseFieldSet(StorageFile._id, StorageFile._fileRef, StorageFile._mimeType, StorageFile._extension, StorageFile._name)); + StorageFile storageFile = this.storageFileService.persistBytes(storageFilePersist, fileEnvelope.getFile(), new BaseFieldSet(StorageFile._id, StorageFile._fileRef, StorageFile._mimeType, StorageFile._extension, StorageFile._name)); fileEnvelope.setFileRef(storageFile.getFileRef()); } - return repository.importDmp(fileEnvelope); + dmpImportModel.setFile(fileEnvelope); + + return repository.importDmp(dmpImportModel); + } + + @Override + public PreprocessingDmpModel preprocessingDmp(UUID fileId, String repositoryId) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException { + this.authorizationService.authorizeForce(Permission.NewDmp); + + StorageFileEntity tempFile = this.queryFactory.query(StorageFileQuery.class).disableTracking().authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(fileId).first(); + + if (tempFile == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{fileId, StorageFile.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + //GK: First get the right client + FileTransformerRepository repository = this.getRepository(repositoryId); + if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{repositoryId, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + String originalFileName = tempFile.getName() + (tempFile.getExtension().startsWith(".") ? "" : ".") + tempFile.getExtension(); + String mimeType = URLConnection.guessContentTypeFromName(originalFileName); + + FileEnvelopeModel fileEnvelope = new FileEnvelopeModel(); + fileEnvelope.setFile(this.storageFileService.readAsBytesSafe(fileId)); + fileEnvelope.setMimeType(mimeType); + fileEnvelope.setFilename(originalFileName); + + if (repository.getConfiguration() != null && repository.getConfiguration().isUseSharedStorage()){ + StorageFilePersist storageFilePersist = new StorageFilePersist(); + storageFilePersist.setName(tempFile.getName()); + storageFilePersist.setExtension(tempFile.getExtension()); + storageFilePersist.setMimeType(mimeType); + storageFilePersist.setOwnerId(this.userScope.getUserIdSafe()); + storageFilePersist.setStorageType(StorageType.Transformer); + + StorageFile storageFile = this.storageFileService.persistBytes(storageFilePersist, fileEnvelope.getFile(), new BaseFieldSet(StorageFile._id, StorageFile._fileRef, StorageFile._mimeType, StorageFile._extension, StorageFile._name)); + fileEnvelope.setFileRef(storageFile.getFileRef()); + } + + return repository.preprocessingDmp(fileEnvelope); } } diff --git a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java index 834c3a259..35a114b38 100644 --- a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java @@ -25,6 +25,7 @@ import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.convention.ConventionService; import org.opencdmp.data.*; import org.opencdmp.errorcode.ErrorThesaurusProperties; +import org.opencdmp.event.*; import org.opencdmp.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEvent; import org.opencdmp.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent; @@ -81,6 +82,7 @@ public class TenantServiceImpl implements TenantService { private final QueryFactory queryFactory; private final CurrentPrincipalResolver currentPrincipalResolver; private final ClaimExtractor claimExtractor; + private final EventBroker eventBroker; @Autowired @@ -91,7 +93,7 @@ public class TenantServiceImpl implements TenantService { BuilderFactory builderFactory, ConventionService conventionService, MessageSource messageSource, - ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor) { + ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor, EventBroker eventBroker) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -108,6 +110,7 @@ public class TenantServiceImpl implements TenantService { this.queryFactory = queryFactory; this.currentPrincipalResolver = currentPrincipalResolver; this.claimExtractor = claimExtractor; + this.eventBroker = eventBroker; } @Override @@ -155,6 +158,8 @@ public class TenantServiceImpl implements TenantService { this.autoAssignGlobalAdminsToNewTenant(data); } + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); + return this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Tenant._id), data); } @@ -178,6 +183,7 @@ public class TenantServiceImpl implements TenantService { tenantUserEntity.setCreatedAt(Instant.now()); tenantUserEntity.setUpdatedAt(Instant.now()); this.entityManager.persist(tenantUserEntity); + this.eventBroker.emit(new UserAddedToTenantEvent(tenantUserEntity.getUserId(), tenantUserEntity.getTenantId())); UserCredentialEntity userCredential = userCredentialEntities.stream().filter(x-> !this.conventionService.isNullOrEmpty(x.getExternalId()) && x.getUserId().equals(userId)).findFirst().orElse(null); if (userCredential == null) continue; @@ -193,12 +199,16 @@ public class TenantServiceImpl implements TenantService { item.setCreatedAt(Instant.now()); this.entityManager.persist(item); keycloakIdsToAddToTenantGroup.add(userCredential.getExternalId()); + + this.eventBroker.emit(new UserCredentialTouchedEvent(userCredential.getId(), userCredential.getExternalId())); } this.entityManager.flush(); for (UUID userId : existingItems.stream().map(UserRoleEntity::getUserId).distinct().toList()) { this.userTouchedIntegrationEventHandler.handle(userId); + this.eventBroker.emit(new UserTouchedEvent(userId)); + } this.entityManager.flush(); @@ -214,6 +224,8 @@ public class TenantServiceImpl implements TenantService { @Override public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting : {}", id); + TenantEntity data = this.entityManager.find(TenantEntity.class, id); + if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); this.authorizationService.authorizeForce(Permission.DeleteTenant); @@ -222,6 +234,8 @@ public class TenantServiceImpl implements TenantService { TenantRemovalIntegrationEvent tenantRemovalIntegrationEvent = new TenantRemovalIntegrationEvent(); tenantRemovalIntegrationEvent.setId(id); this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent); + + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); } diff --git a/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java index 822c4816e..df466dd1e 100644 --- a/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java @@ -33,7 +33,9 @@ import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.user.UserScope; import org.opencdmp.commons.types.actionconfirmation.MergeAccountConfirmationEntity; import org.opencdmp.commons.types.actionconfirmation.RemoveCredentialRequestEntity; -import org.opencdmp.commons.types.notification.*; +import org.opencdmp.commons.types.notification.DataType; +import org.opencdmp.commons.types.notification.FieldInfo; +import org.opencdmp.commons.types.notification.NotificationFieldData; import org.opencdmp.commons.types.reference.DefinitionEntity; import org.opencdmp.commons.types.user.AdditionalInfoEntity; import org.opencdmp.commons.types.usercredential.UserCredentialDataEntity; @@ -41,8 +43,7 @@ import org.opencdmp.convention.ConventionService; import org.opencdmp.data.*; import org.opencdmp.data.tenant.TenantScopedBaseEntity; import org.opencdmp.errorcode.ErrorThesaurusProperties; -import org.opencdmp.event.EventBroker; -import org.opencdmp.event.UserTouchedEvent; +import org.opencdmp.event.*; import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler; @@ -287,6 +288,8 @@ public class UserServiceImpl implements UserService { this.entityManager.reloadTenantFilters(); } this.userRemovalIntegrationEventHandler.handle(id); + + this.eventBroker.emit(new UserTouchedEvent(id)); } //endregion @@ -407,6 +410,8 @@ public class UserServiceImpl implements UserService { tenantUserEntity.setCreatedAt(Instant.now()); tenantUserEntity.setUpdatedAt(Instant.now()); this.entityManager.persist(tenantUserEntity); + + this.eventBroker.emit(new UserAddedToTenantEvent(tenantUserEntity.getUserId(), tenantUserEntity.getTenantId())); } this.entityManager.flush(); @@ -680,7 +685,9 @@ public class UserServiceImpl implements UserService { if (!newUser.getId().equals(userToBeMerge.getId())) { this.syncKeycloakRoles(newUser.getId()); } - + + this.eventBroker.emit(new UserTouchedEvent(newUser.getId())); + this.eventBroker.emit(new UserTouchedEvent(userToBeMerge.getId())); } private void syncKeycloakRoles(UUID userId) throws InvalidApplicationException { @@ -700,6 +707,8 @@ public class UserServiceImpl implements UserService { if (!this.conventionService.isNullOrEmpty(tenantCode)) this.keycloakService.addUserToTenantRoleGroup(userCredential.getExternalId(), tenantCode, userRole.getRole()); } } + + this.eventBroker.emit(new UserCredentialTouchedEvent(userCredential.getId(), userCredential.getExternalId())); } } finally { @@ -714,6 +723,8 @@ public class UserServiceImpl implements UserService { for (UserCredentialEntity userCredential : userCredentials) { userCredential.setUserId(newUser.getId()); this.entityManager.merge(userCredential); + + this.eventBroker.emit(new UserCredentialTouchedEvent(userCredential.getId(), userCredential.getExternalId())); } List userContacts = this.queryFactory.query(UserContactInfoQuery.class).userIds(oldUser.getId()).collect(); @@ -748,8 +759,10 @@ public class UserServiceImpl implements UserService { if (newTenantUsers.stream().anyMatch(x -> Objects.equals(x.getTenantId(), userTenantUser.getTenantId()))) { tenantUsersToDelete.add(userTenantUser); } else { + this.eventBroker.emit(new UserRemovedFromTenantEvent(userTenantUser.getUserId(), userTenantUser.getTenantId())); userTenantUser.setUserId(newUser.getId()); this.entityManager.merge(userTenantUser); + this.eventBroker.emit(new UserAddedToTenantEvent(userTenantUser.getUserId(), userTenantUser.getTenantId())); } } this.deleterFactory.deleter(TenantUserDeleter.class).delete(tenantUsersToDelete); @@ -881,6 +894,9 @@ public class UserServiceImpl implements UserService { this.keycloakService.removeFromAllGroups(userCredentialEntity.getExternalId()); this.addToDefaultUserGroups(userCredentialEntity.getExternalId()); + + this.eventBroker.emit(new UserCredentialTouchedEvent(userCredentialEntity.getId(), userCredentialEntity.getExternalId())); + this.eventBroker.emit(new UserTouchedEvent(userCredentialEntity.getUserId())); } private void addToDefaultUserGroups(String subjectId){ diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java index f2248a4d9..87b1ef9ea 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java @@ -28,6 +28,7 @@ import org.opencdmp.controllers.swagger.SwaggerHelpers; import org.opencdmp.controllers.swagger.annotation.Swagger400; import org.opencdmp.controllers.swagger.annotation.Swagger404; import org.opencdmp.controllers.swagger.annotation.SwaggerErrorResponses; +import org.opencdmp.filetransformerbase.models.misc.PreprocessingDmpModel; import org.opencdmp.model.DescriptionsToBeFinalized; import org.opencdmp.model.DmpUser; import org.opencdmp.model.DmpValidationResult; @@ -453,23 +454,40 @@ public class DmpController { return model; } - @PostMapping("json/import") - @Operation(summary = "Import a plan from an json file") + @PostMapping("json/preprocessing") + @Operation(summary = "preprocessing a plan from an json file") @Transactional - public Dmp importJson( - @RequestParam("file") MultipartFile file, - @RequestParam("label") String label, - @RequestParam("repositoryId") String repositoryId, - @RequestParam("format") String format, - @Parameter(name = "fields", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fields - ) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException { - logger.debug(new MapLogEntry("import json" + Dmp.class.getSimpleName()).And("transformerId", repositoryId).And("file", file).And("label", label)); + public PreprocessingDmpModel preprocessing( + @RequestParam("fileId") UUID fileId, + @RequestParam("repositoryId") String repositoryId + ) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("preprocessing dmp" + Dmp.class.getSimpleName()).And("transformerId", repositoryId).And("fileId", fileId)); - Dmp model = this.dmpService.importJson(file, label, repositoryId, format, fields); + PreprocessingDmpModel model = this.dmpService.preprocessingDmp(fileId, repositoryId); this.auditService.track(AuditableAction.Dmp_Import, Map.ofEntries( new AbstractMap.SimpleEntry("transformerId", repositoryId), - new AbstractMap.SimpleEntry("file", file), + new AbstractMap.SimpleEntry("fileId", fileId) + )); + + return model; + } + + @PostMapping("json/import") + @Operation(summary = "Import a plan from an json file") + @ValidationFilterAnnotation(validator = DmpCommonModelConfig.DmpCommonModelConfigValidator.ValidatorName, argumentName = "model") + @Transactional + public Dmp importJson( + @RequestBody DmpCommonModelConfig dmpCommonModelConfig, + @Parameter(name = "fields", description = SwaggerHelpers.Commons.fieldset_description, required = true) FieldSet fields + ) throws InvalidAlgorithmParameterException, JAXBException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException { + logger.debug(new MapLogEntry("import json" + Dmp.class.getSimpleName()).And("transformerId", dmpCommonModelConfig.getRepositoryId()).And("file id", dmpCommonModelConfig.getFileId()).And("label", dmpCommonModelConfig.getLabel())); + + Dmp model = this.dmpService.importJson(dmpCommonModelConfig, fields); + + this.auditService.track(AuditableAction.Dmp_Import, Map.ofEntries( + new AbstractMap.SimpleEntry("transformerId", dmpCommonModelConfig.getRepositoryId()), + new AbstractMap.SimpleEntry("file id", dmpCommonModelConfig.getFileId()), new AbstractMap.SimpleEntry("fields", fields) )); diff --git a/backend/web/src/main/java/org/opencdmp/interceptors/tenant/TenantByCodeCacheService.java b/backend/web/src/main/java/org/opencdmp/interceptors/tenant/TenantByCodeCacheService.java index 5175e38fd..4cbd538bf 100644 --- a/backend/web/src/main/java/org/opencdmp/interceptors/tenant/TenantByCodeCacheService.java +++ b/backend/web/src/main/java/org/opencdmp/interceptors/tenant/TenantByCodeCacheService.java @@ -1,8 +1,8 @@ package org.opencdmp.interceptors.tenant; +import gr.cite.tools.cache.CacheService; import org.opencdmp.convention.ConventionService; import org.opencdmp.event.TenantTouchedEvent; -import gr.cite.tools.cache.CacheService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @@ -26,7 +26,7 @@ public class TenantByCodeCacheService extends CacheService { + private final ConventionService conventionService; + public static class UserInterceptorCacheValue { public UserInterceptorCacheValue() { @@ -24,7 +28,7 @@ public class UserInterceptorCacheService extends CacheService externalProviderNames; public UUID getUserId() { - return userId; + return this.userId; } public void setUserId(UUID userId) { @@ -46,7 +50,7 @@ public class UserInterceptorCacheService extends CacheService getRoles() { - return roles; + return this.roles; } public void setRoles(List roles) { @@ -54,7 +58,7 @@ public class UserInterceptorCacheService extends CacheService getExternalProviderNames() { - return externalProviderNames; + return this.externalProviderNames; } public void setExternalProviderNames(List externalProviderNames) { @@ -70,10 +74,17 @@ public class UserInterceptorCacheService extends CacheService this.query(this.buildAutocompleteWithDefinitonLookup(null, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteWithDefinitonLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteWithDefinitonLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: DmpBlueprint) => item.label, + subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version, + titleFn: (item: DmpBlueprint) => item.label, + valueAssign: (item: DmpBlueprint) => item.id, + }; + + public buildAutocompleteWithDefinitonLookup(like?: string, excludedIds?: Guid[], ids?: Guid[], statuses?: DmpBlueprintStatus[]): DmpBlueprintLookup { + const lookup: DmpBlueprintLookup = new DmpBlueprintLookup(); + lookup.page = { size: 100, offset: 0 }; + if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } + if (ids && ids.length > 0) { lookup.ids = ids; } + lookup.isActive = [IsActive.Active]; + lookup.statuses = statuses; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label), + nameof(x => x.version), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.hasTemplates)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.descriptionTemplateGroupId)].join('.'), + ] + }; + lookup.order = { items: [nameof(x => x.label)] }; + lookup.versionStatuses = [DmpBlueprintVersionStatus.Previous, DmpBlueprintVersionStatus.Current]; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } + // // // UI Helpers 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 a3bc640f3..4f311f0d9 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -20,6 +20,7 @@ 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'; +import { DmpCommonModelConfig, PreprocessingDmpModel } from '@app/core/model/dmp/dmp-import'; @Injectable() export class DmpService { @@ -197,26 +198,25 @@ export class DmpService { catchError((error: any) => throwError(error)));; } - uploadJson(file: File, label: string, repositoryId: string, format: string, reqFields: string[] = []): Observable { - const url = `${this.apiBase}/json/import`; + preprocessingDmp(fileId: Guid, repositoryId: string): Observable { + const url = `${this.apiBase}/json/preprocessing`; const params = new BaseHttpParams(); params.interceptorContext = { excludedInterceptors: [InterceptorType.JSONContentType] }; const formData = new FormData(); - formData.append('file', file); - formData.append('label', label); + formData.append('fileId', fileId.toString()); formData.append('repositoryId', repositoryId); - formData.append('format', format); - - if (reqFields.length > 0){ - for (var i = 0; i < reqFields.length; i++) { - formData.append('field[]', reqFields[i]); - } - } - return this.http.post(url, formData, { params: params }).pipe(catchError((error: any) => throwError(error))); + return this.http.post(url, formData, { params: params }).pipe(catchError((error: any) => throwError(error))); + } + + uploadJson(item: DmpCommonModelConfig, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/json/import`; + + return this.http.post(url, item).pipe(catchError((error: any) => throwError(error))); + } // diff --git a/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts index e28c66d41..48146ff0e 100644 --- a/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component.ts @@ -79,8 +79,8 @@ export class StartNewDmpDialogComponent extends BaseComponent { }, (error) => this.onCallbackImportFail(error.error) ); - } else if (file?.type.includes('/json')){ - this.dmpService.uploadJson(result.fileList[0], result.dmpTitle, 'rda-file-transformer', 'json') + } else if (file?.type.includes('/json') && result.dmpCommonModelConfig){ + this.dmpService.uploadJson(result.dmpCommonModelConfig) .pipe(takeUntil(this._destroyed)) .subscribe( (complete) => { diff --git a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-common-model-config.editor.model.ts b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-common-model-config.editor.model.ts new file mode 100644 index 000000000..da52f393c --- /dev/null +++ b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-common-model-config.editor.model.ts @@ -0,0 +1,131 @@ +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; +import { Guid } from "@common/types/guid"; +import { DmpCommonModelConfig, PreprocessingDmpModel } from "@app/core/model/dmp/dmp-import"; +import { DescriptionCommonModelConfig, PreprocessingDescriptionModel } from "@app/core/model/description/description-import"; + +export class DmpImportRdaConfigEditorModel implements DmpCommonModelConfig{ + fileId: Guid; + label: string; + blueprintId: Guid; + repositoryId: string = 'rda-file-transformer'; + descriptions: DescriptionImportRdaConfigEditorModel[] = []; + + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor() { } + + fromModel(item: PreprocessingDmpModel, fileId: Guid): DmpImportRdaConfigEditorModel { + this.fileId = fileId; + if (item){ + this.label = item.label + '.json'; + if (item.preprocessingDescriptionModels?.length > 0) { + item.preprocessingDescriptionModels.forEach(x => { + this.descriptions.push(new DescriptionImportRdaConfigEditorModel(this.validationErrorModel).fromModel(x)); + }) + } + } + + + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + if (context == null) { context = this.createValidationContext(); } + + return this.formBuilder.group({ + fileId: [{ value: this.fileId, disabled: disabled }, context.getValidation('fileId').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + blueprintId: [{ value: this.blueprintId, disabled: disabled }, context.getValidation('blueprintId').validators], + repositoryId: [{ value: this.repositoryId, disabled: disabled }, context.getValidation('repositoryId').validators], + descriptions: this.formBuilder.array( + (this.descriptions ?? []).map( + (item, index) => item.buildForm({ + rootPath: `descriptions[${index}].`, + disabled: disabled + }) + ), context.getValidation('descriptions').validators + ), + }); + + + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + + baseValidationArray.push({ key: 'fileId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'fileId')] }); + baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseValidationArray.push({ key: 'blueprintId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprintId')] }); + baseValidationArray.push({ key: 'repositoryId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'repositoryId')] }); + baseValidationArray.push({ key: 'descriptions', validators: [BackendErrorValidator(this.validationErrorModel, 'descriptions')] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + +} + +export class DescriptionImportRdaConfigEditorModel implements DescriptionCommonModelConfig { + id: string; + sectionId: Guid; + templateId: Guid; + label: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: PreprocessingDescriptionModel): DescriptionImportRdaConfigEditorModel { + if (item) { + this.id = item.id; + this.label = item.label; + } + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DescriptionImportRdaConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], + sectionId: [{ value: this.sectionId, disabled: disabled }, context.getValidation('sectionId').validators], + templateId: [{ value: this.templateId, disabled: disabled }, context.getValidation('templateId').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'id', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}id`)] }); + baseValidationArray.push({ key: 'sectionId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}descriptionId`)] }); + baseValidationArray.push({ key: 'templateId', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}blueprintSectionId`)] }); + baseValidationArray.push({ key: 'label', validators: [] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} diff --git a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.html b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.html index 6c9e3038a..8b59ee4ad 100644 --- a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.html +++ b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.html @@ -9,8 +9,8 @@
- - + +
@@ -30,14 +30,42 @@
- + + + + + +
+ + {{ description.get('label').value }} + + + {{'DMP-UPLOAD.FIELDS.DESCRIPTION-TEMPLATE' | translate}} + + {{description.get('templateId').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + {{'DMP-UPLOAD.FIELDS.SECTION' | translate}} + + + + {{ section.label }} + + + + +
+
+
@@ -46,7 +74,7 @@
- +
diff --git a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.ts index 746643e21..65c1bc5c0 100644 --- a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.component.ts @@ -2,12 +2,22 @@ import { HttpClient } from '@angular/common/http'; import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { DescriptionTemplate } from '@app/core/model/description-template/description-template'; +import { DmpBlueprint, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; import { AnalyticsService } from '@app/core/services/matomo/analytics-service'; -import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { BaseComponent } from '@common/base/base.component'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, takeUntil } from 'rxjs/operators'; +import { DmpImportRdaConfigEditorModel } from './dmp-common-model-config.editor.model'; +import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; +import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service'; +import { FormService } from '@common/forms/form-service'; +import { DmpCommonModelConfig } from '@app/core/model/dmp/dmp-import'; +import { StorageFileService } from '@app/core/services/storage-file/storage-file.service'; +import { IsActive } from '@notification-service/core/enum/is-active.enum'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'dmp-upload-dialog', @@ -18,13 +28,17 @@ export class DmpUploadDialogComponent extends BaseComponent { dmpTitle: string; dmpBlueprints: any[] = []; files: File[] = []; + selectedBlueprintSections: DmpBlueprintDefinitionSection[]; + formGroup: UntypedFormGroup; - profilesAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { - filterFn: this.filterProfiles.bind(this), - initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), - displayFn: (item) => item['label'], - titleFn: (item) => item['label'], - subtitleFn: (item) => item['description'], + descriptionTemplateSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active], searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.descriptionTemplateService.query(this.descriptionTemplateService.buildDescriptionTempalteGroupAutocompleteLookup([IsActive.Active, IsActive.Inactive], null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: DescriptionTemplate) => item.label, + titleFn: (item: DescriptionTemplate) => item.label, + subtitleFn: (item: DescriptionTemplate) => item.description, + valueAssign: (item: DescriptionTemplate) => item.id, popupItemActionIcon: 'visibility' }; @@ -35,6 +49,13 @@ export class DmpUploadDialogComponent extends BaseComponent { private dialog: MatDialog, private httpClient: HttpClient, private analyticsService: AnalyticsService, + private formService: FormService, + public descriptionTemplateService: DescriptionTemplateService, + public dmpBlueprintService: DmpBlueprintService, + private dmpService: DmpService, + private storageFileStorage: StorageFileService, + private uiNotificationService: UiNotificationService, + private language: TranslateService, @Inject(MAT_DIALOG_DATA) public data: any, ) { @@ -58,10 +79,28 @@ export class DmpUploadDialogComponent extends BaseComponent { this.data.success = true; this.data.dmpTitle = this.dmpTitle; this.data.dmpBlueprints = this.dmpBlueprints; + + if (this.files.length > 0 && this.files[0].type.includes('/json') && this.formGroup){ + this.formService.removeAllBackEndErrors(this.formGroup); + this.formService.touchAllFormFields(this.formGroup); + if (this.formGroup.valid){ + this.data.dmpCommonModelConfig = this.formService.getValue(this.formGroup.value) as DmpCommonModelConfig; + this.data.dmpCommonModelConfig.file = this.files[0]; + } else { + return; + } + } this.dialogRef.close(this.data); } + disableConfirmButton(){ + if (this.data.fileList.length === 0 || this.files.length === 0) return true; + if (this.files.length > 0 && this.files[0].type.includes('/json') && this.formGroup == null) return true; + return false; + } + uploadFile(event) { + this.formGroup = null; const fileList: FileList = event.target.files this.data.fileList = fileList; if (this.data.fileList.length > 0) { @@ -71,6 +110,28 @@ export class DmpUploadDialogComponent extends BaseComponent { this.files.splice(0, 1); } this.files.push(...event.target.files); + + if (this.files.length > 0 && this.files[0].type.includes('/json')){ + this.storageFileStorage.uploadTempFiles(fileList[0]) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (storageFile) => { + if (storageFile.length >0 ){ + this.dmpService.preprocessingDmp(storageFile[0].id, 'rda-file-transformer') + .pipe(takeUntil(this._destroyed)) + .subscribe( + (preprocessingData) => { + this.formGroup = new DmpImportRdaConfigEditorModel().fromModel(preprocessingData, storageFile[0].id,).buildForm(); + }, + (error) => this.onCallbackEror(error.error) + ); + } + + }, + (error) => this.onCallbackEror(error.error) + ); + + } } selectFile(event) { @@ -88,19 +149,25 @@ export class DmpUploadDialogComponent extends BaseComponent { onRemove(event) { this.files.splice(0, 1); this.dmpTitle = null; + this.formGroup = null; } - filterProfiles(value: string): Observable { - // TODO refactor - // const request = new DataTableRequest(null, null, { fields: ['+label'] }); - // const criteria = new DatasetProfileCriteria(); - // criteria.like = value; - // request.criteria = criteria; - // return this._service.searchDmpBlueprints(request); - return null; + selectedBlueprintChanged(item: DmpBlueprint): void{ + this.selectedBlueprintSections = item.definition?.sections?.filter(x => x.hasTemplates) || null; + if (this.formGroup){ + const descriptionsFormArray = this.formGroup.get('descriptions') as UntypedFormArray; + descriptionsFormArray.controls.forEach( control =>{ + control.get('sectionId').patchValue(null); + }) + } } hasFile(): boolean { return this.files && this.files.length > 0; } + + private onCallbackEror(error: any) { + this.uiNotificationService.snackBarNotification(this.language.instant(error.error), SnackBarNotificationLevel.Error); + this.close(); + } } diff --git a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.module.ts b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.module.ts index 5f28e6c37..09b4ee30a 100644 --- a/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.module.ts +++ b/dmp-frontend/src/app/ui/dmp/new/upload-dialogue/dmp-upload-dialog.module.ts @@ -4,12 +4,16 @@ import { CommonFormsModule } from '@common/forms/common-forms.module'; import { CommonUiModule } from '@common/ui/common-ui.module'; import { NgxDropzoneModule } from 'ngx-dropzone'; import { DmpUploadDialogComponent } from './dmp-upload-dialog.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; @NgModule({ imports: [ CommonUiModule, CommonFormsModule, FormattingModule, + ReactiveFormsModule, + AutoCompleteModule, NgxDropzoneModule ], declarations: [ diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 07bc4bd95..7ebf0d37d 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -699,6 +699,14 @@ "DMP-UPLOAD": { "TITLE": "Import Plan", "UPLOAD-SUCCESS": "Import was Successful", + "ANALYZING-FILE": "Analyzing file", + "FIELDS": { + "BLUEPRINT-PLACEHOLDER": "Select Plan Blueprint", + "DESCRIPTION-TEMPLATE": "Description Template", + "DESCRIPTION-TEMPLATE-PLACHOLDER": "Select description template", + "SECTION": "Plan Section", + "SECTION-PLACEHOLDER": "Select plan section" + }, "ACTIONS": { "IMPORT": "Import", "CANCEL": "Cancel" diff --git a/dmp-frontend/src/common/forms/reactive-asterisk-directive.ts b/dmp-frontend/src/common/forms/reactive-asterisk-directive.ts index 0b253f4d3..8aff7f8db 100644 --- a/dmp-frontend/src/common/forms/reactive-asterisk-directive.ts +++ b/dmp-frontend/src/common/forms/reactive-asterisk-directive.ts @@ -1,4 +1,4 @@ -import { AfterContentChecked, Directive } from "@angular/core"; +import { AfterContentChecked, Directive, Optional } from "@angular/core"; import { AbstractControl } from "@angular/forms"; import { MatFormField } from "@angular/material/form-field"; import { MatInput } from "@angular/material/input"; @@ -15,9 +15,10 @@ import { MarkedValidatorFn } from "./validation/custom-validator"; }) export class ReactiveAsteriskDirective implements AfterContentChecked { private readonly requiredValidatornames = ['RequiredWithVisibilityRulesValidator', 'required']; - constructor(private matFormField: MatFormField) { } + constructor(@Optional() private matFormField: MatFormField) { } ngAfterContentChecked() { + if (!this.matFormField) return; const ctrl = this.matFormField._control; const abstractControl = ctrl?.ngControl?.control; const validators = (abstractControl as AbstractControl & { _rawValidators: MarkedValidatorFn[] })?._rawValidators; diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java index 660677b06..33ffb5a2a 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java @@ -56,8 +56,6 @@ public class TenantByCodeCacheService extends CacheService { + private final ConventionService conventionService; public static class UserInterceptorCacheValue { @@ -41,11 +44,18 @@ public class UserInterceptorCacheService extends CacheService ids) throws InvalidApplicationException { @@ -69,6 +71,7 @@ public class TenantDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new TenantTouchedEvent(item.getId(), item.getCode())); } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java index faf322de7..64bf82a63 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java @@ -3,6 +3,8 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.TenantUserEntity; +import gr.cite.notification.event.EventBroker; +import gr.cite.notification.event.UserRemovedFromTenantEvent; import gr.cite.notification.query.TenantUserQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -28,14 +30,16 @@ public class TenantUserDeleter implements Deleter { private final TenantEntityManager entityManager; private final QueryFactory queryFactory; + private final EventBroker eventBroker; @Autowired public TenantUserDeleter( TenantEntityManager entityManager, - QueryFactory queryFactory + QueryFactory queryFactory, EventBroker eventBroker ) { this.entityManager = entityManager; this.queryFactory = queryFactory; + this.eventBroker = eventBroker; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { @@ -66,6 +70,7 @@ public class TenantUserDeleter implements Deleter { logger.trace("updating item"); this.entityManager.merge(item); logger.trace("updated item"); + this.eventBroker.emit(new UserRemovedFromTenantEvent(item.getUserId(), item.getTenantId())); } } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/QueueInboxQuery.java b/notification-service/notification/src/main/java/gr/cite/notification/query/QueueInboxQuery.java index fc9155e65..51a365de4 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/query/QueueInboxQuery.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/query/QueueInboxQuery.java @@ -214,7 +214,7 @@ public class QueueInboxQuery extends QueryBase { queryContext.CriteriaBuilder.lessThanOrEqualTo(queryContext.Root.get(QueueInboxEntity._retryCount), this.retryThreshold))); } - if (predicates.size() > 0) { + if (!predicates.isEmpty()) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); return queryContext.CriteriaBuilder.and(predicatesArray); } else { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java index 5e7953c40..7441361ae 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java @@ -8,6 +8,8 @@ import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.TenantEntity; import gr.cite.notification.data.TenantEntityManager; +import gr.cite.notification.event.EventBroker; +import gr.cite.notification.event.TenantTouchedEvent; import gr.cite.notification.model.Tenant; import gr.cite.notification.model.builder.TenantBuilder; import gr.cite.notification.model.deleter.TenantDeleter; @@ -23,6 +25,8 @@ import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; @@ -45,17 +49,21 @@ public class TenantServiceImpl implements TenantService { private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; + private final EventBroker eventBroker; + private final MessageSource messageSource; public TenantServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, TenantEntityManager entityManager, - BuilderFactory builderFactory) { + BuilderFactory builderFactory, EventBroker eventBroker, MessageSource messageSource) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; this.conventionService = conventionService; this.entityManager = entityManager; this.builderFactory = builderFactory; + this.eventBroker = eventBroker; + this.messageSource = messageSource; } @Override @@ -91,16 +99,21 @@ public class TenantServiceImpl implements TenantService { this.entityManager.flush(); + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); return this.builderFactory.builder(TenantBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, Tenant._id), data); } @Override public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { logger.debug("deleting Tenant: {}", id); - this.authorizationService.authorizeForce(Permission.DeleteTenant); + TenantEntity data = this.entityManager.find(TenantEntity.class, id); + if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); + this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id)); + + this.eventBroker.emit(new TenantTouchedEvent(data.getId(), data.getCode())); } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java index b07727761..4c5a78b91 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java @@ -10,6 +10,10 @@ import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.types.user.AdditionalInfoEntity; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.*; +import gr.cite.notification.event.EventBroker; +import gr.cite.notification.event.UserAddedToTenantEvent; +import gr.cite.notification.event.UserCredentialTouchedEvent; +import gr.cite.notification.event.UserTouchedEvent; import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; import gr.cite.notification.model.Tenant; import gr.cite.notification.model.User; @@ -63,6 +67,7 @@ public class UserServiceImpl implements UserService { private final TenantScope tenantScope; private final MessageSource messageSource; + private final EventBroker eventBroker; private final JsonHandlingService jsonHandlingService; @@ -71,7 +76,7 @@ public class UserServiceImpl implements UserService { DeleterFactory deleterFactory, ConventionService conventionService, TenantEntityManager entityManager, - BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource, + BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource, EventBroker eventBroker, JsonHandlingService jsonHandlingService) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -81,7 +86,8 @@ public class UserServiceImpl implements UserService { this.queryFactory = queryFactory; this.tenantScope = tenantScope; this.messageSource = messageSource; - this.jsonHandlingService = jsonHandlingService; + this.eventBroker = eventBroker; + this.jsonHandlingService = jsonHandlingService; } @Override @@ -126,6 +132,7 @@ public class UserServiceImpl implements UserService { this.persistTenantUser(model.getTenantUsers(), data.getId()); this.entityManager.flush(); + this.eventBroker.emit(new UserTouchedEvent(data.getId())); return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data); } @@ -146,6 +153,7 @@ public class UserServiceImpl implements UserService { this.authorizationService.authorizeForce(Permission.DeleteUser); this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); + this.eventBroker.emit(new UserTouchedEvent(id)); } private void persistContactInfo(List models, UUID userId) throws InvalidApplicationException { @@ -196,6 +204,7 @@ public class UserServiceImpl implements UserService { data.setCreatedAt(Instant.now()); data.setUpdatedAt(Instant.now()); entityManager.persist(data); + this.eventBroker.emit(new UserCredentialTouchedEvent(data.getId(), data.getExternalId())); } updatedCreatedIds.add(data.getId()); } @@ -233,6 +242,7 @@ public class UserServiceImpl implements UserService { data.setUpdatedAt(Instant.now()); data.setIsActive(IsActive.Active); entityManager.persist(data); + this.eventBroker.emit(new UserAddedToTenantEvent(data.getUserId(), data.getTenantId())); } finally { this.tenantScope.removeTempTenant(this.entityManager); }