RED-3584 / RED-3581 - notification issues

This commit is contained in:
Timo Bejan 2022-06-03 09:29:59 +03:00
parent e4b8dfffe3
commit 356b15c837
9 changed files with 237 additions and 23 deletions

View File

@ -0,0 +1,11 @@
package com.iqser.red.service.persistence.service.v1.api.model.notification;
public enum NotificationType {
ASSIGN_REVIEWER, ASSIGN_APPROVER, UNASSIGNED_FROM_FILE,
DOCUMENT_APPROVED, DOSSIER_OWNER_SET, DOSSIER_OWNER_REMOVED,
USER_BECOMES_DOSSIER_MEMBER, USER_REMOVED_AS_DOSSIER_MEMBER,
USER_PROMOTED_TO_APPROVER, USER_DEGRADED_TO_REVIEWER,
DOSSIER_OWNER_DELETED, DOWNLOAD_READY, DOSSIER_DELETED
}

View File

@ -0,0 +1,45 @@
package com.iqser.red.service.persistence.management.v1.processor.service.persistence;
import com.iqser.red.service.persistence.management.v1.processor.entity.notification.NotificationEntity;
import com.iqser.red.service.persistence.service.v1.api.model.notification.EmailNotificationType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class NotificationEmailService {
public void sendNotificationEmail(String userId, EmailNotificationType emailNotificationType, List<NotificationEntity> notifications) {
switch (emailNotificationType) {
case DAILY:
sendDailyEmail(userId, notifications);
case DAILY_SUMMARY:
sendDailySummaryEmail(userId, notifications);
case WEEKLY_SUMMARY:
sendWeeklySummaryEmail(userId, notifications);
}
}
private void sendWeeklySummaryEmail(String userId, List<NotificationEntity> notifications) {
log.info("Should send weekly notification summary email for {}", userId);
// TODO
}
private void sendDailySummaryEmail(String userId, List<NotificationEntity> notifications) {
log.info("Should send daily notification summary email for {}", userId);
// TODO
}
private void sendDailyEmail(String userId, List<NotificationEntity> notifications) {
log.info("Should send daily notification email for {}", userId);
// TODO
}
}

View File

