Adding outbox events for users on the main app

This commit is contained in:
Thomas Georgios Giannos 2024-01-26 17:42:45 +02:00
parent f58832e3e9
commit a55d7c3692
16 changed files with 792 additions and 397 deletions

View File

@ -7,6 +7,7 @@ import eu.eudat.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum NotificationContactType implements DatabaseEnum<Short> {
EMAIL((short) 0),
SLACK_BROADCAST((short) 1),
SMS((short) 2),

View File

@ -0,0 +1,30 @@
package eu.eudat.commons.enums.notification;
import com.fasterxml.jackson.annotation.JsonValue;
import eu.eudat.commons.enums.EnumUtils;
import eu.eudat.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum TentativeUserProfile implements DatabaseEnum<Short> {
Complete((short) 0), Tentative((short) 1);
private final Short value;
TentativeUserProfile(Short value) {
this.value = value;
}
@JsonValue
public Short getValue() {
return value;
}
private static final Map<Short, TentativeUserProfile> map = EnumUtils.getEnumValueMap(TentativeUserProfile.class);
public static TentativeUserProfile of(Short i) {
return map.get(i);
}
}

View File

@ -0,0 +1,30 @@
package eu.eudat.commons.enums.notification;
import com.fasterxml.jackson.annotation.JsonValue;
import eu.eudat.commons.enums.EnumUtils;
import eu.eudat.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum UserType implements DatabaseEnum<Short> {
Person((short) 0), Service((short) 1);
private final Short value;
UserType(Short value) {
this.value = value;
}
@JsonValue
public Short getValue() {
return value;
}
private static final Map<Short, UserType> map = EnumUtils.getEnumValueMap(UserType.class);
public static UserType of(Short i) {
return map.get(i);
}
}

View File

@ -4,17 +4,29 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import eu.eudat.integrationevent.TrackedEvent;
import gr.cite.rabbitmq.IntegrationEvent;
@JsonIgnoreProperties(ignoreUnknown = true)
public class OutboxIntegrationEvent extends IntegrationEvent {
public static final String FORGET_ME_COMPLETED = "FORGET_ME_COMPLETED";
public static final String NOTIFY = "NOTIFY";
public static final String TENANT_REACTIVATE = "TENANT_REACTIVATE";
public static final String TENANT_REMOVE = "TENANT_REMOVE";
public static final String TENANT_TOUCH = "TENANT_TOUCH";
public static final String TENANT_USER_INVITE = "TENANT_USER_INVITE";
public static final String USER_TOUCH = "USER_TOUCH";
public static final String USER_REMOVE = "USER_REMOVE";
public static final String WHAT_YOU_KNOW_ABOUT_ME_COMPLETED = "WHAT_YOU_KNOW_ABOUT_ME_COMPLETED";
public static final String GENERATE_FILE = "GENERATE_FILE";
private TrackedEvent event;
public TrackedEvent getEvent() {

View File

@ -1,33 +1,30 @@
package eu.eudat.integrationevent.outbox;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotNull;
@Validated
@ConfigurationProperties(prefix = "queue.task.publisher.options")
//@ConstructorBinding
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 userRemovalTopic;
private final String userTouchTopic;
private final String notifyTopic;
@NotNull
private final String forgetMeCompletedTopic;
@NotNull
private final String whatYouKnowAboutMeCompletedTopic;
@NotNull
private final String generateFileTopic;
public OutboxProperties(String exchange,
@ -35,6 +32,8 @@ public class OutboxProperties {
String tenantRemovalTopic,
String tenantReactivationTopic,
String tenantUserInviteTopic,
String userRemovalTopic,
String userTouchTopic,
String notifyTopic,
String forgetMeCompletedTopic,
String whatYouKnowAboutMeCompletedTopic,
@ -45,6 +44,8 @@ public class OutboxProperties {
this.tenantRemovalTopic = tenantRemovalTopic;
this.tenantReactivationTopic = tenantReactivationTopic;
this.tenantUserInviteTopic = tenantUserInviteTopic;
this.userRemovalTopic = userRemovalTopic;
this.userTouchTopic = userTouchTopic;
this.notifyTopic = notifyTopic;
this.forgetMeCompletedTopic = forgetMeCompletedTopic;
this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic;
@ -71,6 +72,14 @@ public class OutboxProperties {
return tenantUserInviteTopic;
}
public String getUserRemovalTopic() {
return userRemovalTopic;
}
public String getUserTouchTopic() {
return userTouchTopic;
}
public String getNotifyTopic() {
return notifyTopic;
}

View File

@ -14,16 +14,15 @@ import gr.cite.rabbitmq.IntegrationEvent;
import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
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 java.time.Instant;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -31,9 +30,11 @@ import java.util.stream.Collectors;
public class OutboxRepositoryImpl implements OutboxRepository {
protected final ApplicationContext applicationContext;
private final Random random = new Random();
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class));
private final JsonHandlingService jsonHandlingService;
private final OutboxProperties outboxProperties;
public OutboxRepositoryImpl(
@ -87,14 +88,17 @@ public class OutboxRepositoryImpl implements OutboxRepository {
} 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();
if (transaction != null)
transaction.rollback();
candidate = null;
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
if (transaction != null) transaction.rollback();
if (transaction != null)
transaction.rollback();
candidate = null;
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
@ -136,10 +140,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
success = false;
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -181,10 +187,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
success = false;
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -236,10 +244,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
success = false;
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -280,9 +290,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -322,9 +334,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -353,10 +367,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
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();
if (transaction != null)
transaction.rollback();
success = false;
} finally {
if (entityManager != null) entityManager.close();
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -383,6 +399,14 @@ public class OutboxRepositoryImpl implements OutboxRepository {
routingKey = this.outboxProperties.getTenantUserInviteTopic();
break;
}
case OutboxIntegrationEvent.USER_REMOVE: {
routingKey = this.outboxProperties.getUserRemovalTopic();
break;
}
case OutboxIntegrationEvent.USER_TOUCH: {
routingKey = this.outboxProperties.getUserTouchTopic();
break;
}
case OutboxIntegrationEvent.FORGET_ME_COMPLETED: {
routingKey = this.outboxProperties.getForgetMeCompletedTopic();
break;
@ -406,7 +430,8 @@ public class OutboxRepositoryImpl implements OutboxRepository {
}
UUID correlationId = UUID.randomUUID();
if (event.getEvent() != null) event.getEvent().setTrackingContextTag(correlationId.toString());
if (event.getEvent() != null)
event.getEvent().setTrackingContextTag(correlationId.toString());
event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent()));
//this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}");

View File

@ -32,11 +32,9 @@ public class OutboxServiceImpl implements OutboxService {
public void publish(OutboxIntegrationEvent event) {
try {
eventPublisher.publishEvent(event);
return;
} catch (Exception ex) {
logger.error(new MapLogEntry(String.format("Could not save message ", event.getMessage())).And("message", event.getMessage()).And("ex", ex));
//Still want to skip it from processing
return;
}
}

View File

@ -0,0 +1,25 @@
package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.integrationevent.inbox.ConsistencyHandler;
import eu.eudat.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<UserRemovalConsistencyPredicates> {
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;
}
}

View File

@ -0,0 +1,21 @@
package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.integrationevent.inbox.ConsistencyPredicates;
import java.util.UUID;
public class UserRemovalConsistencyPredicates implements ConsistencyPredicates {
public UserRemovalConsistencyPredicates(UUID userId) {
this.userId = userId;
}
private UUID userId;
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
}

View File

@ -0,0 +1,28 @@
package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.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;
}
}

View File

@ -0,0 +1,7 @@
package eu.eudat.integrationevent.outbox.userremoval;
public interface UserRemovalIntegrationEventHandler {
void handle(UserRemovalIntegrationEvent event);
}

View File

@ -0,0 +1,40 @@
package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import gr.cite.tools.logging.LoggerService;
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.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserRemovalIntegrationEventHandlerImpl.class));
private final OutboxService outboxService;
private final ApplicationContext applicationContext;
public UserRemovalIntegrationEventHandlerImpl(OutboxService outboxService, ApplicationContext applicationContext) {
this.outboxService = outboxService;
this.applicationContext = applicationContext;
}
@Override
public void handle(UserRemovalIntegrationEvent event) {
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class);
if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))
return;
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_REMOVE);
message.setEvent(event);
this.outboxService.publish(message);
}
}

