diff --git a/build.gradle b/build.gradle index 29bcdb6..290076b 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,7 @@ dependencies { implementation 'org.springframework.data:spring-data-elasticsearch' implementation 'org.apache.jena:apache-jena-libs:4.9.0' implementation 'org.apache.jena:jena-querybuilder:4.9.0' + implementation 'org.apache.commons:commons-text:1.12.0' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r' implementation 'org.kohsuke:github-api:1.321' @@ -210,6 +211,7 @@ jacocoTestCoverageVerification { 'it.gov.innovazione.ndc.harvester.service.OnceLogger', 'it.gov.innovazione.ndc.harvester.service.ConfigReaderService', 'it.gov.innovazione.ndc.service.EventCleaner', + 'it.gov.innovazione.ndc.service.TemplateService', 'it.gov.innovazione.ndc.alerter.*' ] } 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 d9af1a5..9e490c7 100644 --- a/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java +++ b/src/main/java/it/gov/innovazione/ndc/service/AlerterMailSender.java @@ -15,12 +15,16 @@ import org.springframework.stereotype.Component; import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static it.gov.innovazione.ndc.harvester.service.ActualConfigService.ConfigKey.ALERTER_ENABLED; import static org.apache.commons.collections4.ListUtils.emptyIfNull; +import static org.apache.commons.text.StringSubstitutor.replace; @Component @RequiredArgsConstructor @@ -32,6 +36,7 @@ public class AlerterMailSender { private final EventService eventService; private final UserService userService; private final ConfigService configService; + private final TemplateService templateService; private static boolean isAlertable(Instant lastAlertedAt, Integer aggregationTime, Instant now) { return lastAlertedAt.plusSeconds(aggregationTime).isBefore(now); @@ -114,9 +119,9 @@ private void sendMessages(EventCategory category, List eventDtos, Prof recipient.getEmail(), eventDtos.size(), category); - emailService.sendEmail(recipient.getEmail(), + emailService.sendHtmlEmail(recipient.getEmail(), "[SCHEMAGOV] [" + category + "] Alerter: Report degli eventi", - getMessageBody(eventDtos, recipient)); + getHtmlMessageBodyFromTemplates(eventDtos, recipient)); } return; } @@ -131,30 +136,47 @@ private void sendMessages(EventCategory category, List eventDtos, Prof .collect(Collectors.joining(", "))); } - 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)); - i++; - } - message.append("Origine: Generata automaticamente dall'harvester.\n\n"); - message.append("Cordiali saluti,\n\nIl team di supporto di Schemagov"); - return message.toString(); + private String getHtmlMessageBodyFromTemplates(List eventDtos, UserDto recipient) { + return replace( + templateService.getAlerterMailTemplate(), + Map.of( + "recipient.name", recipient.getName(), + "recipient.surname", recipient.getSurname(), + "eventList", replace( + templateService.getEventListTemplate(), + Map.of( + "events", getEvents(eventDtos))))); + } + + private String getEvents(List eventDtos) { + return eventDtos.stream() + .map(eventDto -> replace( + templateService.getEventTemplate(), + Map.of( + "event.name", eventDto.getName(), + "event.description", eventDto.getDescription(), + "event.severity", eventDto.getSeverity(), + "event.context", toSubList(eventDto.getContext()), + "event.createdBy", eventDto.getCreatedBy(), + "event.createdAt", toLocalDate(eventDto.getCreatedAt())))) + .collect(Collectors.joining()); } - private String getDetailsForEvent(int i, EventDto eventDto) { - return i + ". Titolo: " + eventDto.getName() + "\n" - + "Descrizione: " + eventDto.getDescription() + "\n" - + "Severity: " + eventDto.getSeverity() + "\n" - + "Contesto: " + eventDto.getContext() + "\n" - + "Creato da: " + eventDto.getCreatedBy() + "\n" - + "Creato il: " + eventDto.getCreatedAt() + "\n\n"; + private String toLocalDate(Instant createdAt) { + return DateTimeFormatter.ofPattern("dd MMMM yyyy HH:mm:ss") + .withZone(ZoneId.systemDefault()) + .format(createdAt); + } + + private String toSubList(Map context) { + return "
    " + + context.entrySet().stream() + .map(entry -> "
  • " + entry.getKey() + ":" + entry.getValue() + "
  • ") + .collect(Collectors.joining()) + + "
"; } private boolean isSeverityGteThanMin(EventDto eventDto, ProfileDto profileDto) { return eventDto.getSeverity().ordinal() >= profileDto.getMinSeverity().ordinal(); } - } diff --git a/src/main/java/it/gov/innovazione/ndc/service/EmailService.java b/src/main/java/it/gov/innovazione/ndc/service/EmailService.java index 680cd66..0cf8ef1 100644 --- a/src/main/java/it/gov/innovazione/ndc/service/EmailService.java +++ b/src/main/java/it/gov/innovazione/ndc/service/EmailService.java @@ -1,14 +1,16 @@ package it.gov.innovazione.ndc.service; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.event.EventListener; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; +import javax.mail.internet.MimeMessage; + @Component @RequiredArgsConstructor @Slf4j @@ -17,8 +19,6 @@ class EmailService { private final JavaMailSender javaMailSender; @Value("${alerter.mail.sender}") private final String from; - @Value("${spring.mail.properties.mail.debug:false}") - private boolean mailDebug; void sendEmail(String to, String subject, String text) { SimpleMailMessage message = new SimpleMailMessage(); @@ -29,13 +29,14 @@ void sendEmail(String to, String subject, String text) { javaMailSender.send(message); } - @EventListener(ApplicationStartedEvent.class) - void debugSendMail() { - if (mailDebug) { - log.info("Sending test email"); - sendEmail("servicedesk-schema@istat.it", "Test", "Test"); - log.info("Test email sent"); - } + @SneakyThrows + void sendHtmlEmail(String to, String subject, String htmlMessage) { + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8"); + helper.setText(htmlMessage, true); + helper.setTo(to); + helper.setSubject(subject); + helper.setFrom(from); + javaMailSender.send(mimeMessage); } - } diff --git a/src/main/java/it/gov/innovazione/ndc/service/TemplateService.java b/src/main/java/it/gov/innovazione/ndc/service/TemplateService.java new file mode 100644 index 0000000..76bc302 --- /dev/null +++ b/src/main/java/it/gov/innovazione/ndc/service/TemplateService.java @@ -0,0 +1,42 @@ +package it.gov.innovazione.ndc.service; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; + +@RequiredArgsConstructor +@Component +@Getter +public class TemplateService { + + @Value("classpath:templates/alerter_mail_template.html") + private final Resource alerterMailTemplateResource; + @Value("classpath:templates/event_list_template.html") + private final Resource eventListTemplateResource; + @Value("classpath:templates/event_template.html") + private final Resource eventTemplateResource; + + private String alerterMailTemplate; + private String eventListTemplate; + private String eventTemplate; + + @PostConstruct + public void init() { + alerterMailTemplate = readTemplate(alerterMailTemplateResource); + eventListTemplate = readTemplate(eventListTemplateResource); + eventTemplate = readTemplate(eventTemplateResource); + } + + @SneakyThrows + private String readTemplate(Resource eventTemplateResource) { + return IOUtils.toString(eventTemplateResource.getInputStream(), StandardCharsets.UTF_8); + } + +} diff --git a/src/main/resources/templates/alerter_mail_template.html b/src/main/resources/templates/alerter_mail_template.html new file mode 100644 index 0000000..00cdf9e --- /dev/null +++ b/src/main/resources/templates/alerter_mail_template.html @@ -0,0 +1,5 @@ +

Ciao ${recipient.name} ${recipient.surname}

+

Di seguito i dettagli degli eventi riscontrati:

+${eventList} +

Origine: Generata automaticamente dall'harvester.

+

Cordiali saluti,
Il team di supporto di Schemagov

\ No newline at end of file diff --git a/src/main/resources/templates/event_list_template.html b/src/main/resources/templates/event_list_template.html new file mode 100644 index 0000000..4a48d25 --- /dev/null +++ b/src/main/resources/templates/event_list_template.html @@ -0,0 +1,3 @@ +
    + ${events} +
\ No newline at end of file diff --git a/src/main/resources/templates/event_template.html b/src/main/resources/templates/event_template.html new file mode 100644 index 0000000..5f7b3ec --- /dev/null +++ b/src/main/resources/templates/event_template.html @@ -0,0 +1,9 @@ +
  • ${event.name} +
      +
    • Descrizione: ${event.description}
    • +
    • Severity: ${event.severity}
    • +
    • Contesto:
      ${event.context}
    • +
    • Creato da: ${event.createdBy}
    • +
    • Creato il: ${event.createdAt}
    • +
    +