@ -4,6 +4,7 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.notifica
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationRepository;
import com.iqser.red.service.persistence.service.v1.api.model.audit.AddNotificationRequest;
import com.iqser.red.service.persistence.service.v1.api.model.notification.EmailNotificationType;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
@ -20,7 +21,12 @@ public class NotificationPersistenceService {
private final NotificationRepository notificationRepository;
private final NotificationPreferencesPersistenceService notificationPreferencesPersistenceService;
private final NotificationEmailService notificationEmailService;
public boolean hasNewNotificationsSince(String userId, OffsetDateTime since) {
notificationPreferencesPersistenceService.initializePreferencesIfNotExists(userId);
return notificationRepository.hasInAppNotificationForUser(userId, since.truncatedTo(ChronoUnit.MILLIS)) > 0;
}
@ -30,6 +36,16 @@ public class NotificationPersistenceService {
BeanUtils.copyProperties(addNotificationRequest, notification);
notification.setCreationDate(OffsetDateTime.now());
var notificationPreferences = notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(addNotificationRequest.getUserId());
if (!notificationPreferences.isInAppNotificationsEnabled() || notificationPreferences.getInAppNotifications().contains(notification.getNotificationType())) {
notification.setSoftDeleted(OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
}
if (notificationPreferences.isEmailNotificationsEnabled() && notificationPreferences.getEmailNotifications().contains(notification.getNotificationType())
&& notificationPreferences.getEmailNotificationType() == EmailNotificationType.DAILY) {
notificationEmailService.sendNotificationEmail(notification.getUserId(), EmailNotificationType.DAILY, List.of(notification));
}
notificationRepository.save(notification);
}
@ -66,12 +82,17 @@ public class NotificationPersistenceService {
}
public List<NotificationEntity> getNotifications(String userId, boolean includeSeen) {
public List<NotificationEntity> getNotificationsForEmailSummary(String userId, OffsetDateTime from, OffsetDateTime to) {
return notificationRepository.findEmailNotificationsForUserId(userId, from, to);
}
public List<NotificationEntity> getNotifications(String userId, boolean includeSeen) {
notificationPreferencesPersistenceService.initializePreferencesIfNotExists(userId);
if (includeSeen) {
return notificationRepository.findAppNotificationsForUser(userId);
} else {
return notificationRepository.findNotNotificationsSeenForUser(userId);
return notificationRepository.findUnseenNotificationsForUser(userId);
}
}

View File

@ -1,14 +1,21 @@
package com.iqser.red.service.persistence.management.v1.processor.service.persistence;
import com.iqser.red.service.persistence.management.v1.processor.entity.notification.NotificationPreferencesEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationPreferencesRepository;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.NotificationRepository;
import com.iqser.red.service.persistence.service.v1.api.model.notification.NotificationPreferences;
import com.iqser.red.service.persistence.service.v1.api.model.notification.NotificationType;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@ -16,12 +23,27 @@ public class NotificationPreferencesPersistenceService {
private final NotificationPreferencesRepository notificationPreferencesRepository;
private final NotificationRepository notificationRepository;
@Transactional
public void setNotificationPreference(String userId, NotificationPreferences notification) {
public void setNotificationPreference(String userId, NotificationPreferences newNotificationPreferences) {
notificationPreferencesRepository.findById(userId)
.ifPresentOrElse(n -> BeanUtils.copyProperties(notification, n), () -> {
.ifPresentOrElse(oldNotificationPreferences -> {
BeanUtils.copyProperties(newNotificationPreferences, oldNotificationPreferences);
// if we disabled the notification preferences just now
if (oldNotificationPreferences.isInAppNotificationsEnabled() && !newNotificationPreferences.isInAppNotificationsEnabled()) {
notificationRepository.deleteAllByUserId(userId, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
}
var removedNotificationTypes = new ArrayList<>(oldNotificationPreferences.getInAppNotifications());
removedNotificationTypes.removeAll(newNotificationPreferences.getInAppNotifications());
notificationRepository.deleteAllByUserIdAndNotificationTypeIn(userId, removedNotificationTypes, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS));
}, () -> {
NotificationPreferencesEntity notificationPreferencesEntity = new NotificationPreferencesEntity();
BeanUtils.copyProperties(notification, notificationPreferencesEntity);
BeanUtils.copyProperties(newNotificationPreferences, notificationPreferencesEntity);
notificationPreferencesEntity.setUserId(userId);
notificationPreferencesRepository.save(notificationPreferencesEntity);
});
@ -33,9 +55,26 @@ public class NotificationPreferencesPersistenceService {
}
@Transactional
public NotificationPreferencesEntity getNotificationPreferences(String userId) {
return notificationPreferencesRepository.findById(userId).orElseThrow(() -> {
throw new NotFoundException("Notification preferences not found for userId: " + userId);
public NotificationPreferencesEntity getOrCreateNotificationPreferences(String userId) {
return notificationPreferencesRepository.findById(userId).orElseGet(() -> {
var notificationPreference = new NotificationPreferencesEntity();
notificationPreference.setUserId(userId);
notificationPreference.setEmailNotificationsEnabled(false);
notificationPreference.setInAppNotificationsEnabled(true);
notificationPreference.setInAppNotifications(Arrays.stream(NotificationType.values()).map(Enum::name).collect(Collectors.toList()));
return notificationPreferencesRepository.save(notificationPreference);
});
}
public void initializePreferencesIfNotExists(String userId) {
if (!notificationPreferencesRepository.existsByUserId(userId)) {
getOrCreateNotificationPreferences(userId);
}
}
public List<NotificationPreferencesEntity> findAll() {
return notificationPreferencesRepository.findAll();
}
}

View File

@ -4,4 +4,5 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.notifica
import org.springframework.data.jpa.repository.JpaRepository;
public interface NotificationPreferencesRepository extends JpaRepository<NotificationPreferencesEntity, String> {
boolean existsByUserId(String userId);
}

View File

@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
public interface NotificationRepository extends JpaRepository<NotificationEntity, Long> {
@ -13,30 +14,24 @@ public interface NotificationRepository extends JpaRepository<NotificationEntity
@Query("select count(n) from NotificationEntity n where " +
" ( " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId )) " +
" or" +
" not exists (select e from NotificationPreferencesEntity e where e.userId = :userId) " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId and p.inAppNotificationsEnabled = true )) " +
" ) " +
" and n.softDeleted is null and n.userId = :userId and n.creationDate > :since")
int hasInAppNotificationForUser(String userId, OffsetDateTime since);
@Query("Select n from NotificationEntity n where " +
@Query("select n from NotificationEntity n where " +
" ( " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId )) " +
" or" +
" not exists (select e from NotificationPreferencesEntity e where e.userId = :userId) " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId and p.inAppNotificationsEnabled = true )) " +
" ) " +
" and n.softDeleted is null and n.userId = :userId order by n.creationDate desc")
List<NotificationEntity> findAppNotificationsForUser(String userId);
@Query("Select n from NotificationEntity n where " +
@Query("select n from NotificationEntity n where " +
" ( " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId )) " +
" or" +
" not exists (select e from NotificationPreferencesEntity e where e.userId = :userId) " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.inAppNotifications apn where p.userId = :userId and p.inAppNotificationsEnabled = true )) " +
" ) " +
" and n.seenDate is null and n.softDeleted is null and n.userId = :userId order by n.creationDate desc")
List<NotificationEntity> findNotNotificationsSeenForUser(String userId);
" and n.seenDate is null and n.softDeleted is null and n.userId = :userId order by n.creationDate desc ")
List<NotificationEntity> findUnseenNotificationsForUser(String userId);
@Modifying
@Query("update NotificationEntity n set n.seenDate = :seenDate where n.id = :notificationId and n.userId = :userId")
@ -49,4 +44,21 @@ public interface NotificationRepository extends JpaRepository<NotificationEntity
@Modifying
@Query("update NotificationEntity n set n.softDeleted = :softDeleted where n.id = :notificationId and n.userId = :userId")
int softDelete(String userId, long notificationId, OffsetDateTime softDeleted);
@Modifying
@Query("update NotificationEntity n set n.softDeleted = :softDeleted where n.userId = :userId")
void deleteAllByUserId(String userId, OffsetDateTime softDeleted);
@Modifying
@Query("update NotificationEntity n set n.softDeleted = :softDeleted where n.userId = :userId and n.notificationType in :removedNotificationTypes")
void deleteAllByUserIdAndNotificationTypeIn(String userId, List<String> removedNotificationTypes, OffsetDateTime softDeleted);
@Query("select n from NotificationEntity n where " +
" ( " +
" (exists (select e from NotificationPreferencesEntity e where e.userId = :userId) and n.notificationType in ( select apn from NotificationPreferencesEntity p join p.emailNotifications apn where p.userId = :userId and p.emailNotificationsEnabled = true )) " +
" ) " +
" and n.userId = :userId and n.creationDate :from and n.creationDate <= :to order by n.creationDate desc")
List<NotificationEntity> findEmailNotificationsForUserId(String userId, OffsetDateTime from, OffsetDateTime to);
}

