Resolving issues with notification service user integration (on user creation)

This commit is contained in:
Thomas Georgios Giannos 2024-01-31 15:23:41 +02:00
parent 2ba9e146e5
commit 8f7a3cf768
11 changed files with 305 additions and 68 deletions

View File

@ -14,6 +14,8 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private String name;
private String subjectId;
private UserProfile profile;
private List<UserContactInfo> userContactInfo;
@ -42,6 +44,14 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.name = name;
}
public String getSubjectId() {
return subjectId;
}
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
public UserProfile getProfile() {
return profile;
}

View File

@ -62,10 +62,15 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
List<UserContactInfoEntity> userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId)
.collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value));
List<UserCredentialEntity> userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId)
.collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId));
UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent();
event.setId(userId);
event.setTenant(null); //TODO
event.setName(user.getName());
if (userCredentialEntities != null && !userCredentialEntities.isEmpty())
event.setSubjectId(userCredentialEntities.getFirst().getExternalId());
event.setProfile(new UserTouchedIntegrationEvent.UserProfile());
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo());
if (definition != null) {

View File

@ -170,6 +170,7 @@ permissions:
BrowseInAppNotification:
roles:
- Admin
- User
clients: [ ]
allowAnonymous: false
allowAuthenticated: false

View File

@ -323,6 +323,7 @@ public class InboxRepositoryImpl implements InboxRepository {
private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) {
IntegrationEventHandler handler;
logger.debug("Processing message with routing key '{}'", routingKey);
if (this.routingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic()))
handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic()))
@ -333,8 +334,10 @@ public class InboxRepositoryImpl implements InboxRepository {
handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic()))
handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else
else {
logger.error("No handler found for message routing key '{}'. Discarding.", routingKey);
handler = null;
}
if (handler == null)
return EventProcessingStatus.Discard;

View File

@ -67,7 +67,7 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent
if (event.getUserId() == null) {
throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale()));
}
logger.debug("Handling NotifyIntegrationEvent");
logger.debug("Handling {}", NotifyIntegrationEvent.class.getSimpleName());
NotificationPersist model = new NotificationPersist();
model.setType(event.getNotificationType());

View File

@ -67,6 +67,8 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
if (event.getUserId() == null)
throw new MyValidationException(this.errors.getModelValidation().getCode(), "userId", messageSource.getMessage("Validation_Required", new Object[]{"userId"}, LocaleContextHolder.getLocale()));
logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName());
EntityManager entityManager = null;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
@ -134,6 +136,6 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
return EventProcessingStatus.Success;
}
}

View File

@ -1,18 +1,43 @@
package gr.cite.notification.integrationevent.inbox.usertouched;
import gr.cite.notification.common.enums.ContactInfoType;
import gr.cite.notification.common.validation.BaseValidator;
import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.notification.integrationevent.TrackedEvent;
import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class UserTouchedIntegrationEvent extends TrackedEvent {
private UUID id;
public static final String _id = "id";
private UUID tenant;
private String firstName;
private String name;
private String lastName;
public static final String _name = "name";
public static final int _nameLength = 200;
private String subjectId;
public static final String _subjectId = "subjectId";
private UserProfile profile;
private List<UserContactInfo> userContactInfo;
public UUID getId() {
return id;
@ -30,19 +55,140 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.tenant = tenant;
}
public String getFirstName() {
return firstName;
public String getName() {
return name;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
public String getSubjectId() {
return subjectId;
}
public void setLastName(String lastName) {
this.lastName = lastName;
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
public UserProfile getProfile() {
return profile;
}
public void setProfile(UserProfile profile) {
this.profile = profile;
}
public List<UserContactInfo> getUserContactInfo() {
return userContactInfo;
}
public void setUserContactInfo(List<UserContactInfo> userContactInfo) {
this.userContactInfo = userContactInfo;
}
public static class UserProfile {
private String timezone;
private String culture;
private String language;
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
public String getCulture() {
return culture;
}
public void setCulture(String culture) {
this.culture = culture;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
}
public static class UserContactInfo {
private ContactInfoType type;
private String value;
private int ordinal;
public ContactInfoType getType() {
return type;
}
public void setType(ContactInfoType type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getOrdinal() {
return ordinal;
}
public void setOrdinal(int ordinal) {
this.ordinal = ordinal;
}
}
@Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationEventValidator extends BaseValidator<UserTouchedIntegrationEvent> {
public static final String ValidatorName = "UserTouchedIntegrationEventValidator";
private final MessageSource messageSource;
protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<UserTouchedIntegrationEvent> modelClass() {
return UserTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(UserTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.iff(() -> !this.isNull(item.getId()))
.must(() -> this.isValidGuid(item.getId()))
.failOn(UserTouchedIntegrationEvent._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._id}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getName()))
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isEmpty(item.getName()))
.must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength))
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getSubjectId()))
.failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -10,7 +10,6 @@ import gr.cite.notification.integrationevent.inbox.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal;
import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.notification.model.Tenant;
import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist;
import gr.cite.notification.query.TenantQuery;
import gr.cite.notification.service.user.UserService;
import gr.cite.tools.auditing.AuditService;
@ -40,8 +39,8 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
protected final ApplicationContext applicationContext;
private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory;
private final ValidatorFactory validatorFactory;
public UserTouchedIntegrationEventHandlerImpl(
JsonHandlingService jsonHandlingService,
@ -58,11 +57,9 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
if (event == null)
return EventProcessingStatus.Error;
UserTouchedIntegrationEventPersist model = new UserTouchedIntegrationEventPersist();
model.setId(event.getId());
model.setFirstName(event.getFirstName());
model.setLastName(event.getLastName());
this.validatorFactory.validator(UserTouchedIntegrationEventPersist.UserTouchedIntegrationEventPersistValidator.class).validateForce(model);
logger.debug("Handling {}", UserTouchedIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null;
EntityTransaction transaction = null;
@ -96,12 +93,12 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
try {
UserService userService = this.applicationContext.getBean(UserService.class);
userService.persist(model, null);
userService.persist(event, null);
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model)
new AbstractMap.SimpleEntry<String, Object>("model", event)
));
transaction.commit();
@ -111,8 +108,6 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
} finally {
currentPrincipalResolver.pop();
}
transaction.commit();
} catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage());
@ -129,6 +124,6 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
return EventProcessingStatus.Success;
}
}

