EmailNotificationService.java
package cf.maybelambda.httpvalidator.springboot.service;
import com.mailgun.api.v3.MailgunMessagesApi;
import com.mailgun.client.MailgunClient;
import com.mailgun.model.message.Message;
import com.mailgun.model.message.MessageResponse;
import feign.FeignException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import java.rmi.ConnectIOException;
import java.util.List;
import static cf.maybelambda.httpvalidator.springboot.util.HttpSendOutcomeWrapper.NET_ERR_CODE;
import static io.micrometer.common.util.StringUtils.isBlank;
import static io.micrometer.common.util.StringUtils.truncate;
/**
* Service to send email notifications using external SMTP service.
*/
@Service
public class EmailNotificationService {
static final String BODY_LINE1 = "Request URL: ";
static final String BODY_LINE2 = "Response Status Code: ";
static final String BODY_LINE3 = "Response body: ";
static final String APIKEY_PROPERTY = "mailer.apikey";
static final String FROM_PROPERTY = "notifications.from";
static final String TO_PROPERTY = "notifications.to";
private MailgunMessagesApi client;
private static Logger logger = LoggerFactory.getLogger(EmailNotificationService.class);
@Autowired
private Environment env;
public EmailNotificationService(@Value("${" + APIKEY_PROPERTY + "}") String apiKey) {
this.client = MailgunClient.config(apiKey).createApi(MailgunMessagesApi.class);
}
/**
* Builds the email body content from a list of validation results.
*
* @param contents A list of string arrays containing validation results.
* @return The email body as a single string.
*/
String buildMailBody(List<String[]> contents) {
String res = "";
for (String[] c : contents) {
String p0 = BODY_LINE1 + c[0] + "\n";
if (String.valueOf(NET_ERR_CODE).equals(c[1])) {
res += p0 + c[2];
} else {
String p1 = BODY_LINE2 + c[1] + "\n";
String p2 = BODY_LINE3 + (c[2] == null ? "" : truncate(c[2], 800, "..."));
res += p0 + p1 + p2;
}
res += "\n\n\n";
}
return res;
}
/**
* Sends a plain text email with the specified subject and body.
*
* @param subject The subject of the email.
* @param body The body content of the email.
* @throws ConnectIOException If an error occurs while sending the email.
*/
void sendPlainTextEmail(String subject, String body) throws ConnectIOException {
if (!this.isValidConfig()) return;
Message message = Message.builder()
.from(this.getFrom())
.to(this.getTo())
.subject(subject)
.text(body)
.build();
try {
MessageResponse res = this.client.sendMessage(this.getFrom().split("@")[1], message);
logger.info("Email delivery result: " + res.getMessage());
} catch (FeignException e) {
String errmsg = "POST request for delivery of the Notification Email could not be completed.";
logger.error(errmsg);
throw new ConnectIOException(errmsg, e);
}
}
/**
* Sends a notification email with validation task failure results.
*
* @param mailBody A list of validation task failure results.
* @throws ConnectIOException If an error occurs while sending the email.
*/
public void sendVTaskErrorsNotification(@NonNull List<String[]> mailBody) throws ConnectIOException {
String subject = "Notification of HTTP validation results";
String body = this.buildMailBody(mailBody);
this.sendPlainTextEmail(subject, body);
}
/**
* Sends a notification email about application termination.
*
* @param endTime The end time of the application.
* @throws ConnectIOException If an error occurs while sending the email.
*/
public void sendAppTerminatedNotification(String endTime) throws ConnectIOException {
String subject = "HTTP Validator app terminated";
String body = String.format("ContextClosedEvent occurred on: %s.", endTime);
this.sendPlainTextEmail(subject, body);
}
/**
* Checks if the email configuration is valid.
* <p>
* This method verifies that the necessary properties for sending emails
* are properly configured in the environment. Specifically, it checks:
* <ul>
* <li>The "notifications.from" property, which should contain the sender's email address.</li>
* <li>The "notifications.to" property, which should contain the recipient's email address.</li>
* <li>The "mailer.apikey" property, which should contain the API key for authentication with SMTP service.</li>
* </ul>
* If any of these properties are blank or missing, the configuration is considered invalid.
*
* @return True if all necessary properties are set and non-blank, false otherwise.
*/
public boolean isValidConfig() {
return !(isBlank(this.getFrom()) || isBlank(this.getTo()) || isBlank(this.getApiKey()));
}
/**
* Gets the API key of the mailing service from the environment.
*
* @return The API key as a string.
*/
private String getApiKey() { return this.env.getProperty(APIKEY_PROPERTY); }
/**
* Gets the email address through which notifications are sent.
*
* @return The sender email address.
*/
String getFrom() { return this.env.getProperty(FROM_PROPERTY); }
/**
* Gets the email address to which notifications are sent.
*
* @return The recipient email address.
*/
String getTo() { return this.env.getProperty(TO_PROPERTY); }
/**
* Sets the Mailer client; for testing purposes.
*
* @param cl The client to set.
*/
void setClient(MailgunMessagesApi cl) { this.client = cl; }
/**
* Sets the logger; for testing purposes.
*
* @param logger The logger to set.
*/
void setLogger(Logger logger) { EmailNotificationService.logger = logger; }
/**
* Sets the environment; for testing purposes.
*
* @param env The environment to set.
*/
void setEnv(Environment env) { this.env = env; }
}