View File

@ -21,7 +21,7 @@ public class NotificationPreferencesController implements NotificationPreference
@Override
public NotificationPreferences getNotificationPreferences(String userId) {
return convert(notificationPreferencesPersistenceService.getNotificationPreferences(userId), NotificationPreferences.class);
return convert(notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(userId), NotificationPreferences.class);
}
@Override

View File

@ -3,6 +3,7 @@ package com.iqser.red.service.peristence.v1.server.jobs;
import com.iqser.red.service.peristence.v1.server.service.job.AutomaticAnalysisJob;
import com.iqser.red.service.peristence.v1.server.service.job.DeletedFilesCleanupJob;
import com.iqser.red.service.peristence.v1.server.service.job.DownloadCleanupJob;
import com.iqser.red.service.peristence.v1.server.service.job.SendNotificationEmailJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -12,6 +13,26 @@ import java.text.ParseException;
@Configuration
public class CreateJobsConfiguration {
@Bean
public JobDetail notificationEmailSummaryJob() {
return JobBuilder.newJob().ofType(SendNotificationEmailJob.class)
.storeDurably()
.withIdentity("SendNotificationEmailJob")
.withDescription("Send summary email with daily/weekly notifications")
.build();
}
@Bean
public Trigger notificationEmailSummaryJobTrigger() throws ParseException {
return TriggerBuilder.newTrigger().forJob(notificationEmailSummaryJob())
.withIdentity("SendNotificationEmailJobTrigger")
.withDescription("Triggers SendNotificationEmailJob every night at 3 AM")
.withSchedule(CronScheduleBuilder.cronSchedule(new CronExpression("0 0 3 * * ?")))
.build();
}
@Bean
public JobDetail automaticAnalysisJobDetail() {
return JobBuilder.newJob().ofType(AutomaticAnalysisJob.class)

View File

@ -0,0 +1,64 @@
package com.iqser.red.service.peristence.v1.server.service.job;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationEmailService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPreferencesPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.model.notification.EmailNotificationType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
@Slf4j
@RequiredArgsConstructor
@Service
public class SendNotificationEmailJob implements Job {
private final NotificationEmailService notificationEmailService;
private final NotificationPersistenceService notificationPersistenceService;
private final NotificationPreferencesPersistenceService notificationPreferencesPersistenceService;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
var allConfiguredPreferences = notificationPreferencesPersistenceService.findAll();
allConfiguredPreferences.forEach(preference -> {
if (preference.isEmailNotificationsEnabled()) {
// summary emails only
if (preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY || preference.getEmailNotificationType() == EmailNotificationType.DAILY_SUMMARY) {
var now = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS);
// Weekly summary is sent monday night
if (now.getDayOfWeek() == DayOfWeek.MONDAY && preference.getEmailNotificationType() == EmailNotificationType.WEEKLY_SUMMARY) {
var from = OffsetDateTime.now().minusDays(7).truncatedTo(ChronoUnit.HOURS).withHour(0);
var to = OffsetDateTime.now().truncatedTo(ChronoUnit.HOURS).withHour(0);
var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to);
notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.WEEKLY_SUMMARY, notifications);
} else {
var from = OffsetDateTime.now().minusDays(1).withHour(0).truncatedTo(ChronoUnit.HOURS);
var to = OffsetDateTime.now().minusDays(1).withHour(23).truncatedTo(ChronoUnit.HOURS);
var notifications = notificationPersistenceService.getNotificationsForEmailSummary(preference.getUserId(), from, to);
notificationEmailService.sendNotificationEmail(preference.getUserId(), EmailNotificationType.DAILY_SUMMARY, notifications);
}
}
}
});
}
}