From 2fb387825d3a5598cfb8b3c721ba577e088ba345 Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Fri, 12 Jan 2024 09:47:31 +0200 Subject: [PATCH 1/4] Notification service inbox event handlers --- .../notification/audit/AuditableAction.java | 10 + .../common/enums/NotificationContactType.java | 33 +- .../integrationevent/AppRabbitConfigurer.java | 48 +++ .../IntegrationEventContextImpl.java | 24 ++ .../integrationevent/TrackedEvent.java | 18 + .../inbox/ConsistencyHandler.java | 7 + .../inbox/ConsistencyPredicates.java | 5 + .../inbox/EventProcessingStatus.java | 8 + .../inbox/InboxPrincipal.java | 60 +++ .../inbox/InboxProperties.java | 67 ++++ .../inbox/InboxRepositoryImpl.java | 365 ++++++++++++++++++ .../inbox/IntegrationEventHandler.java | 7 + .../inbox/IntegrationEventProperties.java | 25 ++ .../notify/NotifyConsistencyHandler.java | 47 +++ .../notify/NotifyConsistencyPredicates.java | 45 +++ .../inbox/notify/NotifyIntegrationEvent.java | 80 ++++ .../notify/NotifyIntegrationEventHandler.java | 7 + .../NotifyIntegrationEventHandlerImpl.java | 146 +++++++ .../TenantRemovalConsistencyHandler.java | 26 ++ .../TenantRemovalConsistencyPredicates.java | 23 ++ .../TenantRemovalIntegrationEvent.java | 19 + .../TenantRemovalIntegrationEventHandler.java | 7 + ...antRemovalIntegrationEventHandlerImpl.java | 111 ++++++ .../TenantTouchedIntegrationEvent.java | 29 ++ .../TenantTouchedIntegrationEventHandler.java | 7 + ...antTouchedIntegrationEventHandlerImpl.java | 103 +++++ .../UserRemovalConsistencyHandler.java | 25 ++ .../UserRemovalConsistencyPredicates.java | 23 ++ .../UserRemovalIntegrationEvent.java | 28 ++ .../UserRemovalIntegrationEventHandler.java | 7 + ...serRemovalIntegrationEventHandlerImpl.java | 139 +++++++ .../UserTouchedIntegrationEvent.java | 48 +++ .../UserTouchedIntegrationEventHandler.java | 7 + ...serTouchedIntegrationEventHandlerImpl.java | 129 +++++++ .../outbox/OutboxProperties.java | 95 +++++ .../TenantTouchedIntegrationEventPersist.java | 37 ++ .../service/tenant/TenantService.java | 23 ++ .../service/tenant/TenantServiceImpl.java | 51 +++ .../service/user/UserService.java | 21 + .../service/user/UserServiceImpl.java | 51 +++ dmp-backend/notification-service/pom.xml | 24 ++ 41 files changed, 2018 insertions(+), 17 deletions(-) create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/IntegrationEventContextImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/TrackedEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyPredicates.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/EventProcessingStatus.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyPredicates.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyPredicates.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/model/persist/TenantTouchedIntegrationEventPersist.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantService.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java create mode 100644 dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/audit/AuditableAction.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/audit/AuditableAction.java index dee3f5110..0ad1baed7 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/audit/AuditableAction.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/audit/AuditableAction.java @@ -9,6 +9,16 @@ public class AuditableAction { public static final EventId User_Available_Notifiers_Query = new EventId(10004, "User_Available_Notifiers_Query"); + public static final EventId User_Query = new EventId(11000, "User_Query"); + public static final EventId User_Lookup = new EventId(11001, "User_Lookup"); + public static final EventId User_Persist = new EventId(11002, "User_Persist"); + public static final EventId User_Delete = new EventId(11003, "User_Delete"); + + public static final EventId Tenant_Query = new EventId(12000, "Tenant_Query"); + public static final EventId Tenant_Lookup = new EventId(12001, "Tenant_Lookup"); + public static final EventId Tenant_Persist = new EventId(12002, "Tenant_Persist"); + public static final EventId Tenant_Delete = new EventId(12003, "Tenant_Delete"); + public static final EventId Notification_Query = new EventId(19000, "Notification_Query"); public static final EventId Notification_Lookup = new EventId(19001, "Notification_Lookup"); public static final EventId Notification_Persist = new EventId(19002, "Notification_Persist"); diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/common/enums/NotificationContactType.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/common/enums/NotificationContactType.java index 778358983..8b30d02e7 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/common/enums/NotificationContactType.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/common/enums/NotificationContactType.java @@ -3,29 +3,28 @@ package gr.cite.notification.common.enums; import com.fasterxml.jackson.annotation.JsonValue; import gr.cite.notification.data.conventers.DatabaseEnum; -import java.util.HashMap; import java.util.Map; public enum NotificationContactType implements DatabaseEnum { - EMAIL((short)0), - SLACK_BROADCAST((short)1), - SMS((short)2), - IN_APP((short)3); + EMAIL((short) 0), + SLACK_BROADCAST((short) 1), + SMS((short) 2), + IN_APP((short) 3); - private final Short value; + private final Short value; - NotificationContactType(Short value) { - this.value = value; - } + NotificationContactType(Short value) { + this.value = value; + } - @JsonValue - public Short getValue() { - return value; - } + @JsonValue + public Short getValue() { + return value; + } - private static final Map map = EnumUtils.getEnumValueMap(NotificationContactType.class); + private static final Map map = EnumUtils.getEnumValueMap(NotificationContactType.class); - public static NotificationContactType of(Short i) { - return map.get(i); - } + public static NotificationContactType of(Short i) { + return map.get(i); + } } diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java new file mode 100644 index 000000000..ab93cf53f --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java @@ -0,0 +1,48 @@ +package gr.cite.notification.integrationevent; + +import gr.cite.notification.integrationevent.inbox.InboxProperties; +import gr.cite.notification.integrationevent.outbox.OutboxProperties; +import gr.cite.queueinbox.repository.InboxRepository; +import gr.cite.rabbitmq.RabbitConfigurer; +import gr.cite.rabbitmq.consumer.InboxBindings; +import gr.cite.rabbitmq.consumer.InboxCreator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +@EnableConfigurationProperties({OutboxProperties.class, InboxProperties.class}) +@ConditionalOnProperty(prefix = "queue.rabbitmq", name = "listenerEnabled") +public class AppRabbitConfigurer extends RabbitConfigurer { + + private final ApplicationContext applicationContext; + + private final InboxProperties inboxProperties; + + public AppRabbitConfigurer(ApplicationContext applicationContext, InboxProperties inboxProperties) { + this.applicationContext = applicationContext; + this.inboxProperties = inboxProperties; + } + + @Bean + public InboxBindings inboxBindingsCreator() { + List bindingItems = new ArrayList<>(); + bindingItems.addAll(this.inboxProperties.getUserRemovalTopic()); + bindingItems.addAll(this.inboxProperties.getUserTouchedTopic()); + + return new InboxBindings(bindingItems); + } + + @Bean + public InboxCreator inboxCreator() { + return (params) -> { + InboxRepository inboxRepository = this.applicationContext.getBean(InboxRepository.class); + return inboxRepository.create(params) != null; + }; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/IntegrationEventContextImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/IntegrationEventContextImpl.java new file mode 100644 index 000000000..fd8c02dc3 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/IntegrationEventContextImpl.java @@ -0,0 +1,24 @@ +package gr.cite.notification.integrationevent; + +import gr.cite.rabbitmq.IntegrationEventContext; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class IntegrationEventContextImpl implements IntegrationEventContext { + + private UUID tenant; + + public IntegrationEventContextImpl() { + } + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/TrackedEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/TrackedEvent.java new file mode 100644 index 000000000..c5ecb2542 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/TrackedEvent.java @@ -0,0 +1,18 @@ +package gr.cite.notification.integrationevent; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class TrackedEvent { + + public String trackingContextTag; + + public String getTrackingContextTag() { + return trackingContextTag; + } + + public void setTrackingContextTag(String trackingContextTag) { + this.trackingContextTag = trackingContextTag; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyHandler.java new file mode 100644 index 000000000..78f4f7f67 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox; + +public interface ConsistencyHandler { + + Boolean isConsistent(T consistencyPredicates); + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyPredicates.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyPredicates.java new file mode 100644 index 000000000..e77c546a2 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/ConsistencyPredicates.java @@ -0,0 +1,5 @@ +package gr.cite.notification.integrationevent.inbox; + +public interface ConsistencyPredicates { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/EventProcessingStatus.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/EventProcessingStatus.java new file mode 100644 index 000000000..274049473 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/EventProcessingStatus.java @@ -0,0 +1,8 @@ +package gr.cite.notification.integrationevent.inbox; + +public enum EventProcessingStatus { + Error, + Success, + Postponed, + Discard +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java new file mode 100644 index 000000000..9fbc1521e --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java @@ -0,0 +1,60 @@ +package gr.cite.notification.integrationevent.inbox; + +import gr.cite.commons.web.oidc.principal.MyPrincipal; +import org.springframework.security.oauth2.core.ClaimAccessor; +import org.springframework.security.oauth2.jwt.JwtClaimNames; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InboxPrincipal implements MyPrincipal, ClaimAccessor { + + private final Map claims; + + private final boolean isAuthenticated; + + public InboxPrincipal(Boolean isAuthenticated, String name) { + this.claims = new HashMap<>(); + this.put(JwtClaimNames.SUB, name); + this.isAuthenticated = isAuthenticated; + } + + public static InboxPrincipal build(IntegrationEventProperties properties) { + InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId"); + inboxPrincipal.put("client_id", properties.getAppId()); + inboxPrincipal.put("active", "true"); + inboxPrincipal.put("nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString()); + inboxPrincipal.put("exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString()); + return inboxPrincipal; + } + + @Override + public Boolean isAuthenticated() { + return this.isAuthenticated; + } + + @Override + public Map getClaims() { + return this.claims; + } + + @Override + public List getClaimAsStringList(String claim) { + if (claims == null) + return null; + return this.getClaimAsStringList(claim); + } + + @Override + public String getName() { + return this.getClaimAsString(JwtClaimNames.SUB); + } + + public void put(String key, Object value) { + this.claims.put(key, value); + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java new file mode 100644 index 000000000..a3345ca98 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java @@ -0,0 +1,67 @@ +package gr.cite.notification.integrationevent.inbox; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@Validated +@ConfigurationProperties(prefix = "queue.task.listener.options") +public class InboxProperties { + + @NotNull + private final String exchange; + + @NotNull + private final List notifyTopic; + + @NotNull + private final List tenantRemovedTopic; + + @NotNull + private final List tenantTouchedTopic; + + @NotNull + private final List userRemovalTopic; + + @NotNull + private final List userTouchedTopic; + + public InboxProperties( + String exchange, List notifyTopic, List tenantRemovedTopic, List tenantTouchedTopic, + List userRemovalTopic, + List userTouchedTopic) { + this.exchange = exchange; + this.notifyTopic = notifyTopic; + this.tenantRemovedTopic = tenantRemovedTopic; + this.tenantTouchedTopic = tenantTouchedTopic; + this.userRemovalTopic = userRemovalTopic; + this.userTouchedTopic = userTouchedTopic; + } + + public List getNotifyTopic() { + return notifyTopic; + } + + public List getTenantRemovedTopic() { + return tenantRemovedTopic; + } + + public List getTenantTouchedTopic() { + return tenantTouchedTopic; + } + + public List getUserRemovalTopic() { + return userRemovalTopic; + } + + public List getUserTouchedTopic() { + return userTouchedTopic; + } + + public String getExchange() { + return exchange; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java new file mode 100644 index 000000000..3dcff805b --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java @@ -0,0 +1,365 @@ +package gr.cite.notification.integrationevent.inbox; + +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.enums.IsActive; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.data.QueueInboxEntity; +import gr.cite.notification.integrationevent.TrackedEvent; +import gr.cite.notification.integrationevent.inbox.notify.NotifyIntegrationEventHandler; +import gr.cite.notification.integrationevent.inbox.tenantremoval.TenantRemovalIntegrationEventHandler; +import gr.cite.notification.integrationevent.inbox.tenanttouched.TenantTouchedIntegrationEventHandler; +import gr.cite.notification.integrationevent.inbox.userremoval.UserRemovalIntegrationEventHandler; +import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEventHandler; +import gr.cite.notification.query.QueueInboxQuery; +import gr.cite.queueinbox.entity.QueueInbox; +import gr.cite.queueinbox.entity.QueueInboxStatus; +import gr.cite.queueinbox.repository.CandidateInfo; +import gr.cite.queueinbox.repository.InboxRepository; +import gr.cite.queueinbox.task.MessageOptions; +import gr.cite.rabbitmq.consumer.InboxCreatorParams; +import gr.cite.tools.data.query.Ordering; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; + +@Component +public class InboxRepositoryImpl implements InboxRepository { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InboxRepositoryImpl.class)); + + protected final ApplicationContext applicationContext; + + private final JsonHandlingService jsonHandlingService; + + private final InboxProperties inboxProperties; + + public InboxRepositoryImpl( + ApplicationContext applicationContext, + InboxProperties inboxProperties + ) { + this.applicationContext = applicationContext; + this.jsonHandlingService = this.applicationContext.getBean(JsonHandlingService.class); + this.inboxProperties = inboxProperties; + } + + @Override + public CandidateInfo candidate(Instant lastCandidateCreationTimestamp, MessageOptions options) { + EntityTransaction transaction = null; + EntityManager entityManager = null; + CandidateInfo candidate = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + QueueInboxEntity item = queryFactory.query(QueueInboxQuery.class) + .isActives(IsActive.Active) + .status(QueueInboxStatus.PENDING, QueueInboxStatus.ERROR) + .retryThreshold(options.getRetryThreashold()) + .createdAfter(lastCandidateCreationTimestamp) + .ordering(new Ordering().addAscending(QueueInboxEntity._createdAt)) + .first(); + + if (item != null) { + QueueInboxStatus prevState = item.getStatus(); + item.setStatus(QueueInboxStatus.PROCESSING); + + entityManager.merge(item); + entityManager.flush(); + + candidate = new CandidateInfo(); + candidate.setId(item.getId()); + candidate.setCreatedAt(item.getCreatedAt()); + candidate.setPreviousState(prevState); + } + + 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 inbox. Skipping: {} ", ex.getMessage()); + if (transaction != null) + transaction.rollback(); + candidate = null; + } catch (Exception ex) { + logger.error("Problem getting list of queue inbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + candidate = null; + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue inbox. Skipping: {}", ex.getMessage(), ex); + } + + return candidate; + } + + @Override + public Boolean shouldOmit(CandidateInfo candidate, Function shouldOmit) { + EntityTransaction transaction = null; + EntityManager entityManager = null; + boolean success = false; + + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + + entityManager = entityManagerFactory.createEntityManager(); + transaction = entityManager.getTransaction(); + + transaction.begin(); + + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + QueueInboxEntity item = queryFactory.query(QueueInboxQuery.class).ids(candidate.getId()).first(); + + if (item == null) { + logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidate.getId()); + } else { + if (shouldOmit.apply(item)) { + item.setStatus(QueueInboxStatus.OMITTED); + + entityManager.merge(item); + entityManager.flush(); + success = true; + } + } + + transaction.commit(); + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + if (transaction != null) + transaction.rollback(); + success = false; + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + } + return success; + } + + @Override + public boolean shouldWait(CandidateInfo candidate, Function itIsTimeFunc) { + EntityTransaction transaction = null; + EntityManager entityManager = null; + boolean success = false; + + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + + entityManager = entityManagerFactory.createEntityManager(); + transaction = entityManager.getTransaction(); + + transaction.begin(); + + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + QueueInboxEntity item = queryFactory.query(QueueInboxQuery.class).ids(candidate.getId()).first(); + + if (item.getRetryCount() != null && item.getRetryCount() >= 1) { + Boolean itIsTime = itIsTimeFunc.apply(item); + + if (!itIsTime) { + item.setStatus(candidate.getPreviousState()); + + entityManager.merge(item); + entityManager.flush(); + success = true; + } + + success = !itIsTime; + } + transaction.commit(); + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + if (transaction != null) + transaction.rollback(); + success = false; + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + } + return success; + } + + @Override + public QueueInbox create(InboxCreatorParams inboxCreatorParams) { + EntityTransaction transaction = null; + EntityManager entityManager = null; + boolean success = false; + QueueInboxEntity queueMessage = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + queueMessage = this.createQueueInboxEntity(inboxCreatorParams); + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + + entityManager = entityManagerFactory.createEntityManager(); + transaction = entityManager.getTransaction(); + + transaction.begin(); + + entityManager.persist(queueMessage); + entityManager.flush(); + + transaction.commit(); + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + if (transaction != null) + transaction.rollback(); + success = false; + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + } + return queueMessage; + } + + private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) { + + QueueInboxEntity queueMessage = new QueueInboxEntity(); + queueMessage.setId(UUID.randomUUID()); + queueMessage.setTenantId(null); + queueMessage.setExchange(this.inboxProperties.getExchange()); + queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); + queueMessage.setQueue(inboxCreatorParams.getQueueName()); + queueMessage.setApplicationId(inboxCreatorParams.getAppId()); + queueMessage.setMessageId(UUID.fromString(inboxCreatorParams.getMessageId())); + queueMessage.setMessage(inboxCreatorParams.getMessageBody()); + queueMessage.setIsActive(IsActive.Active); + queueMessage.setStatus(QueueInboxStatus.PENDING); + queueMessage.setRetryCount(0); + queueMessage.setCreatedAt(Instant.now()); + + return queueMessage; + } + + @Override + public Boolean emit(CandidateInfo candidateInfo) { + EntityTransaction transaction = null; + EntityManager entityManager = null; + boolean success = false; + + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + + entityManager = entityManagerFactory.createEntityManager(); + transaction = entityManager.getTransaction(); + transaction.begin(); + + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + QueueInboxEntity queueInboxMessage = queryFactory.query(QueueInboxQuery.class).ids(candidateInfo.getId()).first(); + + if (queueInboxMessage == null) { + logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId()); + } else { + + EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); + switch (status) { + case Success: { + queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); + break; + } + case Postponed: { + queueInboxMessage.setStatus(QueueInboxStatus.PARKED); + break; + } + case Error: { + queueInboxMessage.setStatus(QueueInboxStatus.ERROR); + queueInboxMessage.setRetryCount(queueInboxMessage.getRetryCount() != null ? queueInboxMessage.getRetryCount() + 1 : 0); + break; + } + case Discard: + default: { + queueInboxMessage.setStatus(QueueInboxStatus.DISCARD); + break; + } + } + success = status == EventProcessingStatus.Success; + + entityManager.merge(queueInboxMessage); + entityManager.flush(); + } + + transaction.commit(); + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + if (transaction != null) + transaction.rollback(); + success = false; + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + } + return success; + } + + private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { + IntegrationEventHandler handler; + if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) + handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class); + else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getTenantRemovedTopic())) + handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); + else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getTenantTouchedTopic())) + handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class); + else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) + handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); + else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic())) + handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); + else + handler = null; + + if (handler == null) + return EventProcessingStatus.Discard; + + IntegrationEventProperties properties = new IntegrationEventProperties(); + properties.setAppId(appId); + properties.setMessageId(messageId); + + TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, message); +// using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag)) +// { + try { + return handler.handle(properties, message); + } catch (Exception ex) { + logger.error("problem handling event from routing key " + routingKey + ". Setting nack and continuing...", ex); + return EventProcessingStatus.Error; + } +// } + } + + private Boolean RoutingKeyMatched(String routingKey, List topics) { + if (topics == null || topics.isEmpty()) + return false; + return topics.stream().anyMatch(x -> x.equals(routingKey)); + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventHandler.java new file mode 100644 index 000000000..729cd7642 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox; + +public interface IntegrationEventHandler { + + EventProcessingStatus handle(IntegrationEventProperties properties, String message); + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java new file mode 100644 index 000000000..9d5fcea93 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java @@ -0,0 +1,25 @@ +package gr.cite.notification.integrationevent.inbox; + +public class IntegrationEventProperties { + + private String messageId; + + private String appId; + + public String getMessageId() { + return messageId; + } + + public void setMessageId(String messageId) { + this.messageId = messageId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyHandler.java new file mode 100644 index 000000000..9af7e1cc8 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyHandler.java @@ -0,0 +1,47 @@ +package gr.cite.notification.integrationevent.inbox.notify; + +import gr.cite.notification.common.StringUtils; +import gr.cite.notification.common.enums.IsActive; +import gr.cite.notification.common.enums.NotificationContactType; +import gr.cite.notification.integrationevent.inbox.ConsistencyHandler; +import gr.cite.notification.model.User; +import gr.cite.notification.model.builder.UserBuilder; +import gr.cite.notification.query.UserQuery; +import gr.cite.tools.data.builder.BuilderFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class NotifyConsistencyHandler implements ConsistencyHandler { + + private final QueryFactory queryFactory; + + private final BuilderFactory builderFactory; + + public NotifyConsistencyHandler(QueryFactory queryFactory, BuilderFactory builderFactory) { + this.queryFactory = queryFactory; + this.builderFactory = builderFactory; + } + + @Override + public Boolean isConsistent(NotifyConsistencyPredicates consistencyPredicates) { + if (consistencyPredicates.getUserId() != null) { + UserQuery query = this.queryFactory.query(UserQuery.class).ids(consistencyPredicates.getUserId()).isActive(IsActive.Active); + + BaseFieldSet fieldSet = new BaseFieldSet( + User._id, + User._isActive + ); + User user = this.builderFactory.builder(UserBuilder.class).build(fieldSet, query.firstAs(fieldSet)); + if (user == null) + return false; + return consistencyPredicates.getContactTypeHint() == null || consistencyPredicates.getContactTypeHint() == NotificationContactType.IN_APP || !StringUtils.isNullOrEmpty(consistencyPredicates.getContactHint()); + } + return true; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyPredicates.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyPredicates.java new file mode 100644 index 000000000..9d87ad1ac --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyConsistencyPredicates.java @@ -0,0 +1,45 @@ +package gr.cite.notification.integrationevent.inbox.notify; + +import gr.cite.notification.common.enums.NotificationContactType; +import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates; + +import java.util.UUID; + +public class NotifyConsistencyPredicates implements ConsistencyPredicates { + + private UUID userId; + + private NotificationContactType contactTypeHint; + + private String contactHint; + + public NotifyConsistencyPredicates(UUID userId, NotificationContactType contactTypeHint, String contactHint) { + this.userId = userId; + this.contactTypeHint = contactTypeHint; + this.contactHint = contactHint; + } + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public NotificationContactType getContactTypeHint() { + return contactTypeHint; + } + + public void setContactTypeHint(NotificationContactType contactTypeHint) { + this.contactTypeHint = contactTypeHint; + } + + public String getContactHint() { + return contactHint; + } + + public void setContactHint(String contactHint) { + this.contactHint = contactHint; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java new file mode 100644 index 000000000..d04106d74 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java @@ -0,0 +1,80 @@ +package gr.cite.notification.integrationevent.inbox.notify; + +import gr.cite.notification.common.enums.NotificationContactType; +import gr.cite.notification.integrationevent.TrackedEvent; + +import java.util.UUID; + +public class NotifyIntegrationEvent extends TrackedEvent { + + private UUID userId; + + private UUID tenantId; + + private UUID notificationType; + + private NotificationContactType contactTypeHint; + + private String contactHint; + + private String data; + + private String provenanceRef; + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + + public UUID getNotificationType() { + return notificationType; + } + + public void setNotificationType(UUID notificationType) { + this.notificationType = notificationType; + } + + public NotificationContactType getContactTypeHint() { + return contactTypeHint; + } + + public void setContactTypeHint(NotificationContactType contactTypeHint) { + this.contactTypeHint = contactTypeHint; + } + + public String getContactHint() { + return contactHint; + } + + public void setContactHint(String contactHint) { + this.contactHint = contactHint; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getProvenanceRef() { + return provenanceRef; + } + + public void setProvenanceRef(String provenanceRef) { + this.provenanceRef = provenanceRef; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandler.java new file mode 100644 index 000000000..fadf33f60 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox.notify; + +import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; + +public interface NotifyIntegrationEventHandler extends IntegrationEventHandler { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java new file mode 100644 index 000000000..64abfc693 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java @@ -0,0 +1,146 @@ +package gr.cite.notification.integrationevent.inbox.notify; + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.notification.audit.AuditableAction; +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.enums.NotificationNotifyState; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.common.scope.tenant.TenantScope; +import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; +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.NotificationPersist; +import gr.cite.notification.query.TenantQuery; +import gr.cite.notification.service.notification.NotificationService; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +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.AbstractMap; +import java.util.Map; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEventHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotifyIntegrationEventHandlerImpl.class)); + + private final JsonHandlingService jsonHandlingService; + + private final ApplicationContext applicationContext; + + private final ErrorThesaurusProperties errors; + + private final MessageSource messageSource; + + public NotifyIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { + this.jsonHandlingService = jsonHandlingService; + this.applicationContext = applicationContext; + this.errors = errors; + this.messageSource = messageSource; + } + + @Override + public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { + NotifyIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(NotifyIntegrationEvent.class, message); + if (event == null) + return EventProcessingStatus.Error; + if (event.getUserId() == null) { + throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale())); + } + + NotificationPersist model = new NotificationPersist(); + model.setContactHint(event.getContactHint()); + model.setContactTypeHint(event.getContactTypeHint()); + model.setNotifiedWith(event.getContactTypeHint()); + model.setData(event.getData()); + model.setNotifyState(NotificationNotifyState.PENDING); + model.setType(event.getNotificationType()); + + EntityManager entityManager = null; + EntityTransaction transaction = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantScope scope = this.applicationContext.getBean(TenantScope.class); + if (scope.isMultitenant() && event.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + scope.setTenant(event.getTenantId(), tenant.getCode()); + } else if (scope.isMultitenant()) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + currentPrincipalResolver.push(InboxPrincipal.build(properties)); + + NotifyConsistencyHandler notifyConsistencyHandler = this.applicationContext.getBean(NotifyConsistencyHandler.class); + if (!(notifyConsistencyHandler.isConsistent(new NotifyConsistencyPredicates(event.getUserId(), event.getContactTypeHint(), event.getContactHint())))) + return EventProcessingStatus.Postponed; + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + try { + NotificationService notificationService = this.applicationContext.getBean(NotificationService.class); + notificationService.persist(model, new BaseFieldSet()); + + AuditService auditService = this.applicationContext.getBean(AuditService.class); + + auditService.track(AuditableAction.Notification_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getUserId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw e; + } 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()); + if (transaction != null) + transaction.rollback(); + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } + return null; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyHandler.java new file mode 100644 index 000000000..685597a98 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyHandler.java @@ -0,0 +1,26 @@ +package gr.cite.notification.integrationevent.inbox.tenantremoval; + +import gr.cite.notification.integrationevent.inbox.ConsistencyHandler; +import gr.cite.notification.query.TenantQuery; +import gr.cite.tools.data.query.QueryFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantRemovalConsistencyHandler implements ConsistencyHandler { + + private final QueryFactory queryFactory; + + public TenantRemovalConsistencyHandler(QueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + @Override + public Boolean isConsistent(TenantRemovalConsistencyPredicates consistencyPredicates) { + long count = this.queryFactory.query(TenantQuery.class).ids(consistencyPredicates.getTenantId()).count(); + return count > 0; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyPredicates.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyPredicates.java new file mode 100644 index 000000000..f55869126 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalConsistencyPredicates.java @@ -0,0 +1,23 @@ +package gr.cite.notification.integrationevent.inbox.tenantremoval; + +import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates; + +import java.util.UUID; + +public class TenantRemovalConsistencyPredicates implements ConsistencyPredicates { + + private UUID tenantId; + + public TenantRemovalConsistencyPredicates(UUID tenantId) { + this.tenantId = tenantId; + } + + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEvent.java new file mode 100644 index 000000000..cf1201688 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEvent.java @@ -0,0 +1,19 @@ +package gr.cite.notification.integrationevent.inbox.tenantremoval; + +import gr.cite.notification.integrationevent.TrackedEvent; + +import java.util.UUID; + +public class TenantRemovalIntegrationEvent extends TrackedEvent { + + private UUID id; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java new file mode 100644 index 000000000..a6e5d27ed --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox.tenantremoval; + +import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; + +public interface TenantRemovalIntegrationEventHandler extends IntegrationEventHandler { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java new file mode 100644 index 000000000..c27824f3f --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -0,0 +1,111 @@ +package gr.cite.notification.integrationevent.inbox.tenantremoval; + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.notification.audit.AuditableAction; +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; +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.service.tenant.TenantService; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.AbstractMap; +import java.util.Map; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIntegrationEventHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantRemovalIntegrationEventHandlerImpl.class)); + + private final JsonHandlingService jsonHandlingService; + + private final ApplicationContext applicationContext; + + private final ErrorThesaurusProperties errors; + + private final MessageSource messageSource; + + public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { + this.jsonHandlingService = jsonHandlingService; + this.applicationContext = applicationContext; + this.errors = errors; + this.messageSource = messageSource; + } + + @Override + public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { + TenantRemovalIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(TenantRemovalIntegrationEvent.class, message); + if (event == null) + return EventProcessingStatus.Error; + + EntityManager entityManager = null; + EntityTransaction transaction = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + currentPrincipalResolver.push(InboxPrincipal.build(properties)); + + TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class); + if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) + return EventProcessingStatus.Postponed; + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + try { + TenantService tenantService = this.applicationContext.getBean(TenantService.class); + tenantService.deleteAndSave(event.getId()); + + AuditService auditService = this.applicationContext.getBean(AuditService.class); + + auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw e; + } 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()); + if (transaction != null) + transaction.rollback(); + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } + return null; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java new file mode 100644 index 000000000..899d22db8 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java @@ -0,0 +1,29 @@ +package gr.cite.notification.integrationevent.inbox.tenanttouched; + +import gr.cite.notification.integrationevent.TrackedEvent; + +import java.util.UUID; + +public class TenantTouchedIntegrationEvent extends TrackedEvent { + + private UUID id; + + private String code; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandler.java new file mode 100644 index 000000000..f476601f3 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox.tenanttouched; + +import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; + +public interface TenantTouchedIntegrationEventHandler extends IntegrationEventHandler { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java new file mode 100644 index 000000000..a33114668 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java @@ -0,0 +1,103 @@ +package gr.cite.notification.integrationevent.inbox.tenanttouched; + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.notification.audit.AuditableAction; +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +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.persist.TenantTouchedIntegrationEventPersist; +import gr.cite.notification.service.tenant.TenantService; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.AbstractMap; +import java.util.Map; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantTouchedIntegrationEventHandlerImpl.class)); + + protected final ApplicationContext applicationContext; + + private final JsonHandlingService jsonHandlingService; + + public TenantTouchedIntegrationEventHandlerImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService) { + this.applicationContext = applicationContext; + this.jsonHandlingService = jsonHandlingService; + } + + @Override + public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { + TenantTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(TenantTouchedIntegrationEvent.class, message); + if (event == null) + return EventProcessingStatus.Error; + + TenantTouchedIntegrationEventPersist model = new TenantTouchedIntegrationEventPersist(); + model.setId(event.getId()); + model.setCode(event.getCode()); + + EntityManager entityManager = null; + EntityTransaction transaction = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + currentPrincipalResolver.push(InboxPrincipal.build(properties)); + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + try { + TenantService tenantService = this.applicationContext.getBean(TenantService.class); + tenantService.persist(model, null); + + AuditService auditService = this.applicationContext.getBean(AuditService.class); + + auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model) + )); + + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw e; + } 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()); + if (transaction != null) + transaction.rollback(); + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } + return null; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyHandler.java new file mode 100644 index 000000000..958d00b84 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyHandler.java @@ -0,0 +1,25 @@ +package gr.cite.notification.integrationevent.inbox.userremoval; + +import gr.cite.notification.integrationevent.inbox.ConsistencyHandler; +import gr.cite.notification.query.UserQuery; +import gr.cite.tools.data.query.QueryFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserRemovalConsistencyHandler implements ConsistencyHandler { + + private final QueryFactory queryFactory; + + public UserRemovalConsistencyHandler(QueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + @Override + public Boolean isConsistent(UserRemovalConsistencyPredicates consistencyPredicates) { + long count = this.queryFactory.query(UserQuery.class).ids(consistencyPredicates.getUserId()).count(); + return count != 0; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java new file mode 100644 index 000000000..2c9974a7d --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java @@ -0,0 +1,23 @@ +package gr.cite.notification.integrationevent.inbox.userremoval; + +import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates; + +import java.util.UUID; + +public class UserRemovalConsistencyPredicates implements ConsistencyPredicates { + + private UUID userId; + + public UserRemovalConsistencyPredicates(UUID userId) { + this.userId = userId; + } + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java new file mode 100644 index 000000000..2ce9f613d --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java @@ -0,0 +1,28 @@ +package gr.cite.notification.integrationevent.inbox.userremoval; + +import gr.cite.notification.integrationevent.TrackedEvent; + +import java.util.UUID; + +public class UserRemovalIntegrationEvent extends TrackedEvent { + + private UUID userId; + + private UUID tenant; + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java new file mode 100644 index 000000000..7bc8d1245 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox.userremoval; + +import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; + +public interface UserRemovalIntegrationEventHandler extends IntegrationEventHandler { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java new file mode 100644 index 000000000..791082ba8 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -0,0 +1,139 @@ +package gr.cite.notification.integrationevent.inbox.userremoval; + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.notification.audit.AuditableAction; +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.common.scope.tenant.TenantScope; +import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; +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.query.TenantQuery; +import gr.cite.notification.service.user.UserService; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +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.AbstractMap; +import java.util.Map; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegrationEventHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserRemovalIntegrationEventHandlerImpl.class)); + + private final JsonHandlingService jsonHandlingService; + + private final ApplicationContext applicationContext; + + private final ErrorThesaurusProperties errors; + + private final MessageSource messageSource; + + public UserRemovalIntegrationEventHandlerImpl( + JsonHandlingService jsonHandlingService, + ApplicationContext applicationContext, + ErrorThesaurusProperties errors, + MessageSource messageSource + ) { + this.jsonHandlingService = jsonHandlingService; + this.applicationContext = applicationContext; + this.errors = errors; + this.messageSource = messageSource; + } + + @Override + public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { + UserRemovalIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(UserRemovalIntegrationEvent.class, message); + if (event == null) + return EventProcessingStatus.Error; + if (event.getUserId() == null) + throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale())); + + EntityManager entityManager = null; + EntityTransaction transaction = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantScope scope = this.applicationContext.getBean(TenantScope.class); + if (scope.isMultitenant() && event.getTenant() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + scope.setTenant(event.getTenant(), tenant.getCode()); + } else if (scope.isMultitenant()) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + currentPrincipalResolver.push(InboxPrincipal.build(properties)); + + UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); + if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) + return EventProcessingStatus.Postponed; + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + try { + UserService userService = this.applicationContext.getBean(UserService.class); + userService.deleteAndSave(event.getUserId()); + + AuditService auditService = this.applicationContext.getBean(AuditService.class); + + auditService.track(AuditableAction.User_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getUserId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw e; + } 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()); + if (transaction != null) + transaction.rollback(); + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } + return null; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java new file mode 100644 index 000000000..113588fbf --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java @@ -0,0 +1,48 @@ +package gr.cite.notification.integrationevent.inbox.usertouched; + +import gr.cite.notification.integrationevent.TrackedEvent; + +import java.util.UUID; + +public class UserTouchedIntegrationEvent extends TrackedEvent { + + private UUID id; + + private UUID tenant; + + private String firstName; + + private String lastName; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java new file mode 100644 index 000000000..24b11d5f3 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java @@ -0,0 +1,7 @@ +package gr.cite.notification.integrationevent.inbox.usertouched; + +import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; + +public interface UserTouchedIntegrationEventHandler extends IntegrationEventHandler { + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java new file mode 100644 index 000000000..4adc71d59 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -0,0 +1,129 @@ +package gr.cite.notification.integrationevent.inbox.usertouched; + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.notification.audit.AuditableAction; +import gr.cite.notification.common.JsonHandlingService; +import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.common.scope.tenant.TenantScope; +import gr.cite.notification.data.TenantEntity; +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; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.OptimisticLockException; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.AbstractMap; +import java.util.Map; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegrationEventHandler { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class)); + + protected final ApplicationContext applicationContext; + + private final JsonHandlingService jsonHandlingService; + + public UserTouchedIntegrationEventHandlerImpl( + JsonHandlingService jsonHandlingService, + ApplicationContext applicationContext + ) { + this.jsonHandlingService = jsonHandlingService; + this.applicationContext = applicationContext; + } + + @Override + public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { + UserTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(UserTouchedIntegrationEvent.class, message); + if (event == null) + return EventProcessingStatus.Error; + + UserTouchedIntegrationEventPersist model = new UserTouchedIntegrationEventPersist(); + model.setId(event.getId()); + model.setFirstName(event.getFirstName()); + model.setLastName(event.getLastName()); + + EntityManager entityManager = null; + EntityTransaction transaction = null; + try (FakeRequestScope ignored = new FakeRequestScope()) { + try { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantScope scope = this.applicationContext.getBean(TenantScope.class); + if (scope.isMultitenant() && event.getTenant() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + scope.setTenant(event.getTenant(), tenant.getCode()); + } else if (scope.isMultitenant()) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } +// +// ValidationService validator = this.applicationContext.getBean(ValidationService.class); +// validator.validateForce(model); + + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + currentPrincipalResolver.push(InboxPrincipal.build(properties)); + + EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); + entityManager = entityManagerFactory.createEntityManager(); + + transaction = entityManager.getTransaction(); + transaction.begin(); + + try { + UserService userService = this.applicationContext.getBean(UserService.class); + userService.persist(model, null); + + AuditService auditService = this.applicationContext.getBean(AuditService.class); + + auditService.track(AuditableAction.User_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model) + )); + + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw e; + } 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()); + if (transaction != null) + transaction.rollback(); + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + if (transaction != null) + transaction.rollback(); + } finally { + if (entityManager != null) + entityManager.close(); + } + } catch (Exception ex) { + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } + return null; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java new file mode 100644 index 000000000..f1725a708 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java @@ -0,0 +1,95 @@ +package gr.cite.notification.integrationevent.outbox; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; + +@Validated +@ConfigurationProperties(prefix = "queue.task.publisher.options") +public class OutboxProperties { + + @NotNull + private final String exchange; + + @NotNull + private final String tenantTouchTopic; + + @NotNull + private final String tenantRemovalTopic; + + @NotNull + private final String tenantReactivationTopic; + + @NotNull + private final String tenantUserInviteTopic; + + @NotNull + private final String notifyTopic; + + @NotNull + private final String forgetMeCompletedTopic; + + @NotNull + private final String whatYouKnowAboutMeCompletedTopic; + + @NotNull + private final String generateFileTopic; + + public OutboxProperties(String exchange, + String tenantTouchTopic, + String tenantRemovalTopic, + String tenantReactivationTopic, + String tenantUserInviteTopic, + String notifyTopic, + String forgetMeCompletedTopic, + String whatYouKnowAboutMeCompletedTopic, + String generateFileTopic + ) { + this.exchange = exchange; + this.tenantTouchTopic = tenantTouchTopic; + this.tenantRemovalTopic = tenantRemovalTopic; + this.tenantReactivationTopic = tenantReactivationTopic; + this.tenantUserInviteTopic = tenantUserInviteTopic; + this.notifyTopic = notifyTopic; + this.forgetMeCompletedTopic = forgetMeCompletedTopic; + this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic; + this.generateFileTopic = generateFileTopic; + } + + public String getExchange() { + return exchange; + } + + public String getTenantTouchTopic() { + return tenantTouchTopic; + } + + public String getTenantRemovalTopic() { + return tenantRemovalTopic; + } + + public String getTenantReactivationTopic() { + return tenantReactivationTopic; + } + + public String getTenantUserInviteTopic() { + return tenantUserInviteTopic; + } + + public String getNotifyTopic() { + return notifyTopic; + } + + public String getForgetMeCompletedTopic() { + return forgetMeCompletedTopic; + } + + public String getWhatYouKnowAboutMeCompletedTopic() { + return whatYouKnowAboutMeCompletedTopic; + } + + public String getGenerateFileTopic() { + return generateFileTopic; + } +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/model/persist/TenantTouchedIntegrationEventPersist.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/model/persist/TenantTouchedIntegrationEventPersist.java new file mode 100644 index 000000000..695dec029 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/model/persist/TenantTouchedIntegrationEventPersist.java @@ -0,0 +1,37 @@ +package gr.cite.notification.model.persist; + +import gr.cite.notification.common.validation.ValidId; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.UUID; + +public class TenantTouchedIntegrationEventPersist { + + @ValidId(message = "{validation.invalidid}") + @NotNull(message = "{validation.empty}") + private UUID id; + + @NotNull(message = "{validation.empty}") + @NotEmpty(message = "{validation.empty}") + @Size(max = 50, message = "{validation.largerthanmax}") + private String code; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantService.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantService.java new file mode 100644 index 000000000..5e20f9f22 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantService.java @@ -0,0 +1,23 @@ +package gr.cite.notification.service.tenant; + +import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.notification.model.Tenant; +import gr.cite.notification.model.User; +import gr.cite.notification.model.persist.TenantTouchedIntegrationEventPersist; +import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.FieldSet; + +import javax.management.InvalidApplicationException; +import java.util.UUID; + +public interface TenantService { + + Tenant persist(TenantTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException; + + void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java new file mode 100644 index 000000000..f6ad77049 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java @@ -0,0 +1,51 @@ +package gr.cite.notification.service.tenant; + +import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.notification.authorization.Permission; +import gr.cite.notification.model.Tenant; +import gr.cite.notification.model.deleter.TenantDeleter; +import gr.cite.notification.model.deleter.UserDeleter; +import gr.cite.notification.model.persist.TenantTouchedIntegrationEventPersist; +import gr.cite.notification.service.user.UserServiceImpl; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; + +import javax.management.InvalidApplicationException; +import java.util.List; +import java.util.UUID; + +public class TenantServiceImpl implements TenantService { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantServiceImpl.class)); + + private final AuthorizationService authorizationService; + + private final DeleterFactory deleterFactory; + + public TenantServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory) { + this.authorizationService = authorizationService; + this.deleterFactory = deleterFactory; + } + + @Override + public Tenant persist(TenantTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException { + return null; + } + + @Override + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { + logger.debug("deleting Tenant: {}", id); + + this.authorizationService.authorizeForce(Permission.DeleteTenant); + + this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id)); + } + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java new file mode 100644 index 000000000..088b95202 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserService.java @@ -0,0 +1,21 @@ +package gr.cite.notification.service.user; + +import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.notification.model.User; +import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.FieldSet; + +import javax.management.InvalidApplicationException; +import java.util.UUID; + +public interface UserService { + + User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException; + + void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; + +} diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java new file mode 100644 index 000000000..f4025ea52 --- /dev/null +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java @@ -0,0 +1,51 @@ +package gr.cite.notification.service.user; + +import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.notification.authorization.Permission; +import gr.cite.notification.model.User; +import gr.cite.notification.model.deleter.UserDeleter; +import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.exception.MyApplicationException; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.exception.MyValidationException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.LoggerService; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.management.InvalidApplicationException; +import java.util.List; +import java.util.UUID; + +@Service +public class UserServiceImpl implements UserService { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserServiceImpl.class)); + + private final AuthorizationService authorizationService; + + private final DeleterFactory deleterFactory; + + public UserServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory) { + this.authorizationService = authorizationService; + this.deleterFactory = deleterFactory; + } + + @Override + public User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException { + return null; + } + + @Override + public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { + logger.debug("deleting User: {}", id); + + this.authorizationService.authorizeForce(Permission.DeleteUser); + + this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); + } + +} diff --git a/dmp-backend/notification-service/pom.xml b/dmp-backend/notification-service/pom.xml index 63db37937..ebe8127bb 100644 --- a/dmp-backend/notification-service/pom.xml +++ b/dmp-backend/notification-service/pom.xml @@ -29,6 +29,24 @@ + + + jakarta.annotation + jakarta.annotation-api + + + + + jakarta.validation + jakarta.validation-api + + + + jakarta.persistence + jakarta.persistence-api + 3.1.0 + + gr.cite queue-inbox @@ -39,6 +57,12 @@ queue-outbox 1.0.0 + + + gr.cite + rabbitmq-core + 1.0.0 + From 93ce7ecd6d94319a363f2449feb4d2ccf73c7c69 Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Fri, 12 Jan 2024 13:55:50 +0200 Subject: [PATCH 2/4] Notification service config update --- .../outbox/OutboxRepositoryImpl.java | 58 +++++++++---------- .../src/main/resources/config/queue-devel.yml | 2 +- .../src/main/resources/config/queue.yml | 12 ++-- .../integrationevent/AppRabbitConfigurer.java | 3 + .../inbox/InboxProperties.java | 15 +++-- .../inbox/InboxRepositoryImpl.java | 14 ++--- 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java index a9e36748c..0b93590f7 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java @@ -50,7 +50,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { EntityTransaction transaction = null; EntityManager entityManager = null; CandidateInfo candidate = null; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -86,18 +86,18 @@ public class OutboxRepositoryImpl implements OutboxRepository { 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 - this.logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); + logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); if (transaction != null) transaction.rollback(); candidate = null; } catch (Exception ex) { - this.logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); if (transaction != null) transaction.rollback(); candidate = null; } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); } return candidate; @@ -107,9 +107,9 @@ public class OutboxRepositoryImpl implements OutboxRepository { public Boolean shouldOmit(CandidateInfo candidate, Function shouldOmit) { EntityTransaction transaction = null; EntityManager entityManager = null; - Boolean success = false; + boolean success = false; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -122,7 +122,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { QueueOutboxEntity item = queryFactory.query(QueueOutboxQuery.class).ids(candidate.getId()).first(); if (item == null) { - this.logger.warn("Could not lookup queue outbox {} to process. Continuing...", candidate.getId()); + logger.warn("Could not lookup queue outbox {} to process. Continuing...", candidate.getId()); } else { if (shouldOmit.apply(item)) { item.setNotifyStatus(QueueOutboxNotifyStatus.OMITTED); @@ -135,14 +135,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); success = false; } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } return success; } @@ -151,9 +151,9 @@ public class OutboxRepositoryImpl implements OutboxRepository { public Boolean shouldWait(CandidateInfo candidate, Function itIsTimeFunc) { EntityTransaction transaction = null; EntityManager entityManager = null; - Boolean success = false; + boolean success = false; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -180,14 +180,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { } transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); success = false; } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } return success; } @@ -198,7 +198,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { EntityManager entityManager = null; Boolean success = false; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -211,7 +211,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { QueueOutboxEntity item = queryFactory.query(QueueOutboxQuery.class).ids(candidateInfo.getId()).first(); if (item == null) { - this.logger.warn("Could not lookup queue outbox {} to process. Continuing...", candidateInfo.getId()); + logger.warn("Could not lookup queue outbox {} to process. Continuing...", candidateInfo.getId()); } else { success = publish.apply(item); @@ -235,14 +235,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); success = false; } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } return success; } @@ -252,7 +252,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { EntityTransaction transaction = null; EntityManager entityManager = null; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -265,7 +265,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); if (queueOutboxMessages == null) { - this.logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -279,13 +279,13 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } } @@ -294,7 +294,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { EntityTransaction transaction = null; EntityManager entityManager = null; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -307,7 +307,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); if (queueOutboxMessages == null) { - this.logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -321,13 +321,13 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } } @@ -335,9 +335,9 @@ public class OutboxRepositoryImpl implements OutboxRepository { public QueueOutbox create(IntegrationEvent item) { EntityTransaction transaction = null; EntityManager entityManager = null; - Boolean success = false; + boolean success = false; QueueOutboxEntity queueMessage = null; - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + try (FakeRequestScope ignored = new FakeRequestScope()) { try { queueMessage = this.mapEvent((OutboxIntegrationEvent) item); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); @@ -352,14 +352,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); success = false; } finally { if (entityManager != null) entityManager.close(); } } catch (Exception ex) { - this.logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); + logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); } return queueMessage; } diff --git a/dmp-backend/notification-service/notification-web/src/main/resources/config/queue-devel.yml b/dmp-backend/notification-service/notification-web/src/main/resources/config/queue-devel.yml index f0e31766d..04855c727 100644 --- a/dmp-backend/notification-service/notification-web/src/main/resources/config/queue-devel.yml +++ b/dmp-backend/notification-service/notification-web/src/main/resources/config/queue-devel.yml @@ -15,4 +15,4 @@ queue: options: exchange: cite_dmp_devel_queue rabbitmq: - enable: false + enable: true diff --git a/dmp-backend/notification-service/notification-web/src/main/resources/config/queue.yml b/dmp-backend/notification-service/notification-web/src/main/resources/config/queue.yml index 2886f369a..4bc96aee1 100644 --- a/dmp-backend/notification-service/notification-web/src/main/resources/config/queue.yml +++ b/dmp-backend/notification-service/notification-web/src/main/resources/config/queue.yml @@ -26,11 +26,6 @@ queue: options: exchange: null forget-me-completed-topic: forgetme.completed - notify-topic: notification.notify - tenant-reactivation-topic: tenant.reactivated - tenant-removal-topic: tenant.remove - tenant-touch-topic: tenant.touch - tenant-user-invite-topic: tenant.invite what-you-know-about-me-completed-topic: whatyouknowaboutme.completed generate-file-topic: generate.file rabbitmq: @@ -46,8 +41,11 @@ queue: enable: false options: exchange: null - user-removal-topic: [ "user.remove" ] - user-touched-topic: [ "user.touch" ] + notify-topic: notification.notify + tenant-removal-topic: tenant.remove + tenant-touch-topic: tenant.touch + user-removal-topic: user.remove + user-touch-topic: user.touch rabbitmq: enable: false interval-seconds: 30 diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java index ab93cf53f..cfe2ecd6e 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/AppRabbitConfigurer.java @@ -32,6 +32,9 @@ public class AppRabbitConfigurer extends RabbitConfigurer { @Bean public InboxBindings inboxBindingsCreator() { List bindingItems = new ArrayList<>(); + bindingItems.addAll(this.inboxProperties.getNotifyTopic()); + bindingItems.addAll(this.inboxProperties.getTenantRemovalTopic()); + bindingItems.addAll(this.inboxProperties.getTenantTouchedTopic()); bindingItems.addAll(this.inboxProperties.getUserRemovalTopic()); bindingItems.addAll(this.inboxProperties.getUserTouchedTopic()); diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java index a3345ca98..3d257f8aa 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxProperties.java @@ -1,6 +1,7 @@ package gr.cite.notification.integrationevent.inbox; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotNull; @@ -8,6 +9,7 @@ import java.util.List; @Validated @ConfigurationProperties(prefix = "queue.task.listener.options") +@ConstructorBinding public class InboxProperties { @NotNull @@ -17,7 +19,7 @@ public class InboxProperties { private final List notifyTopic; @NotNull - private final List tenantRemovedTopic; + private final List tenantRemovalTopic; @NotNull private final List tenantTouchedTopic; @@ -29,12 +31,15 @@ public class InboxProperties { private final List userTouchedTopic; public InboxProperties( - String exchange, List notifyTopic, List tenantRemovedTopic, List tenantTouchedTopic, + String exchange, + List notifyTopic, + List tenantRemovalTopic, + List tenantTouchedTopic, List userRemovalTopic, List userTouchedTopic) { this.exchange = exchange; this.notifyTopic = notifyTopic; - this.tenantRemovedTopic = tenantRemovedTopic; + this.tenantRemovalTopic = tenantRemovalTopic; this.tenantTouchedTopic = tenantTouchedTopic; this.userRemovalTopic = userRemovalTopic; this.userTouchedTopic = userTouchedTopic; @@ -44,8 +49,8 @@ public class InboxProperties { return notifyTopic; } - public List getTenantRemovedTopic() { - return tenantRemovedTopic; + public List getTenantRemovalTopic() { + return tenantRemovalTopic; } public List getTenantTouchedTopic() { diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java index 3dcff805b..811725fbe 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java @@ -26,14 +26,12 @@ import jakarta.persistence.EntityTransaction; import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; import java.time.Instant; import java.util.List; import java.util.UUID; import java.util.function.Function; -@Component public class InboxRepositoryImpl implements InboxRepository { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InboxRepositoryImpl.class)); @@ -325,15 +323,15 @@ public class InboxRepositoryImpl implements InboxRepository { private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { IntegrationEventHandler handler; - if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) + if (this.routingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class); - else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getTenantRemovedTopic())) + else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic())) handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); - else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getTenantTouchedTopic())) + else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantTouchedTopic())) handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class); - else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) + else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); - else if (this.RoutingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic())) + else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic())) handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); else handler = null; @@ -357,7 +355,7 @@ public class InboxRepositoryImpl implements InboxRepository { // } } - private Boolean RoutingKeyMatched(String routingKey, List topics) { + private Boolean routingKeyMatched(String routingKey, List topics) { if (topics == null || topics.isEmpty()) return false; return topics.stream().anyMatch(x -> x.equals(routingKey)); From a2edcd6a04898d46b74d0c0224e19b315dcbf062 Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Fri, 12 Jan 2024 13:56:22 +0200 Subject: [PATCH 3/4] Notification service config update --- .../target/classes/config/queue-devel.yml | 2 +- .../notification-web/target/classes/config/queue.yml | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dmp-backend/notification-service/notification-web/target/classes/config/queue-devel.yml b/dmp-backend/notification-service/notification-web/target/classes/config/queue-devel.yml index f0e31766d..04855c727 100644 --- a/dmp-backend/notification-service/notification-web/target/classes/config/queue-devel.yml +++ b/dmp-backend/notification-service/notification-web/target/classes/config/queue-devel.yml @@ -15,4 +15,4 @@ queue: options: exchange: cite_dmp_devel_queue rabbitmq: - enable: false + enable: true diff --git a/dmp-backend/notification-service/notification-web/target/classes/config/queue.yml b/dmp-backend/notification-service/notification-web/target/classes/config/queue.yml index 2886f369a..4bc96aee1 100644 --- a/dmp-backend/notification-service/notification-web/target/classes/config/queue.yml +++ b/dmp-backend/notification-service/notification-web/target/classes/config/queue.yml @@ -26,11 +26,6 @@ queue: options: exchange: null forget-me-completed-topic: forgetme.completed - notify-topic: notification.notify - tenant-reactivation-topic: tenant.reactivated - tenant-removal-topic: tenant.remove - tenant-touch-topic: tenant.touch - tenant-user-invite-topic: tenant.invite what-you-know-about-me-completed-topic: whatyouknowaboutme.completed generate-file-topic: generate.file rabbitmq: @@ -46,8 +41,11 @@ queue: enable: false options: exchange: null - user-removal-topic: [ "user.remove" ] - user-touched-topic: [ "user.touch" ] + notify-topic: notification.notify + tenant-removal-topic: tenant.remove + tenant-touch-topic: tenant.touch + user-removal-topic: user.remove + user-touch-topic: user.touch rabbitmq: enable: false interval-seconds: 30 From 9e413d77f276b0bb273ea84929e92836b3aecf66 Mon Sep 17 00:00:00 2001 From: amentis Date: Fri, 12 Jan 2024 14:01:59 +0200 Subject: [PATCH 4/4] add user field in notification listing and small changes in other UI listings --- .../notification/query/NotificationQuery.java | 21 ++++++++++ .../query/lookup/NotificationLookup.java | 10 +++++ .../src/app/core/query/notification.lookup.ts | 2 + .../listing/language-listing.component.html | 4 +- ...tification-template-listing.component.html | 8 ++++ ...otification-listing-filters.component.html | 8 ++++ .../notification-listing-filters.component.ts | 23 ++++++++--- .../notification-listing.component.html | 38 ++++++++++--------- .../notification-listing.component.ts | 9 ++++- .../reference-type-listing.component.html | 15 ++++---- .../reference-type-listing.component.scss | 2 +- .../listing/reference-listing.component.html | 34 ++++++++++++++++- .../listing/tenant-listing.component.html | 15 ++++---- dmp-frontend/src/assets/i18n/en.json | 2 + 14 files changed, 149 insertions(+), 42 deletions(-) diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationQuery.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationQuery.java index eabc676b5..53bd80673 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationQuery.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationQuery.java @@ -35,6 +35,8 @@ public class NotificationQuery extends QueryBase { private List trackingState; private List trackingProgress; + + private Collection userIds; private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); public NotificationQuery ids(UUID value) { @@ -172,6 +174,21 @@ public class NotificationQuery extends QueryBase { return this; } + public NotificationQuery userIds(UUID value) { + this.userIds = List.of(value); + return this; + } + + public NotificationQuery userIds(UUID... value) { + this.userIds = Arrays.asList(value); + return this; + } + + public NotificationQuery userIds(Collection values) { + this.userIds = values; + return this; + } + public NotificationQuery ordering(Ordering ordering) { this.setOrder(ordering); return this; @@ -246,6 +263,10 @@ public class NotificationQuery extends QueryBase { predicates.add(queryContext.Root.get(NotificationEntity.Field._trackingProcess).in(trackingProgress)); } + if (this.userIds != null) { + predicates.add(queryContext.Root.get(NotificationEntity.Field._userId).in(userIds)); + } + if (predicates.size() > 0) { Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); return queryContext.CriteriaBuilder.and(predicatesArray); diff --git a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/NotificationLookup.java b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/NotificationLookup.java index 70938b9b0..ca27d07f7 100644 --- a/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/NotificationLookup.java +++ b/dmp-backend/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/NotificationLookup.java @@ -24,6 +24,7 @@ public class NotificationLookup extends Lookup { private Instant createdAfter; private List trackingState; private List trackingProcess; + private List userIds; public List getIsActive() { @@ -122,6 +123,14 @@ public class NotificationLookup extends Lookup { this.trackingProcess = trackingProcess; } + public List getUserIds() { + return userIds; + } + + public void setUserIds(List userIds) { + this.userIds = userIds; + } + public NotificationQuery enrich(QueryFactory queryFactory) { NotificationQuery query = queryFactory.query(NotificationQuery.class); if (this.isActive != null) query.isActive(this.isActive); @@ -136,6 +145,7 @@ public class NotificationLookup extends Lookup { if (this.type != null) query.type(this.type); if (this.trackingProcess != null) query.trackingProgress(this.trackingProcess); if (this.trackingState != null) query.trackingState(this.trackingState); + if (this.userIds != null) query.userIds(this.userIds); this.enrichCommon(query); diff --git a/dmp-frontend/src/app/core/query/notification.lookup.ts b/dmp-frontend/src/app/core/query/notification.lookup.ts index 7c4d3ac71..3ac6f12cc 100644 --- a/dmp-frontend/src/app/core/query/notification.lookup.ts +++ b/dmp-frontend/src/app/core/query/notification.lookup.ts @@ -17,6 +17,7 @@ export class NotificationLookup extends Lookup implements NotificationFilter { contactType: NotificationContactType[]; trackingState: NotificationTrackingState[]; trackingProcess: NotificationTrackingProcess[]; + userIds: Guid[]; constructor() { super(); @@ -33,4 +34,5 @@ export interface NotificationFilter { contactType: NotificationContactType[]; trackingState: NotificationTrackingState[]; trackingProcess: NotificationTrackingProcess[]; + userIds: Guid[]; } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/language/listing/language-listing.component.html b/dmp-frontend/src/app/ui/admin/language/listing/language-listing.component.html index 55be97c8b..94d9f035b 100644 --- a/dmp-frontend/src/app/ui/admin/language/listing/language-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/language/listing/language-listing.component.html @@ -39,9 +39,9 @@
- + {{item?.name | nullifyValue}} + (click)="$event.stopPropagation()">{{item?.code | nullifyValue}}
diff --git a/dmp-frontend/src/app/ui/admin/notification-template/listing/notification-template-listing.component.html b/dmp-frontend/src/app/ui/admin/notification-template/listing/notification-template-listing.component.html index 70c326c4d..616f548b7 100644 --- a/dmp-frontend/src/app/ui/admin/notification-template/listing/notification-template-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/notification-template/listing/notification-template-listing.component.html @@ -71,6 +71,14 @@
+ + + {{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FIELDS.CREATED-AT' | translate}}: + + {{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}} + + + {{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FIELDS.UPDATED-AT' | translate}}: diff --git a/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.html b/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.html index b06b4cd12..36441e232 100644 --- a/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.html +++ b/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.html @@ -20,6 +20,14 @@ {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.IS-ACTIVE' | translate}} +
+ + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.USERS' | translate}} + + + +
+
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFICATION-TYPE' | translate}} diff --git a/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.ts b/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.ts index 088945fb2..402dad24b 100644 --- a/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.ts +++ b/dmp-frontend/src/app/ui/admin/notification/filters/notification-listing-filters.component.ts @@ -1,3 +1,4 @@ +import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { IsActive } from '@app/core/common/enum/is-active.enum'; import { NotificationContactType } from '@app/core/common/enum/notification-contact-type'; @@ -6,8 +7,11 @@ import { NotificationTrackingProcess } from '@app/core/common/enum/notification- import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state'; import { NotificationType } from '@app/core/common/enum/notification-type'; import { NotificationFilter } from '@app/core/query/notification.lookup'; +import { UserService } from '@app/core/services/user/user.service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { BaseComponent } from '@common/base/base.component'; +import { Guid } from '@common/types/guid'; import { nameof } from 'ts-simple-nameof'; @Component({ @@ -24,6 +28,9 @@ export class NotificationListingFiltersComponent extends BaseComponent implement notificationTrackingStateEnumValues = this.enumUtils.getEnumValues(NotificationTrackingState); notificationTrackingProcessEnumValues = this.enumUtils.getEnumValues(NotificationTrackingProcess); notificationTypeEnumValues = this.enumUtils.getEnumValues(NotificationType); + userAutoCompleteConfiguration: MultipleAutoCompleteConfiguration; + + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; // * State internalFilters: NotificationListingFilters = this._getEmptyFilters(); @@ -31,9 +38,11 @@ export class NotificationListingFiltersComponent extends BaseComponent implement protected appliedFilterCount: number = 0; constructor( public enumUtils: EnumUtils, + private userService: UserService, ) { super(); } ngOnInit() { + this.userAutoCompleteConfiguration = this.userService.multipleAutocompleteConfiguration; } ngOnChanges(changes: SimpleChanges): void { @@ -55,7 +64,7 @@ export class NotificationListingFiltersComponent extends BaseComponent implement } protected applyFilters(): void { - const { isActive, type, notifyState, notifiedWith, contactType, trackingState, trackingProcess } = this.internalFilters ?? {} + const { isActive, type, notifyState, notifiedWith, contactType, trackingState, trackingProcess, userIds } = this.internalFilters ?? {} this.filterChange.emit({ ...this.filter, isActive: isActive ? [IsActive.Active] : [IsActive.Inactive], @@ -64,7 +73,8 @@ export class NotificationListingFiltersComponent extends BaseComponent implement notifiedWith, contactType, trackingState, - trackingProcess + trackingProcess, + userIds }) } @@ -74,7 +84,7 @@ export class NotificationListingFiltersComponent extends BaseComponent implement return this._getEmptyFilters(); } - let { isActive, type, notifyState, notifiedWith, contactType, trackingState, trackingProcess } = inputFilter; + let { isActive, type, notifyState, notifiedWith, contactType, trackingState, trackingProcess, userIds } = inputFilter; return { isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length, @@ -83,7 +93,8 @@ export class NotificationListingFiltersComponent extends BaseComponent implement notifiedWith: notifiedWith, contactType: contactType, trackingState: trackingState, - trackingProcess: trackingProcess + trackingProcess: trackingProcess, + userIds: userIds } } @@ -96,7 +107,8 @@ export class NotificationListingFiltersComponent extends BaseComponent implement notifiedWith: null, contactType: null, trackingState: null, - trackingProcess: null + trackingProcess: null, + userIds: null } } @@ -121,4 +133,5 @@ interface NotificationListingFilters { contactType: NotificationContactType[]; trackingState: NotificationTrackingState[]; trackingProcess: NotificationTrackingProcess[]; + userIds: Guid[]; } diff --git a/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.html b/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.html index bdd4865f8..5d78108ad 100644 --- a/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.html @@ -11,7 +11,7 @@ @@ -31,43 +31,47 @@
- - {{item?.name | nullifyValue}} -
-
- + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.NOTIFICATION-TYPE' | translate}}: - {{enumUtils.toNotificationTypeString(item.notificationType) | nullifyValue}} + {{enumUtils.toNotificationTypeString(item.type) | nullifyValue}}
- + - {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.KIND' | translate}}: + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.CONTACT-TYPE' | translate}}: - {{enumUtils.toNotificationTemplateKindString(item.kind) | nullifyValue}} + {{enumUtils.toNotificationContactTypeString(item.contactTypeHint) | nullifyValue}}
- + - {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.CHANNEL' | translate}}: + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.NOTIFY-STATE' | translate}}: - {{enumUtils.toNotificationTemplateChannelString(item.channel) | nullifyValue}} + {{enumUtils.toNotificationNotifyStateString(item.notifyState) | nullifyValue}}
- + - {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.UPDATED-AT' | translate}}: + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.NOTIFIED-AT' | translate}}: - {{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}} + {{item?.notifiedAt | dateTimeFormatter : 'short' | nullifyValue}} + + +
+
+ + + {{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.CREATED-AT' | translate}}: + + {{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}} diff --git a/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.ts b/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.ts index 7d23dccda..e47455f95 100644 --- a/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/notification/notification-listing.component.ts @@ -50,6 +50,8 @@ export class NotificationListingComponent extends BaseListingComponent(x => x.notifyState), nameof(x => x.notifiedWith), nameof(x => x.notifiedAt), + nameof(x => x.user.id), + nameof(x => x.user.name), nameof(x => x.createdAt), nameof(x => x.updatedAt), nameof(x => x.hash), @@ -125,7 +127,7 @@ export class NotificationListingComponent extends BaseListingComponent(x => x.notifiedAt), sortable: true, languageName: 'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.NOTIFIED-AT', - // pipe: this.pipeService.getPipe(NotificationContactTypePipe) + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') }, { prop: nameof(x => x.trackingState), @@ -133,6 +135,11 @@ export class NotificationListingComponent extends BaseListingComponent(NotificationTrackingStatePipe) }, + { + prop: nameof(x => x.user.name), + sortable: true, + languageName: 'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FIELDS.USER', + }, { prop: nameof(x => x.trackingProcess), sortable: true, diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html index 26530010e..32e3296e4 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html @@ -1,4 +1,4 @@ -
+
@@ -49,12 +49,13 @@ -
-
- {{enumUtils.toDescriptionTemplateTypeStatusString(item.status) | nullifyValue}} -
-
+ + {{'REFERENCE-TYPE-LISTING.FIELDS.CODE' | translate}}: + + {{item?.code | nullifyValue}} + + +
diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss index 6e1b48814..cd02faad1 100644 --- a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss @@ -1,4 +1,4 @@ -.description-template-type-listing { +.reference-type-listing { margin-top: 1.3rem; margin-left: 1rem; margin-right: 2rem; diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html index c1a42cc4b..6f349fd72 100644 --- a/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html @@ -39,12 +39,42 @@
- + {{item?.name | nullifyValue}} + (click)="$event.stopPropagation()">{{item?.label | nullifyValue}}
+ + + {{'REFERENCE-LISTING.FIELDS.SOURCE' | translate}}: + + {{item.source}} + + +
+
+ + + + {{'REFERENCE-LISTING.FIELDS.SOURCE-TYPE' | translate}}: + + {{enumUtils.toReferenceTypeSourceTypeString(item.sourceType) | nullifyValue}} + + +
+
+ + + + {{'REFERENCE-LISTING.FIELDS.TYPE' | translate}}: + + {{enumUtils.toReferenceTypeString(item.type) | nullifyValue}} + + +
+
+ {{'REFERENCE-LISTING.FIELDS.CREATED-AT' | translate}}: diff --git a/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html b/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html index 93669a2f5..7e147010a 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html @@ -45,13 +45,14 @@
- -
-
- {{enumUtils.toDescriptionTemplateTypeStatusString(item.status) | nullifyValue}} -
-
+ + + {{'TENANT-LISTING.FIELDS.CODE' | translate}}: + + {{item?.code | nullifyValue}} + + +
diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index b6e029b5e..21c6af833 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1421,6 +1421,7 @@ "TRACKING-PROCESS": "Tracking Process", "RETRY-COUNT": "Retry Count", "NOTIFIED-AT": "Notified At", + "USER": "User", "UPDATED-AT": "Updated", "CREATED-AT": "Created", "IS-ACTIVE": "Is Active" @@ -1433,6 +1434,7 @@ "CONTACT-TYPE": "Contact Type", "TRACKING-STATE": "Tracking State", "TRACKING-PROCESS": "Tracking Process", + "USERS": "Users", "CANCEL": "Cancel", "APPLY-FILTERS": "Apply filters" },