View File

@ -4,6 +4,7 @@ import gr.cite.notification.common.validation.BaseValidator;
import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
@ -20,14 +21,11 @@ public class UserTouchedIntegrationEventPersist {
private UUID id;
public static final String _id = "id";
private String firstName;
public static final String _firstName = "firstName";
public static final int _firstNameLength = 200;
private String name;
private String lastName;
public static final String _lastName = "lastName";
public static final int _lastNameLength = 200;
public static final String _name = "name";
public static final int _nameLength = 200;
public UUID getId() {
return id;
@ -37,20 +35,12 @@ public class UserTouchedIntegrationEventPersist {
this.id = id;
}
public String getFirstName() {
return firstName;
public String getName() {
return name;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
public void setName(String name) {
this.name = name;
}
@Component(UserTouchedIntegrationEventPersist.UserTouchedIntegrationEventPersistValidator.ValidatorName)
@ -77,21 +67,14 @@ public class UserTouchedIntegrationEventPersist {
this.spec()
.iff(() -> !this.isNull(item.getId()))
.must(() -> this.isValidGuid(item.getId()))
.failOn(UserTouchedIntegrationEventPersist._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._id}, LocaleContextHolder.getLocale())),
.failOn(UserTouchedIntegrationEvent._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._id}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getFirstName()))
.failOn(UserTouchedIntegrationEventPersist._firstName).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._firstName}, LocaleContextHolder.getLocale())),
.must(() -> !this.isEmpty(item.getName()))
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._name}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isEmpty(item.getFirstName()))
.must(() -> this.lessEqualLength(item.getFirstName(), UserTouchedIntegrationEventPersist._firstNameLength))
.failOn(UserTouchedIntegrationEventPersist._firstName).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._firstName}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getLastName()))
.failOn(UserTouchedIntegrationEventPersist._lastName).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEventPersist._lastName}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isEmpty(item.getLastName()))
.must(() -> this.lessEqualLength(item.getLastName(), UserTouchedIntegrationEventPersist._lastNameLength))
.failOn(UserTouchedIntegrationEventPersist._lastName).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._lastName}, LocaleContextHolder.getLocale()))
.iff(() -> !this.isEmpty(item.getName()))
.must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEvent._nameLength))
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEventPersist._name}, LocaleContextHolder.getLocale()))
);
}
}

View File

