From 8f7a3cf76849967fc270314796808c79fade4abe Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Wed, 31 Jan 2024 15:23:41 +0200 Subject: [PATCH] Resolving issues with notification service user integration (on user creation) --- .../UserTouchedIntegrationEvent.java | 10 ++ ...serTouchedIntegrationEventHandlerImpl.java | 5 + .../src/main/resources/config/permissions.yml | 1 + .../inbox/InboxRepositoryImpl.java | 5 +- .../NotifyIntegrationEventHandlerImpl.java | 2 +- ...serRemovalIntegrationEventHandlerImpl.java | 4 +- .../UserTouchedIntegrationEvent.java | 166 ++++++++++++++++-- ...serTouchedIntegrationEventHandlerImpl.java | 19 +- .../UserTouchedIntegrationEventPersist.java | 45 ++--- .../service/user/UserService.java | 3 +- .../service/user/UserServiceImpl.java | 113 ++++++++++-- 11 files changed, 305 insertions(+), 68 deletions(-) diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java index 44a051d90..9bd56130b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java @@ -14,6 +14,8 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private String name; + private String subjectId; + private UserProfile profile; private List userContactInfo; @@ -42,6 +44,14 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.name = name; } + public String getSubjectId() { + return subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + public UserProfile getProfile() { return profile; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java index 5c40bdce2..b80078437 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -62,10 +62,15 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr List userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId) .collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value)); + List userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId) + .collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId)); + UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent(); event.setId(userId); event.setTenant(null); //TODO event.setName(user.getName()); + if (userCredentialEntities != null && !userCredentialEntities.isEmpty()) + event.setSubjectId(userCredentialEntities.getFirst().getExternalId()); event.setProfile(new UserTouchedIntegrationEvent.UserProfile()); AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo()); if (definition != null) { diff --git a/notification-service/notification-web/src/main/resources/config/permissions.yml b/notification-service/notification-web/src/main/resources/config/permissions.yml index e93f4f3af..d7247f8d8 100644 --- a/notification-service/notification-web/src/main/resources/config/permissions.yml +++ b/notification-service/notification-web/src/main/resources/config/permissions.yml @@ -170,6 +170,7 @@ permissions: BrowseInAppNotification: roles: - Admin + - User clients: [ ] allowAnonymous: false allowAuthenticated: false diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java index 811725fbe..148da2082 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java @@ -323,6 +323,7 @@ public class InboxRepositoryImpl implements InboxRepository { private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { IntegrationEventHandler handler; + logger.debug("Processing message with routing key '{}'", routingKey); if (this.routingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class); else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic())) @@ -333,8 +334,10 @@ public class InboxRepositoryImpl implements InboxRepository { handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic())) handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); - else + else { + logger.error("No handler found for message routing key '{}'. Discarding.", routingKey); handler = null; + } if (handler == null) return EventProcessingStatus.Discard; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java index d08ec5ef7..8a5b37d03 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java @@ -67,7 +67,7 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent if (event.getUserId() == null) { throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale())); } - logger.debug("Handling NotifyIntegrationEvent"); + logger.debug("Handling {}", NotifyIntegrationEvent.class.getSimpleName()); NotificationPersist model = new NotificationPersist(); model.setType(event.getNotificationType()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index 791082ba8..326325373 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -67,6 +67,8 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr if (event.getUserId() == null) throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale())); + logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName()); + EntityManager entityManager = null; EntityTransaction transaction = null; try (FakeRequestScope ignored = new FakeRequestScope()) { @@ -134,6 +136,6 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); } - return null; + return EventProcessingStatus.Success; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java index 113588fbf..daa3d9a54 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java @@ -1,18 +1,43 @@ package gr.cite.notification.integrationevent.inbox.usertouched; +import gr.cite.notification.common.enums.ContactInfoType; +import gr.cite.notification.common.validation.BaseValidator; +import gr.cite.notification.convention.ConventionService; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.integrationevent.TrackedEvent; +import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.validation.specification.Specification; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Scope; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Component; +import java.util.Arrays; +import java.util.List; import java.util.UUID; public class UserTouchedIntegrationEvent extends TrackedEvent { private UUID id; + public static final String _id = "id"; + private UUID tenant; - private String firstName; + private String name; - private String lastName; + public static final String _name = "name"; + + public static final int _nameLength = 200; + + private String subjectId; + + public static final String _subjectId = "subjectId"; + + private UserProfile profile; + + private List userContactInfo; public UUID getId() { return id; @@ -30,19 +55,140 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.tenant = tenant; } - public String getFirstName() { - return firstName; + public String getName() { + return name; } - public void setFirstName(String firstName) { - this.firstName = firstName; + public void setName(String name) { + this.name = name; } - public String getLastName() { - return lastName; + public String getSubjectId() { + return subjectId; } - public void setLastName(String lastName) { - this.lastName = lastName; + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + + public UserProfile getProfile() { + return profile; + } + + public void setProfile(UserProfile profile) { + this.profile = profile; + } + + public List getUserContactInfo() { + return userContactInfo; + } + + public void setUserContactInfo(List userContactInfo) { + this.userContactInfo = userContactInfo; + } + + public static class UserProfile { + + private String timezone; + + private String culture; + + private String language; + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } + + public String getCulture() { + return culture; + } + + public void setCulture(String culture) { + this.culture = culture; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + } + + public static class UserContactInfo { + + private ContactInfoType type; + + private String value; + + private int ordinal; + + public ContactInfoType getType() { + return type; + } + + public void setType(ContactInfoType type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public int getOrdinal() { + return ordinal; + } + + public void setOrdinal(int ordinal) { + this.ordinal = ordinal; + } + } + + @Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return UserTouchedIntegrationEvent.class; + } + + @Override + protected List specifications(UserTouchedIntegrationEvent item) { + return Arrays.asList( + this.spec() + .iff(() -> !this.isNull(item.getId())) + .must(() -> this.isValidGuid(item.getId())) + .failOn(UserTouchedIntegrationEvent._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._id}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isEmpty(item.getName())) + .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getName())) + .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength)) + .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isEmpty(item.getSubjectId())) + .failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())) + ); + } } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java index 16ca28924..4b0bafa27 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -10,7 +10,6 @@ import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; import gr.cite.notification.model.Tenant; -import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; import gr.cite.notification.query.TenantQuery; import gr.cite.notification.service.user.UserService; import gr.cite.tools.auditing.AuditService; @@ -40,8 +39,8 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr protected final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; - private final ValidatorFactory validatorFactory; + private final ValidatorFactory validatorFactory; public UserTouchedIntegrationEventHandlerImpl( JsonHandlingService jsonHandlingService, @@ -58,11 +57,9 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr if (event == null) return EventProcessingStatus.Error; - UserTouchedIntegrationEventPersist model = new UserTouchedIntegrationEventPersist(); - model.setId(event.getId()); - model.setFirstName(event.getFirstName()); - model.setLastName(event.getLastName()); - this.validatorFactory.validator(UserTouchedIntegrationEventPersist.UserTouchedIntegrationEventPersistValidator.class).validateForce(model); + logger.debug("Handling {}", UserTouchedIntegrationEvent.class.getSimpleName()); + + this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event); EntityManager entityManager = null; EntityTransaction transaction = null; @@ -96,12 +93,12 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr try { UserService userService = this.applicationContext.getBean(UserService.class); - userService.persist(model, null); + userService.persist(event, null); AuditService auditService = this.applicationContext.getBean(AuditService.class); auditService.track(AuditableAction.User_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", model) + new AbstractMap.SimpleEntry("model", event) )); transaction.commit(); @@ -111,8 +108,6 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr } finally { currentPrincipalResolver.pop(); } - - transaction.commit(); } catch (OptimisticLockException ex) { // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); @@ -129,6 +124,6 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); } - return null; + return EventProcessingStatus.Success; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/persist/UserTouchedIntegrationEventPersist.java b/notification-service/notification/src/main/java/gr/cite/notification/model/persist/UserTouchedIntegrationEventPersist.java index f77a1fb41..952eee89b 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/persist/UserTouchedIntegrationEventPersist.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/persist/UserTouchedIntegrationEventPersist.java @@ -4,6 +4,7 @@ import gr.cite.notification.common.validation.BaseValidator; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.errorcode.ErrorThesaurusProperties; +import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; import gr.cite.tools.validation.specification.Specification; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; @@ -20,14 +21,11 @@ public class UserTouchedIntegrationEventPersist { private UUID id; public static final String _id = "id"; - private String firstName; - public static final String _firstName = "firstName"; - public static final int _firstNameLength = 200; + private String name; - private String lastName; - public static final String _lastName = "lastName"; - public static final int _lastNameLength = 200; + public static final String _name = "name"; + public static final int _nameLength = 200; public UUID getId() { return id; @@ -37,20 +35,12 @@ public class UserTouchedIntegrationEventPersist { this.id = id; } - public String getFirstName() { - return firstName; + public String getName() { + return name; } - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; + public void setName(String name) { + this.name = name; } @Component(UserTouchedIntegrationEventPersist.UserTouchedIntegrationEventPersistValidator.ValidatorName) @@ -77,21 +67,14 @@ public class UserTouchedIntegrationEventPersist { this.spec() .iff(() -> !this.isNull(item.getId())) .must(() -> this.isValidGuid(item.getId())) - .failOn(UserTouchedIntegrationEventPersist._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._id}, LocaleContextHolder.getLocale())), + .failOn(UserTouchedIntegrationEvent._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._id}, LocaleContextHolder.getLocale())), this.spec() - .must(() -> !this.isEmpty(item.getFirstName())) - .failOn(UserTouchedIntegrationEventPersist._firstName).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._firstName}, LocaleContextHolder.getLocale())), + .must(() -> !this.isEmpty(item.getName())) + .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._name}, LocaleContextHolder.getLocale())), this.spec() - .iff(() -> !this.isEmpty(item.getFirstName())) - .must(() -> this.lessEqualLength(item.getFirstName(), UserTouchedIntegrationEventPersist._firstNameLength)) - .failOn(UserTouchedIntegrationEventPersist._firstName).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._firstName}, LocaleContextHolder.getLocale())), - this.spec() - .must(() -> !this.isEmpty(item.getLastName())) - .failOn(UserTouchedIntegrationEventPersist._lastName).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._lastName}, LocaleContextHolder.getLocale())), - this.spec() - .iff(() -> !this.isEmpty(item.getLastName())) - .must(() -> this.lessEqualLength(item.getLastName(), UserTouchedIntegrationEventPersist._lastNameLength)) - .failOn(UserTouchedIntegrationEventPersist._lastName).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._lastName}, LocaleContextHolder.getLocale())) + .iff(() -> !this.isEmpty(item.getName())) + .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEvent._nameLength)) + .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._name}, LocaleContextHolder.getLocale())) ); } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java index 088b95202..d08b9cfab 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java @@ -1,6 +1,7 @@ package gr.cite.notification.service.user; import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; import gr.cite.notification.model.User; import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; import gr.cite.tools.exception.MyApplicationException; @@ -14,7 +15,7 @@ import java.util.UUID; public interface UserService { - User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException; + User persist(UserTouchedIntegrationEvent model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; 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 5b8318aea..13fd51a13 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 @@ -8,13 +8,17 @@ import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.convention.ConventionService; +import gr.cite.notification.data.UserContactInfoEntity; +import gr.cite.notification.data.UserCredentialEntity; import gr.cite.notification.data.UserEntity; +import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; import gr.cite.notification.model.User; import gr.cite.notification.model.builder.UserBuilder; import gr.cite.notification.model.deleter.UserDeleter; -import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.notification.query.UserContactInfoQuery; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; @@ -24,16 +28,13 @@ import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; import org.slf4j.LoggerFactory; -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; import java.time.Instant; -import java.util.EnumSet; -import java.util.List; -import java.util.UUID; +import java.util.*; @Service public class UserServiceImpl implements UserService { @@ -50,23 +51,32 @@ public class UserServiceImpl implements UserService { private final BuilderFactory builderFactory; + private final QueryFactory queryFactory; + + private final JsonHandlingService jsonHandlingService; + public UserServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, EntityManager entityManager, - BuilderFactory builderFactory) { + BuilderFactory builderFactory, + QueryFactory queryFactory, + JsonHandlingService jsonHandlingService) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; this.conventionService = conventionService; this.entityManager = entityManager; this.builderFactory = builderFactory; + this.queryFactory = queryFactory; + this.jsonHandlingService = jsonHandlingService; } @Override - public User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException { + @Transactional + public User persist(UserTouchedIntegrationEvent model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException { logger.debug(new MapLogEntry("persisting user").And("model", model).And("fields", fields)); - this.authorizationService.authorizeAtLeastOneForce(model.getId() != null ? List.of(new OwnedResource(model.getId())) : null, Permission.EditUser); +// this.authorizationService.authorizeAtLeastOneForce(model.getId() != null ? List.of(new OwnedResource(model.getId())) : null, Permission.EditUser); Boolean isValid = this.conventionService.isValidGuid(model.getId()); @@ -76,18 +86,73 @@ public class UserServiceImpl implements UserService { if (data == null) { data = new UserEntity(); data.setId(model.getId()); - data.setName(model.getFirstName() + " " + model.getLastName()); + data.setName(model.getName()); + data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile())); data.setCreatedAt(Instant.now()); data.setUpdatedAt(Instant.now()); data.setIsActive(IsActive.Active); this.entityManager.persist(data); + + for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { + this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId())); + } + + this.entityManager.persist(this.buildUserCredentialEntityFromEventData(model)); } else { - data.setName(model.getFirstName() + " " + model.getLastName()); + data.setName(model.getName()); + data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile())); data.setUpdatedAt(Instant.now()); data.setIsActive(IsActive.Active); this.entityManager.merge(data); + + List contactInfoList = this.queryFactory.query(UserContactInfoQuery.class).userIds(model.getId()).collect(); + + List updatedContacts = new ArrayList<>(); + for (UserContactInfoEntity c : contactInfoList) { + boolean updated = false; + + //Contact found on the existing, update it + for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { + if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) { + c.setUserId(model.getId()); + c.setType(c.getType()); + c.setValue(c.getValue()); + c.setOrdinal(c.getOrdinal()); + c.setUpdatedAt(Instant.now()); + c.setIsActive(IsActive.Active); + + this.entityManager.merge(c); + + updatedContacts.add(c); + updated = true; + break; + } + } + + //Contact was not found on the event data, remove it + if (!updated) { + c.setIsActive(IsActive.Inactive); + this.entityManager.merge(c); + } + } + + for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { + boolean updated = false; + for (UserContactInfoEntity c : updatedContacts) { + if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) { + updated = true; + break; + } + } + + //New contact detected since it came with the event data and was not existing in the database, add it + if (!updated) { + this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId())); + } + } + } } else { throw new MyApplicationException("Not valid user id"); @@ -107,4 +172,30 @@ public class UserServiceImpl implements UserService { this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); } + private UserContactInfoEntity buildContactInfoEntityFromEventData(UserTouchedIntegrationEvent.UserContactInfo eventC, UUID userId) { + UserContactInfoEntity contactInfo = new UserContactInfoEntity(); + contactInfo.setId(UUID.randomUUID()); + contactInfo.setUserId(userId); + contactInfo.setType(eventC.getType()); + contactInfo.setValue(eventC.getValue()); + contactInfo.setOrdinal(eventC.getOrdinal()); + contactInfo.setCreatedAt(Instant.now()); + contactInfo.setUpdatedAt(Instant.now()); + contactInfo.setIsActive(IsActive.Active); + + return contactInfo; + } + + private UserCredentialEntity buildUserCredentialEntityFromEventData(UserTouchedIntegrationEvent event) { + UserCredentialEntity credentialEntity = new UserCredentialEntity(); + credentialEntity.setId(UUID.randomUUID()); + credentialEntity.setUserId(event.getId()); + credentialEntity.setExternalId(event.getSubjectId()); + credentialEntity.setCreatedAt(Instant.now()); + credentialEntity.setUpdatedAt(Instant.now()); + credentialEntity.setIsActive(IsActive.Active); + + return credentialEntity; + } + }