From 34310f8cc724f92c25c80f6741214a261fe2490a Mon Sep 17 00:00:00 2001 From: ndc-dxc <162444006+ndc-dxc@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:35:19 +0200 Subject: [PATCH] fixes: (#121) - event windows --- .../ndc/alerter/AlerterService.java | 2 +- .../ndc/alerter/data/EventMapper.java | 10 +++ .../ndc/alerter/data/ProfileService.java | 4 +- .../ndc/service/AlerterMailSender.java | 84 ++++++++++++------- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/main/java/it/gov/innovazione/ndc/alerter/AlerterService.java b/src/main/java/it/gov/innovazione/ndc/alerter/AlerterService.java index 91906cb0..136e04db 100644 --- a/src/main/java/it/gov/innovazione/ndc/alerter/AlerterService.java +++ b/src/main/java/it/gov/innovazione/ndc/alerter/AlerterService.java @@ -32,7 +32,7 @@ public void alert(AlertableEvent alertableEvent) { .build()); } - private String getUser() { + public static String getUser() { return Optional.of(SecurityContextHolder.getContext()) .map(SecurityContext::getAuthentication) .map(Principal::getName) diff --git a/src/main/java/it/gov/innovazione/ndc/alerter/data/EventMapper.java b/src/main/java/it/gov/innovazione/ndc/alerter/data/EventMapper.java index 0ace0a93..a4992dde 100644 --- a/src/main/java/it/gov/innovazione/ndc/alerter/data/EventMapper.java +++ b/src/main/java/it/gov/innovazione/ndc/alerter/data/EventMapper.java @@ -1,7 +1,9 @@ package it.gov.innovazione.ndc.alerter.data; +import it.gov.innovazione.ndc.alerter.AlerterService; import it.gov.innovazione.ndc.alerter.dto.EventDto; import it.gov.innovazione.ndc.alerter.entities.Event; +import org.apache.commons.lang3.StringUtils; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.springframework.beans.factory.annotation.Autowired; @@ -20,8 +22,16 @@ public void setContextUtils(ContextUtils contextUtils) { @Mapping(target = "context", expression = "java(contextUtils.toContext(entity.getContext()))") public abstract EventDto toDto(Event entity); + public static String defaultIfNull(String createdBy) { + if (StringUtils.isEmpty(createdBy)) { + return AlerterService.getUser(); + } + return createdBy; + } + @Override @Mapping(target = "context", expression = "java(contextUtils.fromContext(dto.getContext()))") @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "createdBy", expression = "java(EventMapper.defaultIfNull(dto.getCreatedBy()))") public abstract Event toEntity(EventDto dto); } diff --git a/src/main/java/it/gov/innovazione/ndc/alerter/data/ProfileService.java b/src/main/java/it/gov/innovazione/ndc/alerter/data/ProfileService.java index 5098c7d2..ba45dd65 100644 --- a/src/main/java/it/gov/innovazione/ndc/alerter/data/ProfileService.java +++ b/src/main/java/it/gov/innovazione/ndc/alerter/data/ProfileService.java @@ -33,10 +33,10 @@ public class ProfileService extends EntityService { @Getter(AccessLevel.PROTECTED) private final Sort defaultSorting = Sort.by("name").ascending(); - public void setLastAlertedAt(String id) { + public void setLastAlertedAt(String id, Instant instant) { Profile profile = repository.findById(id) .orElseThrow(() -> new IllegalStateException("Profile not found: " + id)); - profile.setLastAlertedAt(Instant.now()); + profile.setLastAlertedAt(instant); repository.save(profile); } diff --git a/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java b/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java index df213b9d..2b45423b 100644 --- a/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java +++ b/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java @@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -34,33 +33,52 @@ public class AlerterMailSender { private final UserService userService; private final ConfigService configService; - @Value("${alerter.aggregation-time-multiplier:50}") - private final long aggregationTimeMultiplier; - - private static boolean isNotTooRecent(ProfileDto profileDto, EventDto eventDto, Instant now) { - return eventDto.getCreatedAt().plusSeconds(profileDto.getAggregationTime()).isBefore(now); + private static boolean isAlertable(Instant lastAlertedAt, Integer aggregationTime, Instant now) { + return lastAlertedAt.plusSeconds(aggregationTime).isBefore(now); } @Scheduled(fixedDelayString = "${alerter.mail-sender.fixed-delay:6000}") void getEventsAndAlert() { + + log.info("Executing AlerterMailSenderJob"); + Collection profiles = profileService.findAll(); for (ProfileDto profileDto : profiles) { + + log.info("Checking for alertable events for profile: {}", profileDto.getName()); + Instant now = Instant.now(); Instant lastAlertedAt = profileDto.getLastAlertedAt(); - eventService.getEventsNewerThan(lastAlertedAt).stream() - .filter(eventDto -> isNotTooRecent(profileDto, eventDto, now)) - .filter(eventDto -> isNotTooOld(eventDto, profileDto, now)) - .filter(eventDto -> isSeverityGteThanMin(eventDto, profileDto)) - .filter(eventDto -> isApplicableToProfile(eventDto, profileDto)) - .collect(Collectors.groupingBy(EventDto::getCategory)) - .forEach((key, value) -> sendMessages(key, value, profileDto)); - } - } + if (isAlertable(lastAlertedAt, profileDto.getAggregationTime(), now)) { + + List eventsNewerThan = eventService.getEventsNewerThan(lastAlertedAt).stream() + .filter(eventDto -> isApplicableToProfile(eventDto, profileDto)) + .collect(Collectors.toList()); + + List filteredEvents = eventsNewerThan.stream() + .filter(eventDto -> eventDto.getCreatedAt().isBefore(now)) + .filter(eventDto -> isSeverityGteThanMin(eventDto, profileDto)) + .filter(eventDto -> isApplicableToProfile(eventDto, profileDto)) + .collect(Collectors.toList()); + + log.info("Found {} event applicable to profile {}, of which {} match the conditions for alert", + eventsNewerThan.size(), + profileDto.getName(), + filteredEvents); + + filteredEvents.stream() + .collect(Collectors.groupingBy(EventDto::getCategory)) + .forEach((key, value) -> sendMessages(key, value, profileDto)); - private boolean isNotTooOld(EventDto eventDto, ProfileDto profileDto, Instant now) { - return eventDto.getCreatedAt().plusSeconds(aggregationTimeMultiplier * profileDto.getAggregationTime()).isAfter(now); + log.info("Updating profile {} using lastAlertedAt {}", profileDto.getName(), now); + profileService.setLastAlertedAt(profileDto.getId(), now); + return; + } + + log.info("No alertable events found"); + } } private boolean isAlerterEnabled() { @@ -78,7 +96,7 @@ private void sendMessages(EventCategory category, List eventDtos, Prof } if (!isAlerterEnabled()) { - log.warn("Alerter is disabled, no mails will be sent, following events will be stored in the database." + log.warn("Alerter is disabled, no mails will be sent, following events will be just stored in the database." + "Events: {}", eventDtos.stream() .map(EventDto::toString) @@ -88,34 +106,36 @@ private void sendMessages(EventCategory category, List eventDtos, Prof log.info("Sending email for detected {} events, to users with profile {} for category: {}", eventDtos.size(), profileDto.getName(), category); + List recipients = userService.findAll().stream() .filter(user -> StringUtils.equals(user.getProfile(), profileDto.getName())) .collect(Collectors.toList()); + if (!recipients.isEmpty()) { for (UserDto recipient : recipients) { emailService.sendEmail(recipient.getEmail(), "[SCHEMAGOV] [" + category + "] Alerter: Report degli eventi", - getMessageBody(eventDtos, recipient, profileDto)); + getMessageBody(eventDtos, recipient)); } - } else { - log.warn("No recipients found for profile {}, " - + "for this profile no mails will be sent. " - + "It might still be possible these events will be notified to other profiles. " - + "Events: {}", - profileDto.getName(), - eventDtos.stream() - .map(EventDto::toString) - .collect(Collectors.joining(", "))); + return; } - profileService.setLastAlertedAt(profileDto.getId()); + + log.warn("No recipients found for profile {}, " + + "for this profile no mails will be sent. " + + "It might still be possible these events will be notified to other profiles. " + + "Events: {}", + profileDto.getName(), + eventDtos.stream() + .map(EventDto::toString) + .collect(Collectors.joining(", "))); } - private String getMessageBody(List eventDtos, UserDto recipient, ProfileDto profileDto) { + private String getMessageBody(List eventDtos, UserDto recipient) { StringBuilder message = new StringBuilder("Ciao " + recipient.getName() + " " + recipient.getSurname() + ",\n\n" + "Di seguito i dettagli degli eventi riscontrati:\n"); int i = 1; for (EventDto eventDto : eventDtos) { - message.append(getDetailsForEvent(i, eventDto, recipient, profileDto)); + message.append(getDetailsForEvent(i, eventDto)); i++; } message.append("Origine: Generata automaticamente dall'harvester.\n\n"); @@ -123,7 +143,7 @@ private String getMessageBody(List eventDtos, UserDto recipient, Profi return message.toString(); } - private String getDetailsForEvent(int i, EventDto eventDto, UserDto recipient, ProfileDto profileDto) { + private String getDetailsForEvent(int i, EventDto eventDto) { return i + ". Titolo: " + eventDto.getName() + "\n" + "Descrizione: " + eventDto.getDescription() + "\n" + "Severity: " + eventDto.getSeverity() + "\n"