@ -1,6 +1,7 @@
package gr.cite.notification.service.user;
import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent;
import gr.cite.notification.model.User;
import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist;
import gr.cite.tools.exception.MyApplicationException;
@ -14,7 +15,7 @@ import java.util.UUID;
public interface UserService {
User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException;
User persist(UserTouchedIntegrationEvent model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;

View File

@ -8,13 +8,17 @@ import gr.cite.notification.authorization.Permission;
import gr.cite.notification.common.JsonHandlingService;
import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.data.UserContactInfoEntity;
import gr.cite.notification.data.UserCredentialEntity;
import gr.cite.notification.data.UserEntity;
import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent;
import gr.cite.notification.model.User;
import gr.cite.notification.model.builder.UserBuilder;
import gr.cite.notification.model.deleter.UserDeleter;
import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist;
import gr.cite.notification.query.UserContactInfoQuery;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
@ -24,16 +28,13 @@ import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import java.util.*;
@Service
public class UserServiceImpl implements UserService {
@ -50,23 +51,32 @@ public class UserServiceImpl implements UserService {
private final BuilderFactory builderFactory;
private final QueryFactory queryFactory;
private final JsonHandlingService jsonHandlingService;
public UserServiceImpl(AuthorizationService authorizationService,
DeleterFactory deleterFactory,
ConventionService conventionService,
EntityManager entityManager,
BuilderFactory builderFactory) {
BuilderFactory builderFactory,
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService) {
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.conventionService = conventionService;
this.entityManager = entityManager;
this.builderFactory = builderFactory;
this.queryFactory = queryFactory;
this.jsonHandlingService = jsonHandlingService;
}
@Override
public User persist(UserTouchedIntegrationEventPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException {
@Transactional
public User persist(UserTouchedIntegrationEvent model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JsonProcessingException {
logger.debug(new MapLogEntry("persisting user").And("model", model).And("fields", fields));
this.authorizationService.authorizeAtLeastOneForce(model.getId() != null ? List.of(new OwnedResource(model.getId())) : null, Permission.EditUser);
// this.authorizationService.authorizeAtLeastOneForce(model.getId() != null ? List.of(new OwnedResource(model.getId())) : null, Permission.EditUser);
Boolean isValid = this.conventionService.isValidGuid(model.getId());
@ -76,18 +86,73 @@ public class UserServiceImpl implements UserService {
if (data == null) {
data = new UserEntity();
data.setId(model.getId());
data.setName(model.getFirstName() + " " + model.getLastName());
data.setName(model.getName());
data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile()));
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
this.entityManager.persist(data);
for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) {
this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId()));
}
this.entityManager.persist(this.buildUserCredentialEntityFromEventData(model));
} else {
data.setName(model.getFirstName() + " " + model.getLastName());
data.setName(model.getName());
data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile()));
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
this.entityManager.merge(data);
List<UserContactInfoEntity> contactInfoList = this.queryFactory.query(UserContactInfoQuery.class).userIds(model.getId()).collect();
List<UserContactInfoEntity> updatedContacts = new ArrayList<>();
for (UserContactInfoEntity c : contactInfoList) {
boolean updated = false;
//Contact found on the existing, update it
for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) {
if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) {
c.setUserId(model.getId());
c.setType(c.getType());
c.setValue(c.getValue());
c.setOrdinal(c.getOrdinal());
c.setUpdatedAt(Instant.now());
c.setIsActive(IsActive.Active);
this.entityManager.merge(c);
updatedContacts.add(c);
updated = true;
break;
}
}
//Contact was not found on the event data, remove it
if (!updated) {
c.setIsActive(IsActive.Inactive);
this.entityManager.merge(c);
}
}
for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) {
boolean updated = false;
for (UserContactInfoEntity c : updatedContacts) {
if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) {
updated = true;
break;
}
}
//New contact detected since it came with the event data and was not existing in the database, add it
if (!updated) {
this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId()));
}
}
}
} else {
throw new MyApplicationException("Not valid user id");
@ -107,4 +172,30 @@ public class UserServiceImpl implements UserService {
this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id));
}
private UserContactInfoEntity buildContactInfoEntityFromEventData(UserTouchedIntegrationEvent.UserContactInfo eventC, UUID userId) {
UserContactInfoEntity contactInfo = new UserContactInfoEntity();
contactInfo.setId(UUID.randomUUID());
contactInfo.setUserId(userId);
contactInfo.setType(eventC.getType());
contactInfo.setValue(eventC.getValue());
contactInfo.setOrdinal(eventC.getOrdinal());
contactInfo.setCreatedAt(Instant.now());
contactInfo.setUpdatedAt(Instant.now());
contactInfo.setIsActive(IsActive.Active);
return contactInfo;
}
private UserCredentialEntity buildUserCredentialEntityFromEventData(UserTouchedIntegrationEvent event) {
UserCredentialEntity credentialEntity = new UserCredentialEntity();
credentialEntity.setId(UUID.randomUUID());
credentialEntity.setUserId(event.getId());
credentialEntity.setExternalId(event.getSubjectId());
credentialEntity.setCreatedAt(Instant.now());
credentialEntity.setUpdatedAt(Instant.now());
credentialEntity.setIsActive(IsActive.Active);
return credentialEntity;
}
}