diff --git a/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java b/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java index 82e2ee64b..7a7870062 100644 --- a/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java +++ b/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java @@ -22,6 +22,7 @@ import static javax.servlet.http.HttpServletResponse.SC_OK; import com.google.api.services.dataflow.Dataflow; import com.google.api.services.dataflow.model.Job; +import com.google.common.collect.ImmutableList; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; import google.registry.config.RegistryConfig.Config; @@ -31,15 +32,17 @@ import google.registry.request.Parameter; import google.registry.request.Response; import google.registry.request.auth.Auth; import java.io.IOException; +import java.util.List; import javax.inject.Inject; import org.joda.time.LocalDate; +import org.json.JSONException; /** * Retries until a {@code Dataflow} job with a given {@code jobId} completes, continuing the Spec11 * pipeline accordingly. * - *

This calls {@link Spec11EmailUtils#emailSpec11Reports()} on success or {@link - * Spec11EmailUtils#sendAlertEmail(String, String)} on failure. + *

This calls {@link Spec11EmailUtils#emailSpec11Reports(String, String, List)} ()} on success or + * {@link Spec11EmailUtils#sendAlertEmail(String, String)} on failure. */ @Action(path = PublishSpec11ReportAction.PATH, method = POST, auth = Auth.AUTH_INTERNAL_OR_ADMIN) public class PublishSpec11ReportAction implements Runnable { @@ -51,8 +54,10 @@ public class PublishSpec11ReportAction implements Runnable { private static final String JOB_FAILED = "JOB_STATE_FAILED"; private final String projectId; + private final String spec11EmailBodyTemplate; private final String jobId; private final Spec11EmailUtils emailUtils; + private final Spec11RegistrarThreatMatchesParser spec11RegistrarThreatMatchesParser; private final Dataflow dataflow; private final Response response; private final LocalDate date; @@ -60,14 +65,18 @@ public class PublishSpec11ReportAction implements Runnable { @Inject PublishSpec11ReportAction( @Config("projectId") String projectId, + @Config("spec11EmailBodyTemplate") String spec11EmailBodyTemplate, @Parameter(ReportingModule.PARAM_JOB_ID) String jobId, Spec11EmailUtils emailUtils, + Spec11RegistrarThreatMatchesParser spec11RegistrarThreatMatchesParser, Dataflow dataflow, Response response, LocalDate date) { this.projectId = projectId; + this.spec11EmailBodyTemplate = spec11EmailBodyTemplate; this.jobId = jobId; this.emailUtils = emailUtils; + this.spec11RegistrarThreatMatchesParser = spec11RegistrarThreatMatchesParser; this.dataflow = dataflow; this.response = response; this.date = date; @@ -85,7 +94,10 @@ public class PublishSpec11ReportAction implements Runnable { "Dataflow job %s finished successfully, publishing results if appropriate.", jobId); response.setStatus(SC_OK); if (shouldSendSpec11Email()) { - emailUtils.emailSpec11Reports(); + ImmutableList matchesList = + spec11RegistrarThreatMatchesParser.getRegistrarThreatMatches(); + String subject = String.format("Google Registry Monthly Threat Detector [%s]", date); + emailUtils.emailSpec11Reports(spec11EmailBodyTemplate, subject, matchesList); } break; case JOB_FAILED: @@ -100,7 +112,7 @@ public class PublishSpec11ReportAction implements Runnable { response.setStatus(SC_NOT_MODIFIED); break; } - } catch (IOException e) { + } catch (IOException | JSONException e) { logger.atSevere().withCause(e).log("Failed to publish Spec11 reports."); emailUtils.sendAlertEmail( String.format("Spec11 Publish Failure %s", date), diff --git a/java/google/registry/reporting/spec11/Spec11EmailUtils.java b/java/google/registry/reporting/spec11/Spec11EmailUtils.java index 1661efc93..7b54068e3 100644 --- a/java/google/registry/reporting/spec11/Spec11EmailUtils.java +++ b/java/google/registry/reporting/spec11/Spec11EmailUtils.java @@ -16,63 +16,58 @@ package google.registry.reporting.spec11; import static com.google.common.base.Throwables.getRootCause; -import com.google.common.collect.ImmutableList; import google.registry.beam.spec11.ThreatMatch; import google.registry.config.RegistryConfig.Config; import google.registry.util.Retrier; import google.registry.util.SendEmailService; import java.io.IOException; +import java.util.List; import javax.inject.Inject; import javax.mail.Message; import javax.mail.Message.RecipientType; import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; -import org.joda.time.YearMonth; +import org.joda.time.LocalDate; /** Provides e-mail functionality for Spec11 tasks, such as sending Spec11 reports to registrars. */ public class Spec11EmailUtils { private final SendEmailService emailService; - private final YearMonth yearMonth; + private final LocalDate date; private final String outgoingEmailAddress; private final String alertRecipientAddress; private final String spec11ReplyToAddress; - private final String spec11EmailBodyTemplate; - private final Spec11RegistrarThreatMatchesParser spec11RegistrarThreatMatchesParser; private final Retrier retrier; @Inject Spec11EmailUtils( SendEmailService emailService, - YearMonth yearMonth, + LocalDate date, @Config("gSuiteOutgoingEmailAddress") String outgoingEmailAddress, @Config("alertRecipientEmailAddress") String alertRecipientAddress, @Config("spec11ReplyToEmailAddress") String spec11ReplyToAddress, - @Config("spec11EmailBodyTemplate") String spec11EmailBodyTemplate, - Spec11RegistrarThreatMatchesParser spec11RegistrarThreatMatchesParser, Retrier retrier) { this.emailService = emailService; - this.yearMonth = yearMonth; + this.date = date; this.outgoingEmailAddress = outgoingEmailAddress; this.alertRecipientAddress = alertRecipientAddress; this.spec11ReplyToAddress = spec11ReplyToAddress; - this.spec11RegistrarThreatMatchesParser = spec11RegistrarThreatMatchesParser; - this.spec11EmailBodyTemplate = spec11EmailBodyTemplate; this.retrier = retrier; } /** - * Processes a Spec11 report on GCS for a given month and e-mails registrars based on the - * contents. + * Processes a list of registrar/list-of-threat pairings and sends a notification email to the + * appropriate address. */ - void emailSpec11Reports() { + void emailSpec11Reports( + String spec11EmailBodyTemplate, + String subject, + List registrarThreatMatchesList) { try { retrier.callWithRetry( () -> { - ImmutableList registrarThreatMatchesList = - spec11RegistrarThreatMatchesParser.getRegistrarThreatMatches(); for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesList) { - emailRegistrar(registrarThreatMatches); + emailRegistrar(spec11EmailBodyTemplate, subject, registrarThreatMatches); } }, IOException.class, @@ -80,16 +75,17 @@ public class Spec11EmailUtils { } catch (Throwable e) { // Send an alert with the root cause, unwrapping the retrier's RuntimeException sendAlertEmail( - String.format("Spec11 Emailing Failure %s", yearMonth.toString()), + String.format("Spec11 Emailing Failure %s", date), String.format("Emailing spec11 reports failed due to %s", getRootCause(e).getMessage())); throw new RuntimeException("Emailing spec11 report failed", e); } sendAlertEmail( - String.format("Spec11 Pipeline Success %s", yearMonth.toString()), + String.format("Spec11 Pipeline Success %s", date), "Spec11 reporting completed successfully."); } - private void emailRegistrar(RegistrarThreatMatches registrarThreatMatches) + private void emailRegistrar( + String spec11EmailBodyTemplate, String subject, RegistrarThreatMatches registrarThreatMatches) throws MessagingException { String registrarEmail = registrarThreatMatches.registrarEmailAddress(); StringBuilder threatList = new StringBuilder(); @@ -103,8 +99,7 @@ public class Spec11EmailUtils { .replace("{REPLY_TO_EMAIL}", spec11ReplyToAddress) .replace("{LIST_OF_THREATS}", threatList.toString()); Message msg = emailService.createMessage(); - msg.setSubject( - String.format("Google Registry Monthly Threat Detector [%s]", yearMonth.toString())); + msg.setSubject(subject); msg.setText(body); msg.setFrom(new InternetAddress(outgoingEmailAddress)); msg.addRecipient(RecipientType.TO, new InternetAddress(registrarEmail)); diff --git a/javatests/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java b/javatests/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java index b2a7ba5ad..3a60f3b6e 100644 --- a/javatests/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java +++ b/javatests/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java @@ -15,6 +15,7 @@ package google.registry.reporting.spec11; import static com.google.common.truth.Truth.assertThat; +import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; @@ -42,18 +43,22 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class PublishSpec11ReportActionTest { + private final String spec11BodyTemplate = "{LIST_OF_THREATS}\n{REPLY_TO_EMAIL}"; + private final LocalDate date = new LocalDate(2018, 6, 5); + private Dataflow dataflow; private Projects projects; private Jobs jobs; private Get get; private Spec11EmailUtils emailUtils; + private Spec11RegistrarThreatMatchesParser parser; private Job expectedJob; private FakeResponse response; private PublishSpec11ReportAction publishAction; @Before - public void setUp() throws IOException { + public void setUp() throws Exception { dataflow = mock(Dataflow.class); projects = mock(Projects.class); jobs = mock(Jobs.class); @@ -65,20 +70,40 @@ public class PublishSpec11ReportActionTest { when(get.execute()).thenReturn(expectedJob); emailUtils = mock(Spec11EmailUtils.class); response = new FakeResponse(); + parser = mock(Spec11RegistrarThreatMatchesParser.class); + when(parser.getRegistrarThreatMatches()).thenReturn(sampleThreatMatches()); publishAction = new PublishSpec11ReportAction( - "test-project", "12345", emailUtils, dataflow, response, new LocalDate(2018, 6, 5)); + "test-project", + spec11BodyTemplate, + "12345", + emailUtils, + mock(Spec11RegistrarThreatMatchesParser.class), + dataflow, + response, + date); } @Test - public void testJobDone_emailsResultsOnSecondOfMonth() { + public void testJobDone_emailsResultsOnSecondOfMonth() throws Exception { expectedJob.setCurrentState("JOB_STATE_DONE"); publishAction = new PublishSpec11ReportAction( - "test-project", "12345", emailUtils, dataflow, response, new LocalDate(2018, 6, 2)); + "test-project", + spec11BodyTemplate, + "12345", + emailUtils, + parser, + dataflow, + response, + date.withDayOfMonth(2)); publishAction.run(); assertThat(response.getStatus()).isEqualTo(SC_OK); - verify(emailUtils).emailSpec11Reports(); + verify(emailUtils) + .emailSpec11Reports( + spec11BodyTemplate, + "Google Registry Monthly Threat Detector [2018-06-02]", + sampleThreatMatches()); } @Test diff --git a/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java b/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java index 01d09c5e8..c170e0c54 100644 --- a/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java +++ b/javatests/google/registry/reporting/spec11/Spec11EmailUtilsTest.java @@ -39,7 +39,7 @@ import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; -import org.joda.time.YearMonth; +import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,18 +73,19 @@ public class Spec11EmailUtilsTest { emailUtils = new Spec11EmailUtils( emailService, - new YearMonth(2018, 7), + new LocalDate(2018, 7, 15), "my-sender@test.com", "my-receiver@test.com", "my-reply-to@test.com", - "{LIST_OF_THREATS}\n{REPLY_TO_EMAIL}", - parser, new Retrier(new FakeSleeper(new FakeClock()), RETRY_COUNT)); } @Test - public void testSuccess_emailSpec11Reports() throws MessagingException, IOException { - emailUtils.emailSpec11Reports(); + public void testSuccess_emailSpec11Reports() throws Exception { + emailUtils.emailSpec11Reports( + "{LIST_OF_THREATS}\n{REPLY_TO_EMAIL}", + "Google Registry Monthly Threat Detector [2018-07-15]", + sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). verify(emailService, times(3)).sendMessage(gotMessage.capture()); List capturedMessages = gotMessage.getAllValues(); @@ -93,21 +94,21 @@ public class Spec11EmailUtilsTest { "my-sender@test.com", "a@fake.com", "my-reply-to@test.com", - "Google Registry Monthly Threat Detector [2018-07]", + "Google Registry Monthly Threat Detector [2018-07-15]", "a.com - MALWARE\n\nmy-reply-to@test.com"); validateMessage( capturedMessages.get(1), "my-sender@test.com", "b@fake.com", "my-reply-to@test.com", - "Google Registry Monthly Threat Detector [2018-07]", + "Google Registry Monthly Threat Detector [2018-07-15]", "b.com - MALWARE\nc.com - MALWARE\n\nmy-reply-to@test.com"); validateMessage( capturedMessages.get(2), "my-sender@test.com", "my-receiver@test.com", null, - "Spec11 Pipeline Success 2018-07", + "Spec11 Pipeline Success 2018-07-15", "Spec11 reporting completed successfully."); } @@ -136,7 +137,9 @@ public class Spec11EmailUtilsTest { } }); RuntimeException thrown = - assertThrows(RuntimeException.class, () -> emailUtils.emailSpec11Reports()); + assertThrows( + RuntimeException.class, + () -> emailUtils.emailSpec11Reports("foo", "bar", sampleThreatMatches())); assertThat(thrown).hasMessageThat().isEqualTo("Emailing spec11 report failed"); assertThat(thrown) .hasCauseThat() @@ -151,7 +154,7 @@ public class Spec11EmailUtilsTest { "my-sender@test.com", "my-receiver@test.com", null, - "Spec11 Emailing Failure 2018-07", + "Spec11 Emailing Failure 2018-07-15", "Emailing spec11 reports failed due to expected"); }