View File

@ -0,0 +1,126 @@
package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.commons.enums.notification.TentativeUserProfile;
import eu.eudat.commons.enums.notification.UserType;
import eu.eudat.integrationevent.TrackedEvent;
import eu.eudat.model.UserContactInfo;
import java.util.List;
import java.util.UUID;
public class UserTouchedIntegrationEvent extends TrackedEvent {
private UUID id;
private UUID tenant;
private String firstName;
private String lastName;
private UserType type;
private UserProfile profile;
private List<UserContactInfo> userContactInfo;
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;
}
public UserType getType() {
return type;
}
public void setType(UserType type) {
this.type = type;
}
public UserProfile getProfile() {
return profile;
}
public void setProfile(UserProfile profile) {
this.profile = profile;
}
public List<UserContactInfo> getUserContactInfo() {
return userContactInfo;
}
public void setUserContactInfo(List<UserContactInfo> userContactInfo) {
this.userContactInfo = userContactInfo;
}
public static class UserProfile {
private TentativeUserProfile isTentative;
private String timezone;
private String culture;
private String language;
public TentativeUserProfile getIsTentative() {
return isTentative;
}
public void setIsTentative(TentativeUserProfile isTentative) {
this.isTentative = isTentative;
}
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
public String getCulture() {
return culture;
}
public void setCulture(String culture) {
this.culture = culture;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
}
}

View File

@ -0,0 +1,7 @@
package eu.eudat.integrationevent.outbox.usertouched;
public interface UserTouchedIntegrationEventHandler {
void handle(UserTouchedIntegrationEvent event);
}

View File

@ -0,0 +1,34 @@
package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class));
private final OutboxService outboxService;
public UserTouchedIntegrationEventHandlerImpl(
OutboxService outboxService) {
this.outboxService = outboxService;
}
@Override
public void handle(UserTouchedIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_TOUCH);
message.setEvent(event);
this.outboxService.publish(message);
}
}

View File

@ -31,6 +31,8 @@ queue:
tenant-removal-topic: tenant.remove
tenant-touch-topic: tenant.touch
tenant-user-invite-topic: tenant.invite
user-touch-topic: user.touch
user-removal-topic: user.remove
what-you-know-about-me-completed-topic: whatyouknowaboutme.completed
generate-file-topic: generate.file
rabbitmq: