package gr.cite.notification.service.userNotificationPreference; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.notification.authorization.OwnedResource; import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.NotificationContactType; import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer; import gr.cite.notification.config.notification.NotificationConfig; import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.UserNotificationPreferenceEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.UserNotificationPreference; import gr.cite.notification.model.builder.UserNotificationPreferenceBuilder; import gr.cite.notification.model.persist.UserNotificationPreferencePersist; import gr.cite.notification.query.UserNotificationPreferenceQuery; import gr.cite.notification.service.tenantconfiguration.TenantConfigurationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.web.context.annotation.RequestScope; import javax.management.InvalidApplicationException; import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @Service @RequestScope public class UserNotificationPreferenceServiceImpl implements UserNotificationPreferenceService{ private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserNotificationPreferenceServiceImpl.class)); private final QueryFactory queryFactory; private final BuilderFactory builderFactory; private final AuthorizationService authService; private final TenantConfigurationService tenantConfigurationService; private final Map> globalPoliciesMap; private final ErrorThesaurusProperties errors; private final TenantEntityManager entityManager; @Autowired public UserNotificationPreferenceServiceImpl(QueryFactory queryFactory, BuilderFactory builderFactory, AuthorizationService authService, TenantConfigurationService tenantConfigurationService, @Qualifier(NotificationConfig.BeanQualifier.GLOBAL_POLICIES_MAP) Map> globalPoliciesMap, ErrorThesaurusProperties errors, TenantEntityManager entityManager) { this.queryFactory = queryFactory; this.builderFactory = builderFactory; this.authService = authService; this.tenantConfigurationService = tenantConfigurationService; this.globalPoliciesMap = globalPoliciesMap; this.errors = errors; this.entityManager = entityManager; } @Override public List persist(UserNotificationPreferencePersist model, FieldSet fieldSet) { logger.debug(new MapLogEntry("persisting").And("model", model).And("fields", fieldSet)); this.authService.authorizeAtLeastOneForce(model.getUserId() != null ? List.of(new OwnedResource(model.getUserId())) : null, Permission.EditUserNotificationPreference); Map> currentNotificationListPolicies; NotifierListConfigurationDataContainer tenantNotifierListPolicies = this.tenantConfigurationService.collectTenantNotifierList(); if (tenantNotifierListPolicies != null) { currentNotificationListPolicies = mergeNotifierPolicies(tenantNotifierListPolicies.getNotifiers(), this.globalPoliciesMap); } else { currentNotificationListPolicies = this.globalPoliciesMap; } if (model.getNotificationPreferences().entrySet().stream().anyMatch(entry -> model.getNotificationPreferences().get(entry.getKey()).stream().anyMatch(contactType -> !currentNotificationListPolicies.get(entry.getKey()).contains(contactType)))) { throw new MyValidationException(this.errors.getOverlappingTenantConfigurationNotifierList().getCode(), this.errors.getOverlappingTenantConfigurationNotifierList().getMessage()); } List datas = this.patchAndSave(List.of(model)); return this.builderFactory.builder(UserNotificationPreferenceBuilder.class).build(fieldSet, datas); } @Override public NotifierListConfigurationDataContainer collectUserAvailableNotifierList(Set notificationTypes) { Map> currentNotificationListPolicies; NotifierListConfigurationDataContainer tenantNotifierListPolicies = this.tenantConfigurationService.collectTenantNotifierList(); if (tenantNotifierListPolicies != null) { currentNotificationListPolicies = mergeNotifierPolicies(tenantNotifierListPolicies.getNotifiers(), this.globalPoliciesMap); } else { currentNotificationListPolicies = this.globalPoliciesMap; } if (notificationTypes != null && !notificationTypes.isEmpty()) { return new NotifierListConfigurationDataContainer(currentNotificationListPolicies .entrySet().stream() .filter(x -> notificationTypes.contains(x.getKey())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } else return new NotifierListConfigurationDataContainer(currentNotificationListPolicies); } @Override public List collectUserNotificationPreferences(UUID id) { Map> result = this.collectUserNotificationPreferences(List.of(id)); if (result != null) { return result.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); } logger.error("failed to collect user notification preferences for user " + id); return new ArrayList<>(); } @Override public Map> collectUserNotificationPreferences(List ids) { return this.builderFactory.builder(UserNotificationPreferenceBuilder.class) .build(new BaseFieldSet(UserNotificationPreference._userId, UserNotificationPreference._type, UserNotificationPreference._channel, UserNotificationPreference._ordinal), this.queryFactory .query(UserNotificationPreferenceQuery.class) .userId(ids).collect()).stream().collect(Collectors.groupingBy(UserNotificationPreference::getUserId)); //GK: Yep that exist on JAVA Streams } private Map> mergeNotifierPolicies(Map> overrides, Map> bases) { Map> mergedPolicies = new HashMap<>(); for(Map.Entry> policyEntry: bases.entrySet()) { if (overrides.containsKey(policyEntry.getKey())) { List notifierList = overrides.get(policyEntry.getKey()); if(notifierList == null) throw new MyApplicationException(this.errors.getSystemError().getCode(), this.errors.getSystemError().getMessage()); mergedPolicies.put(policyEntry.getKey(), notifierList); } else { mergedPolicies.put(policyEntry.getKey(), policyEntry.getValue()); } } return mergedPolicies; } private List patchAndSave(List models) { List datas = new ArrayList<>(); for (UserNotificationPreferencePersist model: models) { for (Map.Entry> notificationPreference: model.getNotificationPreferences().entrySet()) { this.patchAndSave(model.getUserId(), notificationPreference.getKey(), notificationPreference.getValue()); } } return datas; } private List patchAndSave(UUID userId, UUID type, List contactTypes) { List preferences = null; try { preferences = this.queryFactory .query(UserNotificationPreferenceQuery.class) .type(type) .isActives(IsActive.Active) .userId(userId).collect(); int ordinal = 0; List updatedPreferences = new ArrayList<>(); for (NotificationContactType contactType : contactTypes) { UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> x.getChannel() == contactType).findFirst().orElse(null); boolean isUpdate = preference != null; if (preference != null) { preference.setOrdinal(ordinal); } else { preference = new UserNotificationPreferenceEntity(); preference.setUserId(userId); preference.setType(type); preference.setOrdinal(ordinal); preference.setChannel(contactType); preference.setCreatedAt(Instant.now()); preference.setIsActive(IsActive.Active); } preference.setUpdatedAt(Instant.now()); if(isUpdate) this.entityManager.merge(preference); else this.entityManager.persist(preference); updatedPreferences.add(preference); ordinal++; } List toDelete = preferences.stream().filter(x -> !updatedPreferences.stream().map(y-> y.getChannel()).collect(Collectors.toList()).contains(x.getChannel())).collect(Collectors.toList()); for (UserNotificationPreferenceEntity deletable: toDelete) { this.entityManager.remove(deletable); } } catch (InvalidApplicationException e) { logger.error(e.getMessage(), e); } return preferences; } }