mirror of
https://github.com/google/nomulus.git
synced 2025-05-15 00:47:11 +02:00
Refactor common email sending utility
The main thrust of this is to create a common POJO that contains email content in a simple way, then have one class that converts that to an email and sends it. Any class that uses email should only have to deal with creating that POJO. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=237883643
This commit is contained in:
parent
792cd21da7
commit
058c12c343
35 changed files with 773 additions and 614 deletions
|
@ -10,6 +10,7 @@ com.google.apis:google-api-services-storage:v1-rev150-1.22.0
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
com.google.auto.value:auto-value-annotations:1.6.2
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.code.gson:gson:2.8.5
|
com.google.code.gson:gson:2.8.5
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
|
|
|
@ -10,6 +10,7 @@ com.google.apis:google-api-services-storage:v1-rev150-1.22.0
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
com.google.auto.value:auto-value-annotations:1.6.2
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.code.gson:gson:2.8.5
|
com.google.code.gson:gson:2.8.5
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
|
|
|
@ -2,6 +2,7 @@ dependencies {
|
||||||
def deps = rootProject.dependencyMap
|
def deps = rootProject.dependencyMap
|
||||||
compile deps['com.google.appengine:appengine-api-1.0-sdk']
|
compile deps['com.google.appengine:appengine-api-1.0-sdk']
|
||||||
compile deps['com.google.appengine:appengine-testing']
|
compile deps['com.google.appengine:appengine-testing']
|
||||||
|
compile deps['com.google.auto.value:auto-value-annotations']
|
||||||
compile deps['com.google.code.findbugs:jsr305']
|
compile deps['com.google.code.findbugs:jsr305']
|
||||||
compile deps['com.google.dagger:dagger']
|
compile deps['com.google.dagger:dagger']
|
||||||
compile deps['com.google.flogger:flogger']
|
compile deps['com.google.flogger:flogger']
|
||||||
|
@ -13,6 +14,7 @@ dependencies {
|
||||||
compile deps['javax.xml.bind:jaxb-api']
|
compile deps['javax.xml.bind:jaxb-api']
|
||||||
compile deps['joda-time:joda-time']
|
compile deps['joda-time:joda-time']
|
||||||
compile deps['org.yaml:snakeyaml']
|
compile deps['org.yaml:snakeyaml']
|
||||||
|
runtime deps['com.google.auto.value:auto-value']
|
||||||
testCompile deps['com.google.appengine:appengine-api-stubs']
|
testCompile deps['com.google.appengine:appengine-api-stubs']
|
||||||
testCompile deps['com.google.guava:guava-testlib']
|
testCompile deps['com.google.guava:guava-testlib']
|
||||||
testCompile deps['com.google.truth:truth']
|
testCompile deps['com.google.truth:truth']
|
||||||
|
@ -23,7 +25,9 @@ dependencies {
|
||||||
testCompile files("${rootDir}/../third_party/objectify/v4_1/objectify-4.1.3.jar")
|
testCompile files("${rootDir}/../third_party/objectify/v4_1/objectify-4.1.3.jar")
|
||||||
testCompile project(':third_party')
|
testCompile project(':third_party')
|
||||||
testCompile project(path: ':core', configuration: 'testRuntime')
|
testCompile project(path: ':core', configuration: 'testRuntime')
|
||||||
|
annotationProcessor deps['com.google.auto.value:auto-value']
|
||||||
annotationProcessor deps['com.google.dagger:dagger-compiler']
|
annotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||||
|
testAnnotationProcessor deps['com.google.auto.value:auto-value']
|
||||||
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
|
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.github.kevinstern:software-and-algorithms:1.0
|
com.github.kevinstern:software-and-algorithms:1.0
|
||||||
com.github.stephenc.jcip:jcip-annotations:1.0-1
|
com.github.stephenc.jcip:jcip-annotations:1.0-1
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.auto:auto-common:0.10
|
com.google.auto:auto-common:0.10
|
||||||
com.google.code.findbugs:jFormatString:3.0.0
|
com.google.code.findbugs:jFormatString:3.0.0
|
||||||
com.google.code.findbugs:jsr305:3.0.0
|
com.google.code.findbugs:jsr305:3.0.0
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
com.google.errorprone:error_prone_annotations:2.2.0
|
com.google.errorprone:error_prone_annotations:2.2.0
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
com.google.errorprone:error_prone_annotations:2.2.0
|
com.google.errorprone:error_prone_annotations:2.2.0
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
com.google.errorprone:error_prone_annotations:2.2.0
|
com.google.errorprone:error_prone_annotations:2.2.0
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
com.google.errorprone:error_prone_annotations:2.2.0
|
com.google.errorprone:error_prone_annotations:2.2.0
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
com.google.appengine:appengine-api-1.0-sdk:1.9.48
|
||||||
com.google.appengine:appengine-testing:1.9.58
|
com.google.appengine:appengine-testing:1.9.58
|
||||||
|
com.google.auto.value:auto-value-annotations:1.6.2
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.code.findbugs:jsr305:3.0.2
|
com.google.code.findbugs:jsr305:3.0.2
|
||||||
com.google.dagger:dagger:2.21
|
com.google.dagger:dagger:2.21
|
||||||
com.google.errorprone:error_prone_annotations:2.2.0
|
com.google.errorprone:error_prone_annotations:2.2.0
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# This file is expected to be part of source control.
|
# This file is expected to be part of source control.
|
||||||
com.github.kevinstern:software-and-algorithms:1.0
|
com.github.kevinstern:software-and-algorithms:1.0
|
||||||
com.github.stephenc.jcip:jcip-annotations:1.0-1
|
com.github.stephenc.jcip:jcip-annotations:1.0-1
|
||||||
|
com.google.auto.value:auto-value:1.6.2
|
||||||
com.google.auto:auto-common:0.10
|
com.google.auto:auto-common:0.10
|
||||||
com.google.code.findbugs:jFormatString:3.0.0
|
com.google.code.findbugs:jFormatString:3.0.0
|
||||||
com.google.code.findbugs:jsr305:3.0.0
|
com.google.code.findbugs:jsr305:3.0.0
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package google.registry.config;
|
package google.registry.config;
|
||||||
|
|
||||||
import static com.google.common.base.Suppliers.memoize;
|
import static com.google.common.base.Suppliers.memoize;
|
||||||
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static google.registry.config.ConfigUtils.makeUrl;
|
import static google.registry.config.ConfigUtils.makeUrl;
|
||||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
@ -41,6 +42,8 @@ import javax.annotation.Nullable;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javax.mail.internet.AddressException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
import org.joda.time.DateTimeConstants;
|
import org.joda.time.DateTimeConstants;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
|
|
||||||
|
@ -510,8 +513,8 @@ public final class RegistryConfig {
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("gSuiteOutgoingEmailAddress")
|
@Config("gSuiteOutgoingEmailAddress")
|
||||||
public static String provideGSuiteOutgoingEmailAddress(RegistryConfigSettings config) {
|
public static InternetAddress provideGSuiteOutgoingEmailAddress(RegistryConfigSettings config) {
|
||||||
return config.gSuite.outgoingEmailAddress;
|
return parseEmailAddress(config.gSuite.outgoingEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -697,9 +700,11 @@ public final class RegistryConfig {
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("invoiceEmailRecipients")
|
@Config("invoiceEmailRecipients")
|
||||||
public static ImmutableList<String> provideInvoiceEmailRecipients(
|
public static ImmutableList<InternetAddress> provideInvoiceEmailRecipients(
|
||||||
RegistryConfigSettings config) {
|
RegistryConfigSettings config) {
|
||||||
return ImmutableList.copyOf(config.billing.invoiceEmailRecipients);
|
return config.billing.invoiceEmailRecipients.stream()
|
||||||
|
.map(RegistryConfig::parseEmailAddress)
|
||||||
|
.collect(toImmutableList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -863,14 +868,13 @@ public final class RegistryConfig {
|
||||||
* <p>This allows us to easily verify the success or failure of periodic tasks by passively
|
* <p>This allows us to easily verify the success or failure of periodic tasks by passively
|
||||||
* checking e-mail.
|
* checking e-mail.
|
||||||
*
|
*
|
||||||
* @see google.registry.reporting.icann.ReportingEmailUtils
|
|
||||||
* @see google.registry.reporting.billing.BillingEmailUtils
|
* @see google.registry.reporting.billing.BillingEmailUtils
|
||||||
* @see google.registry.reporting.spec11.Spec11EmailUtils
|
* @see google.registry.reporting.spec11.Spec11EmailUtils
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("alertRecipientEmailAddress")
|
@Config("alertRecipientEmailAddress")
|
||||||
public static String provideAlertRecipientEmailAddress(RegistryConfigSettings config) {
|
public static InternetAddress provideAlertRecipientEmailAddress(RegistryConfigSettings config) {
|
||||||
return config.misc.alertRecipientEmailAddress;
|
return parseEmailAddress(config.misc.alertRecipientEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -880,8 +884,8 @@ public final class RegistryConfig {
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Config("spec11ReplyToEmailAddress")
|
@Config("spec11ReplyToEmailAddress")
|
||||||
public static String provideSpec11ReplyToEmailAddress(RegistryConfigSettings config) {
|
public static InternetAddress provideSpec11ReplyToEmailAddress(RegistryConfigSettings config) {
|
||||||
return config.misc.spec11ReplyToEmailAddress;
|
return parseEmailAddress(config.misc.spec11ReplyToEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -931,7 +935,7 @@ public final class RegistryConfig {
|
||||||
/**
|
/**
|
||||||
* Number of times to retry a GAE operation when {@code TransientFailureException} is thrown.
|
* Number of times to retry a GAE operation when {@code TransientFailureException} is thrown.
|
||||||
*
|
*
|
||||||
* <p>The number of milliseconds it'll sleep before giving up is {@code 2^n - 2}.
|
* <p>The number of milliseconds it'll sleep before giving up is {@code (2^n - 2) * 100}.
|
||||||
*
|
*
|
||||||
* <p>Note that this uses {@code @Named} instead of {@code @Config} so that it can be used from
|
* <p>Note that this uses {@code @Named} instead of {@code @Config} so that it can be used from
|
||||||
* the low-level util package, which cannot have a dependency on the config package.
|
* the low-level util package, which cannot have a dependency on the config package.
|
||||||
|
@ -940,8 +944,8 @@ public final class RegistryConfig {
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
@Named("transientFailureRetries")
|
@Named("transientFailureRetries")
|
||||||
public static int provideTransientFailureRetries() {
|
public static int provideTransientFailureRetries(RegistryConfigSettings config) {
|
||||||
return 12; // Four seconds.
|
return config.misc.transientFailureRetries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1489,8 +1493,8 @@ public final class RegistryConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the email address that outgoing emails from the app are sent from. */
|
/** Returns the email address that outgoing emails from the app are sent from. */
|
||||||
public static String getGSuiteOutgoingEmailAddress() {
|
public static InternetAddress getGSuiteOutgoingEmailAddress() {
|
||||||
return CONFIG_SETTINGS.get().gSuite.outgoingEmailAddress;
|
return parseEmailAddress(CONFIG_SETTINGS.get().gSuite.outgoingEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the display name that outgoing emails from the app are sent from. */
|
/** Returns the display name that outgoing emails from the app are sent from. */
|
||||||
|
@ -1548,5 +1552,13 @@ public final class RegistryConfig {
|
||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InternetAddress parseEmailAddress(String email) {
|
||||||
|
try {
|
||||||
|
return new InternetAddress(email);
|
||||||
|
} catch (AddressException e) {
|
||||||
|
throw new IllegalArgumentException(String.format("Could not parse email address %s.", email));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private RegistryConfig() {}
|
private RegistryConfig() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,6 +169,7 @@ public class RegistryConfigSettings {
|
||||||
public String alertRecipientEmailAddress;
|
public String alertRecipientEmailAddress;
|
||||||
public String spec11ReplyToEmailAddress;
|
public String spec11ReplyToEmailAddress;
|
||||||
public int asyncDeleteDelaySeconds;
|
public int asyncDeleteDelaySeconds;
|
||||||
|
public int transientFailureRetries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configuration for keyrings (used to store secrets outside of source). */
|
/** Configuration for keyrings (used to store secrets outside of source). */
|
||||||
|
|
|
@ -361,6 +361,10 @@ misc:
|
||||||
# hosts from being used on domains.
|
# hosts from being used on domains.
|
||||||
asyncDeleteDelaySeconds: 90
|
asyncDeleteDelaySeconds: 90
|
||||||
|
|
||||||
|
# Number of times to retry a GAE operation when a transient exception is thrown.
|
||||||
|
# The number of milliseconds it'll sleep before giving up is (2^n - 2) * 100.
|
||||||
|
transientFailureRetries: 12
|
||||||
|
|
||||||
beam:
|
beam:
|
||||||
# The default zone to run Apache Beam (Cloud Dataflow) jobs in.
|
# The default zone to run Apache Beam (Cloud Dataflow) jobs in.
|
||||||
defaultJobZone: us-east1-c
|
defaultJobZone: us-east1-c
|
||||||
|
|
|
@ -27,3 +27,7 @@ caching:
|
||||||
# tests
|
# tests
|
||||||
gSuite:
|
gSuite:
|
||||||
supportGroupEmailAddress:
|
supportGroupEmailAddress:
|
||||||
|
|
||||||
|
misc:
|
||||||
|
# We would rather have failures than timeouts, so reduce the number of retries
|
||||||
|
transientFailureRetries: 3
|
||||||
|
|
|
@ -20,23 +20,17 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.gcs.GcsUtils;
|
import google.registry.gcs.GcsUtils;
|
||||||
import google.registry.reporting.billing.BillingModule.InvoiceDirectoryPrefix;
|
import google.registry.reporting.billing.BillingModule.InvoiceDirectoryPrefix;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.EmailMessage;
|
||||||
|
import google.registry.util.EmailMessage.Attachment;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.mail.BodyPart;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.MessagingException;
|
|
||||||
import javax.mail.Multipart;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeBodyPart;
|
|
||||||
import javax.mail.internet.MimeMultipart;
|
|
||||||
import org.joda.time.YearMonth;
|
import org.joda.time.YearMonth;
|
||||||
|
|
||||||
/** Utility functions for sending emails involving monthly invoices. */
|
/** Utility functions for sending emails involving monthly invoices. */
|
||||||
|
@ -44,27 +38,25 @@ class BillingEmailUtils {
|
||||||
|
|
||||||
private final SendEmailService emailService;
|
private final SendEmailService emailService;
|
||||||
private final YearMonth yearMonth;
|
private final YearMonth yearMonth;
|
||||||
private final String outgoingEmailAddress;
|
private final InternetAddress outgoingEmailAddress;
|
||||||
private final String alertRecipientAddress;
|
private final InternetAddress alertRecipientAddress;
|
||||||
private final ImmutableList<String> invoiceEmailRecipients;
|
private final ImmutableList<InternetAddress> invoiceEmailRecipients;
|
||||||
private final String billingBucket;
|
private final String billingBucket;
|
||||||
private final String invoiceFilePrefix;
|
private final String invoiceFilePrefix;
|
||||||
private final String invoiceDirectoryPrefix;
|
private final String invoiceDirectoryPrefix;
|
||||||
private final GcsUtils gcsUtils;
|
private final GcsUtils gcsUtils;
|
||||||
private final Retrier retrier;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BillingEmailUtils(
|
BillingEmailUtils(
|
||||||
SendEmailService emailService,
|
SendEmailService emailService,
|
||||||
YearMonth yearMonth,
|
YearMonth yearMonth,
|
||||||
@Config("gSuiteOutgoingEmailAddress") String outgoingEmailAddress,
|
@Config("gSuiteOutgoingEmailAddress") InternetAddress outgoingEmailAddress,
|
||||||
@Config("alertRecipientEmailAddress") String alertRecipientAddress,
|
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||||
@Config("invoiceEmailRecipients") ImmutableList<String> invoiceEmailRecipients,
|
@Config("invoiceEmailRecipients") ImmutableList<InternetAddress> invoiceEmailRecipients,
|
||||||
@Config("billingBucket") String billingBucket,
|
@Config("billingBucket") String billingBucket,
|
||||||
@Config("invoiceFilePrefix") String invoiceFilePrefix,
|
@Config("invoiceFilePrefix") String invoiceFilePrefix,
|
||||||
@InvoiceDirectoryPrefix String invoiceDirectoryPrefix,
|
@InvoiceDirectoryPrefix String invoiceDirectoryPrefix,
|
||||||
GcsUtils gcsUtils,
|
GcsUtils gcsUtils) {
|
||||||
Retrier retrier) {
|
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
this.yearMonth = yearMonth;
|
this.yearMonth = yearMonth;
|
||||||
this.outgoingEmailAddress = outgoingEmailAddress;
|
this.outgoingEmailAddress = outgoingEmailAddress;
|
||||||
|
@ -74,50 +66,34 @@ class BillingEmailUtils {
|
||||||
this.invoiceFilePrefix = invoiceFilePrefix;
|
this.invoiceFilePrefix = invoiceFilePrefix;
|
||||||
this.invoiceDirectoryPrefix = invoiceDirectoryPrefix;
|
this.invoiceDirectoryPrefix = invoiceDirectoryPrefix;
|
||||||
this.gcsUtils = gcsUtils;
|
this.gcsUtils = gcsUtils;
|
||||||
this.retrier = retrier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sends an e-mail to all expected recipients with an attached overall invoice from GCS. */
|
/** Sends an e-mail to all expected recipients with an attached overall invoice from GCS. */
|
||||||
void emailOverallInvoice() {
|
void emailOverallInvoice() {
|
||||||
try {
|
try {
|
||||||
retrier.callWithRetry(
|
String invoiceFile = String.format("%s-%s.csv", invoiceFilePrefix, yearMonth);
|
||||||
() -> {
|
|
||||||
String invoiceFile =
|
|
||||||
String.format("%s-%s.csv", invoiceFilePrefix, yearMonth);
|
|
||||||
GcsFilename invoiceFilename =
|
GcsFilename invoiceFilename =
|
||||||
new GcsFilename(billingBucket, invoiceDirectoryPrefix + invoiceFile);
|
new GcsFilename(billingBucket, invoiceDirectoryPrefix + invoiceFile);
|
||||||
try (InputStream in = gcsUtils.openInputStream(invoiceFilename)) {
|
try (InputStream in = gcsUtils.openInputStream(invoiceFilename)) {
|
||||||
Message msg = emailService.createMessage();
|
emailService.sendEmail(
|
||||||
msg.setFrom(new InternetAddress(outgoingEmailAddress));
|
EmailMessage.newBuilder()
|
||||||
for (String recipient : invoiceEmailRecipients) {
|
.setSubject(String.format("Domain Registry invoice data %s", yearMonth))
|
||||||
msg.addRecipient(RecipientType.TO, new InternetAddress(recipient));
|
.setBody(
|
||||||
|
String.format("Attached is the %s invoice for the domain registry.", yearMonth))
|
||||||
|
.setFrom(outgoingEmailAddress)
|
||||||
|
.setRecipients(invoiceEmailRecipients)
|
||||||
|
.setAttachment(
|
||||||
|
Attachment.newBuilder()
|
||||||
|
.setContent(CharStreams.toString(new InputStreamReader(in, UTF_8)))
|
||||||
|
.setContentType(MediaType.CSV_UTF_8)
|
||||||
|
.setFilename(invoiceFile)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
msg.setSubject(
|
|
||||||
String.format("Domain Registry invoice data %s", yearMonth));
|
|
||||||
Multipart multipart = new MimeMultipart();
|
|
||||||
BodyPart textPart = new MimeBodyPart();
|
|
||||||
textPart.setText(
|
|
||||||
String.format(
|
|
||||||
"Attached is the %s invoice for the domain registry.", yearMonth));
|
|
||||||
multipart.addBodyPart(textPart);
|
|
||||||
BodyPart invoicePart = new MimeBodyPart();
|
|
||||||
String invoiceData = CharStreams.toString(new InputStreamReader(in, UTF_8));
|
|
||||||
invoicePart.setContent(invoiceData, "text/csv; charset=utf-8");
|
|
||||||
invoicePart.setFileName(invoiceFile);
|
|
||||||
multipart.addBodyPart(invoicePart);
|
|
||||||
msg.setContent(multipart);
|
|
||||||
msg.saveChanges();
|
|
||||||
emailService.sendMessage(msg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IOException.class,
|
|
||||||
MessagingException.class);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// Strip one layer, because callWithRetry wraps in a RuntimeException
|
// Strip one layer, because callWithRetry wraps in a RuntimeException
|
||||||
sendAlertEmail(
|
sendAlertEmail(
|
||||||
String.format(
|
String.format("Emailing invoice failed due to %s", getRootCause(e).getMessage()));
|
||||||
"Emailing invoice failed due to %s",
|
|
||||||
getRootCause(e).getMessage()));
|
|
||||||
throw new RuntimeException("Emailing invoice failed", e);
|
throw new RuntimeException("Emailing invoice failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,17 +101,13 @@ class BillingEmailUtils {
|
||||||
/** Sends an e-mail to the provided alert e-mail address indicating a billing failure. */
|
/** Sends an e-mail to the provided alert e-mail address indicating a billing failure. */
|
||||||
void sendAlertEmail(String body) {
|
void sendAlertEmail(String body) {
|
||||||
try {
|
try {
|
||||||
retrier.callWithRetry(
|
emailService.sendEmail(
|
||||||
() -> {
|
EmailMessage.newBuilder()
|
||||||
Message msg = emailService.createMessage();
|
.setSubject(String.format("Billing Pipeline Alert: %s", yearMonth))
|
||||||
msg.setFrom(new InternetAddress(outgoingEmailAddress));
|
.setBody(body)
|
||||||
msg.addRecipient(RecipientType.TO, new InternetAddress(alertRecipientAddress));
|
.addRecipient(alertRecipientAddress)
|
||||||
msg.setSubject(String.format("Billing Pipeline Alert: %s", yearMonth));
|
.setFrom(outgoingEmailAddress)
|
||||||
msg.setText(body);
|
.build());
|
||||||
emailService.sendMessage(msg);
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
MessagingException.class);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException("The alert e-mail system failed.", e);
|
throw new RuntimeException("The alert e-mail system failed.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,18 @@ import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.flogger.FluentLogger;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.common.net.MediaType;
|
import com.google.common.net.MediaType;
|
||||||
import google.registry.bigquery.BigqueryJobFailureException;
|
import google.registry.bigquery.BigqueryJobFailureException;
|
||||||
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||||
import google.registry.request.Action;
|
import google.registry.request.Action;
|
||||||
import google.registry.request.Parameter;
|
import google.registry.request.Parameter;
|
||||||
import google.registry.request.Response;
|
import google.registry.request.Response;
|
||||||
import google.registry.request.auth.Auth;
|
import google.registry.request.auth.Auth;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.Retrier;
|
||||||
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
import org.joda.time.Duration;
|
import org.joda.time.Duration;
|
||||||
import org.joda.time.YearMonth;
|
import org.joda.time.YearMonth;
|
||||||
import org.joda.time.format.DateTimeFormat;
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
@ -79,7 +83,10 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||||
@Inject IcannReportingStager stager;
|
@Inject IcannReportingStager stager;
|
||||||
@Inject Retrier retrier;
|
@Inject Retrier retrier;
|
||||||
@Inject Response response;
|
@Inject Response response;
|
||||||
@Inject ReportingEmailUtils emailUtils;
|
@Inject @Config("gSuiteOutgoingEmailAddress") InternetAddress sender;
|
||||||
|
@Inject @Config("alertRecipientEmailAddress") InternetAddress recipient;
|
||||||
|
@Inject SendEmailService emailService;
|
||||||
|
|
||||||
@Inject IcannReportingStagingAction() {}
|
@Inject IcannReportingStagingAction() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,11 +103,16 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||||
stager.createAndUploadManifest(subdir, manifestedFiles);
|
stager.createAndUploadManifest(subdir, manifestedFiles);
|
||||||
|
|
||||||
logger.atInfo().log("Completed staging %d report files.", manifestedFiles.size());
|
logger.atInfo().log("Completed staging %d report files.", manifestedFiles.size());
|
||||||
emailUtils.emailResults(
|
emailService.sendEmail(
|
||||||
"ICANN Monthly report staging summary [SUCCESS]",
|
EmailMessage.newBuilder()
|
||||||
|
.setSubject("ICANN Monthly report staging summary [SUCCESS]")
|
||||||
|
.setBody(
|
||||||
String.format(
|
String.format(
|
||||||
"Completed staging the following %d ICANN reports:\n%s",
|
"Completed staging the following %d ICANN reports:\n%s",
|
||||||
manifestedFiles.size(), Joiner.on('\n').join(manifestedFiles)));
|
manifestedFiles.size(), Joiner.on('\n').join(manifestedFiles)))
|
||||||
|
.addRecipient(recipient)
|
||||||
|
.setFrom(sender)
|
||||||
|
.build());
|
||||||
|
|
||||||
response.setStatus(SC_OK);
|
response.setStatus(SC_OK);
|
||||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||||
|
@ -117,11 +129,13 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||||
},
|
},
|
||||||
BigqueryJobFailureException.class);
|
BigqueryJobFailureException.class);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
emailUtils.emailResults(
|
emailService.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report staging summary [FAILURE]",
|
"ICANN Monthly report staging summary [FAILURE]",
|
||||||
String.format(
|
String.format(
|
||||||
"Staging failed due to %s, check logs for more details.",
|
"Staging failed due to %s, check logs for more details.", getRootCause(e)),
|
||||||
getRootCause(e).toString()));
|
recipient,
|
||||||
|
sender));
|
||||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||||
response.setPayload(
|
response.setPayload(
|
||||||
|
|
|
@ -34,11 +34,14 @@ import google.registry.request.Action;
|
||||||
import google.registry.request.Parameter;
|
import google.registry.request.Parameter;
|
||||||
import google.registry.request.Response;
|
import google.registry.request.Response;
|
||||||
import google.registry.request.auth.Auth;
|
import google.registry.request.auth.Auth;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.Retrier;
|
||||||
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action that uploads the monthly activity/transactions reports from GCS to ICANN via an HTTP PUT.
|
* Action that uploads the monthly activity/transactions reports from GCS to ICANN via an HTTP PUT.
|
||||||
|
@ -74,8 +77,9 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||||
@Inject IcannHttpReporter icannReporter;
|
@Inject IcannHttpReporter icannReporter;
|
||||||
@Inject Retrier retrier;
|
@Inject Retrier retrier;
|
||||||
@Inject Response response;
|
@Inject Response response;
|
||||||
@Inject ReportingEmailUtils emailUtils;
|
@Inject @Config("gSuiteOutgoingEmailAddress") InternetAddress sender;
|
||||||
|
@Inject @Config("alertRecipientEmailAddress") InternetAddress recipient;
|
||||||
|
@Inject SendEmailService emailService;
|
||||||
@Inject
|
@Inject
|
||||||
IcannReportingUploadAction() {}
|
IcannReportingUploadAction() {}
|
||||||
|
|
||||||
|
@ -112,19 +116,18 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emailUploadResults(ImmutableMap<String, Boolean> reportSummary) {
|
private void emailUploadResults(ImmutableMap<String, Boolean> reportSummary) {
|
||||||
emailUtils.emailResults(
|
String subject = String.format(
|
||||||
String.format(
|
|
||||||
"ICANN Monthly report upload summary: %d/%d succeeded",
|
"ICANN Monthly report upload summary: %d/%d succeeded",
|
||||||
reportSummary.values().stream().filter((b) -> b).count(), reportSummary.size()),
|
reportSummary.values().stream().filter((b) -> b).count(), reportSummary.size());
|
||||||
|
String body =
|
||||||
String.format(
|
String.format(
|
||||||
"Report Filename - Upload status:\n%s",
|
"Report Filename - Upload status:\n%s",
|
||||||
reportSummary
|
reportSummary.entrySet().stream()
|
||||||
.entrySet()
|
|
||||||
.stream()
|
|
||||||
.map(
|
.map(
|
||||||
(e) ->
|
(e) ->
|
||||||
String.format("%s - %s", e.getKey(), e.getValue() ? "SUCCESS" : "FAILURE"))
|
String.format("%s - %s", e.getKey(), e.getValue() ? "SUCCESS" : "FAILURE"))
|
||||||
.collect(Collectors.joining("\n"))));
|
.collect(Collectors.joining("\n")));
|
||||||
|
emailService.sendEmail(EmailMessage.create(subject, body, recipient, sender));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<String> getManifestedFiles(String reportBucketname) {
|
private ImmutableList<String> getManifestedFiles(String reportBucketname) {
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package google.registry.reporting.icann;
|
|
||||||
|
|
||||||
import com.google.common.flogger.FluentLogger;
|
|
||||||
import google.registry.config.RegistryConfig.Config;
|
|
||||||
import google.registry.util.SendEmailService;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
|
||||||
|
|
||||||
/** Static utils for emailing reporting results. */
|
|
||||||
public class ReportingEmailUtils {
|
|
||||||
|
|
||||||
@Inject @Config("gSuiteOutgoingEmailAddress") String sender;
|
|
||||||
@Inject @Config("alertRecipientEmailAddress") String recipient;
|
|
||||||
@Inject SendEmailService emailService;
|
|
||||||
@Inject ReportingEmailUtils() {}
|
|
||||||
|
|
||||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
|
||||||
|
|
||||||
void emailResults(String subject, String body) {
|
|
||||||
try {
|
|
||||||
Message msg = emailService.createMessage();
|
|
||||||
logger.atInfo().log("Emailing %s", recipient);
|
|
||||||
msg.setFrom(new InternetAddress(sender));
|
|
||||||
msg.addRecipient(RecipientType.TO, new InternetAddress(recipient));
|
|
||||||
msg.setSubject(subject);
|
|
||||||
msg.setText(body);
|
|
||||||
emailService.sendMessage(msg);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.atWarning().withCause(e).log("E-mail service failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,21 +20,19 @@ import static com.google.common.io.Resources.getResource;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
import com.google.template.soy.SoyFileSet;
|
import com.google.template.soy.SoyFileSet;
|
||||||
import com.google.template.soy.parseinfo.SoyTemplateInfo;
|
import com.google.template.soy.parseinfo.SoyTemplateInfo;
|
||||||
import com.google.template.soy.tofu.SoyTofu;
|
import com.google.template.soy.tofu.SoyTofu;
|
||||||
import com.google.template.soy.tofu.SoyTofu.Renderer;
|
import com.google.template.soy.tofu.SoyTofu.Renderer;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
|
@ -52,29 +50,26 @@ public class Spec11EmailUtils {
|
||||||
.compileToTofu();
|
.compileToTofu();
|
||||||
|
|
||||||
private final SendEmailService emailService;
|
private final SendEmailService emailService;
|
||||||
private final String outgoingEmailAddress;
|
private final InternetAddress outgoingEmailAddress;
|
||||||
private final String alertRecipientAddress;
|
private final InternetAddress alertRecipientAddress;
|
||||||
private final String spec11ReplyToAddress;
|
private final InternetAddress spec11ReplyToAddress;
|
||||||
private final ImmutableList<String> spec11WebResources;
|
private final ImmutableList<String> spec11WebResources;
|
||||||
private final String registryName;
|
private final String registryName;
|
||||||
private final Retrier retrier;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Spec11EmailUtils(
|
Spec11EmailUtils(
|
||||||
SendEmailService emailService,
|
SendEmailService emailService,
|
||||||
@Config("gSuiteOutgoingEmailAddress") String outgoingEmailAddress,
|
@Config("gSuiteOutgoingEmailAddress") InternetAddress outgoingEmailAddress,
|
||||||
@Config("alertRecipientEmailAddress") String alertRecipientAddress,
|
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||||
@Config("spec11ReplyToEmailAddress") String spec11ReplyToAddress,
|
@Config("spec11ReplyToEmailAddress") InternetAddress spec11ReplyToAddress,
|
||||||
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
|
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
|
||||||
@Config("registryName") String registryName,
|
@Config("registryName") String registryName) {
|
||||||
Retrier retrier) {
|
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
this.outgoingEmailAddress = outgoingEmailAddress;
|
this.outgoingEmailAddress = outgoingEmailAddress;
|
||||||
this.alertRecipientAddress = alertRecipientAddress;
|
this.alertRecipientAddress = alertRecipientAddress;
|
||||||
this.spec11ReplyToAddress = spec11ReplyToAddress;
|
this.spec11ReplyToAddress = spec11ReplyToAddress;
|
||||||
this.spec11WebResources = spec11WebResources;
|
this.spec11WebResources = spec11WebResources;
|
||||||
this.registryName = registryName;
|
this.registryName = registryName;
|
||||||
this.retrier = retrier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,14 +82,9 @@ public class Spec11EmailUtils {
|
||||||
String subject,
|
String subject,
|
||||||
Set<RegistrarThreatMatches> registrarThreatMatchesSet) {
|
Set<RegistrarThreatMatches> registrarThreatMatchesSet) {
|
||||||
try {
|
try {
|
||||||
retrier.callWithRetry(
|
|
||||||
() -> {
|
|
||||||
for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesSet) {
|
for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesSet) {
|
||||||
emailRegistrar(date, soyTemplateInfo, subject, registrarThreatMatches);
|
emailRegistrar(date, soyTemplateInfo, subject, registrarThreatMatches);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
IOException.class,
|
|
||||||
MessagingException.class);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// Send an alert with the root cause, unwrapping the retrier's RuntimeException
|
// Send an alert with the root cause, unwrapping the retrier's RuntimeException
|
||||||
sendAlertEmail(
|
sendAlertEmail(
|
||||||
|
@ -113,16 +103,15 @@ public class Spec11EmailUtils {
|
||||||
String subject,
|
String subject,
|
||||||
RegistrarThreatMatches registrarThreatMatches)
|
RegistrarThreatMatches registrarThreatMatches)
|
||||||
throws MessagingException {
|
throws MessagingException {
|
||||||
Message msg = emailService.createMessage();
|
emailService.sendEmail(
|
||||||
msg.setSubject(subject);
|
EmailMessage.newBuilder()
|
||||||
String content = getContent(date, soyTemplateInfo, registrarThreatMatches);
|
.setSubject(subject)
|
||||||
msg.setContent(content, "text/html");
|
.setBody(getContent(date, soyTemplateInfo, registrarThreatMatches))
|
||||||
msg.setHeader("Content-Type", "text/html");
|
.setContentType(MediaType.HTML_UTF_8)
|
||||||
msg.setFrom(new InternetAddress(outgoingEmailAddress));
|
.setFrom(outgoingEmailAddress)
|
||||||
msg.addRecipient(
|
.addRecipient(new InternetAddress(registrarThreatMatches.registrarEmailAddress()))
|
||||||
RecipientType.TO, new InternetAddress(registrarThreatMatches.registrarEmailAddress()));
|
.setBcc(spec11ReplyToAddress)
|
||||||
msg.addRecipient(RecipientType.BCC, new InternetAddress(spec11ReplyToAddress));
|
.build());
|
||||||
emailService.sendMessage(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getContent(
|
private String getContent(
|
||||||
|
@ -144,7 +133,7 @@ public class Spec11EmailUtils {
|
||||||
ImmutableMap.of(
|
ImmutableMap.of(
|
||||||
"date", date.toString(),
|
"date", date.toString(),
|
||||||
"registry", registryName,
|
"registry", registryName,
|
||||||
"replyToEmail", spec11ReplyToAddress,
|
"replyToEmail", spec11ReplyToAddress.getAddress(),
|
||||||
"threats", threatMatchMap,
|
"threats", threatMatchMap,
|
||||||
"resources", spec11WebResources);
|
"resources", spec11WebResources);
|
||||||
renderer.setData(data);
|
renderer.setData(data);
|
||||||
|
@ -154,17 +143,13 @@ public class Spec11EmailUtils {
|
||||||
/** Sends an e-mail indicating the state of the spec11 pipeline, with a given subject and body. */
|
/** Sends an e-mail indicating the state of the spec11 pipeline, with a given subject and body. */
|
||||||
void sendAlertEmail(String subject, String body) {
|
void sendAlertEmail(String subject, String body) {
|
||||||
try {
|
try {
|
||||||
retrier.callWithRetry(
|
emailService.sendEmail(
|
||||||
() -> {
|
EmailMessage.newBuilder()
|
||||||
Message msg = emailService.createMessage();
|
.setFrom(outgoingEmailAddress)
|
||||||
msg.setFrom(new InternetAddress(outgoingEmailAddress));
|
.addRecipient(alertRecipientAddress)
|
||||||
msg.addRecipient(RecipientType.TO, new InternetAddress(alertRecipientAddress));
|
.setBody(body)
|
||||||
msg.setSubject(subject);
|
.setSubject(subject)
|
||||||
msg.setText(body);
|
.build());
|
||||||
emailService.sendMessage(msg);
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
MessagingException.class);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException("The spec11 alert e-mail system failed.", e);
|
throw new RuntimeException("The spec11 alert e-mail system failed.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,16 @@
|
||||||
package google.registry.ui.server;
|
package google.registry.ui.server;
|
||||||
|
|
||||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||||
import static com.google.common.collect.Iterables.toArray;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.flogger.FluentLogger;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import google.registry.config.RegistryConfig.Config;
|
import google.registry.config.RegistryConfig.Config;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.internet.AddressException;
|
import javax.mail.internet.AddressException;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
|
|
||||||
|
@ -37,14 +35,14 @@ public class SendEmailUtils {
|
||||||
|
|
||||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
|
|
||||||
private final String gSuiteOutgoingEmailAddress;
|
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||||
private final String gSuiteOutgoingEmailDisplayName;
|
private final String gSuiteOutgoingEmailDisplayName;
|
||||||
private final SendEmailService emailService;
|
private final SendEmailService emailService;
|
||||||
private final ImmutableList<String> registrarChangesNotificationEmailAddresses;
|
private final ImmutableList<String> registrarChangesNotificationEmailAddresses;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SendEmailUtils(
|
public SendEmailUtils(
|
||||||
@Config("gSuiteOutgoingEmailAddress") String gSuiteOutgoingEmailAddress,
|
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||||
@Config("gSuiteOutgoingEmailDisplayName") String gSuiteOutgoingEmailDisplayName,
|
@Config("gSuiteOutgoingEmailDisplayName") String gSuiteOutgoingEmailDisplayName,
|
||||||
@Config("registrarChangesNotificationEmailAddresses")
|
@Config("registrarChangesNotificationEmailAddresses")
|
||||||
ImmutableList<String> registrarChangesNotificationEmailAddresses,
|
ImmutableList<String> registrarChangesNotificationEmailAddresses,
|
||||||
|
@ -68,10 +66,10 @@ public class SendEmailUtils {
|
||||||
public boolean sendEmail(
|
public boolean sendEmail(
|
||||||
final String subject, String body, ImmutableList<String> additionalAddresses) {
|
final String subject, String body, ImmutableList<String> additionalAddresses) {
|
||||||
try {
|
try {
|
||||||
Message msg = emailService.createMessage();
|
InternetAddress from =
|
||||||
msg.setFrom(
|
new InternetAddress(
|
||||||
new InternetAddress(gSuiteOutgoingEmailAddress, gSuiteOutgoingEmailDisplayName));
|
gSuiteOutgoingEmailAddress.getAddress(), gSuiteOutgoingEmailDisplayName);
|
||||||
List<InternetAddress> emails =
|
ImmutableList<InternetAddress> recipients =
|
||||||
Stream.concat(
|
Stream.concat(
|
||||||
registrarChangesNotificationEmailAddresses.stream(), additionalAddresses.stream())
|
registrarChangesNotificationEmailAddresses.stream(), additionalAddresses.stream())
|
||||||
.map(
|
.map(
|
||||||
|
@ -88,20 +86,23 @@ public class SendEmailUtils {
|
||||||
})
|
})
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(toImmutableList());
|
.collect(toImmutableList());
|
||||||
if (emails.isEmpty()) {
|
if (recipients.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
msg.addRecipients(Message.RecipientType.TO, toArray(emails, InternetAddress.class));
|
emailService.sendEmail(
|
||||||
msg.setSubject(subject);
|
EmailMessage.newBuilder()
|
||||||
msg.setText(body);
|
.setBody(body)
|
||||||
emailService.sendMessage(msg);
|
.setSubject(subject)
|
||||||
|
.setRecipients(recipients)
|
||||||
|
.setFrom(from)
|
||||||
|
.build());
|
||||||
|
return true;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.atSevere().withCause(t).log(
|
logger.atSevere().withCause(t).log(
|
||||||
"Could not email to addresses %s with subject '%s'.",
|
"Could not email to addresses %s with subject '%s'.",
|
||||||
Joiner.on(", ").join(registrarChangesNotificationEmailAddresses), subject);
|
Joiner.on(", ").join(registrarChangesNotificationEmailAddresses), subject);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sends an email from Nomulus to the registrarChangesNotificationEmailAddresses.
|
/** Sends an email from Nomulus to the registrarChangesNotificationEmailAddresses.
|
||||||
|
|
|
@ -11,6 +11,7 @@ java_library(
|
||||||
"//third_party/jaxb",
|
"//third_party/jaxb",
|
||||||
"//third_party/objectify:objectify-v4_1",
|
"//third_party/objectify:objectify-v4_1",
|
||||||
"@com_google_appengine_api_1_0_sdk",
|
"@com_google_appengine_api_1_0_sdk",
|
||||||
|
"@com_google_auto_value",
|
||||||
"@com_google_code_findbugs_jsr305",
|
"@com_google_code_findbugs_jsr305",
|
||||||
"@com_google_dagger",
|
"@com_google_dagger",
|
||||||
"@com_google_errorprone_error_prone_annotations",
|
"@com_google_errorprone_error_prone_annotations",
|
||||||
|
|
94
java/google/registry/util/EmailMessage.java
Normal file
94
java/google/registry/util/EmailMessage.java
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.util;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
|
|
||||||
|
/** Value class representing the content and metadata of an email. */
|
||||||
|
@AutoValue
|
||||||
|
public abstract class EmailMessage {
|
||||||
|
|
||||||
|
public abstract String subject();
|
||||||
|
public abstract String body();
|
||||||
|
public abstract ImmutableList<InternetAddress> recipients();
|
||||||
|
public abstract InternetAddress from();
|
||||||
|
public abstract Optional<InternetAddress> bcc();
|
||||||
|
public abstract Optional<MediaType> contentType();
|
||||||
|
public abstract Optional<Attachment> attachment();
|
||||||
|
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return new AutoValue_EmailMessage.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmailMessage create(
|
||||||
|
String subject, String body, InternetAddress recipient, InternetAddress from) {
|
||||||
|
return newBuilder()
|
||||||
|
.setSubject(subject)
|
||||||
|
.setBody(body)
|
||||||
|
.setRecipients(ImmutableList.of(recipient))
|
||||||
|
.setFrom(from)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder for {@link EmailMessage}. */
|
||||||
|
@AutoValue.Builder
|
||||||
|
public abstract static class Builder {
|
||||||
|
|
||||||
|
public abstract Builder setSubject(String subject);
|
||||||
|
public abstract Builder setBody(String body);
|
||||||
|
public abstract Builder setRecipients(Collection<InternetAddress> recipients);
|
||||||
|
public abstract Builder setFrom(InternetAddress from);
|
||||||
|
public abstract Builder setBcc(InternetAddress bcc);
|
||||||
|
public abstract Builder setContentType(MediaType contentType);
|
||||||
|
public abstract Builder setAttachment(Attachment attachment);
|
||||||
|
|
||||||
|
abstract ImmutableList.Builder<InternetAddress> recipientsBuilder();
|
||||||
|
|
||||||
|
public Builder addRecipient(InternetAddress value) {
|
||||||
|
recipientsBuilder().add(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract EmailMessage build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An attachment to the email, if one exists. */
|
||||||
|
@AutoValue
|
||||||
|
public abstract static class Attachment {
|
||||||
|
|
||||||
|
public abstract MediaType contentType();
|
||||||
|
public abstract String filename();
|
||||||
|
public abstract String content();
|
||||||
|
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return new AutoValue_EmailMessage_Attachment.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder for {@link Attachment}. */
|
||||||
|
@AutoValue.Builder
|
||||||
|
public abstract static class Builder {
|
||||||
|
|
||||||
|
public abstract Builder setContentType(MediaType contentType);
|
||||||
|
public abstract Builder setFilename(String filename);
|
||||||
|
public abstract Builder setContent(String content);
|
||||||
|
public abstract Attachment build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,29 +14,78 @@
|
||||||
|
|
||||||
package google.registry.util;
|
package google.registry.util;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Iterables.toArray;
|
||||||
|
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
|
import google.registry.util.EmailMessage.Attachment;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javax.mail.BodyPart;
|
||||||
import javax.mail.Message;
|
import javax.mail.Message;
|
||||||
|
import javax.mail.Message.RecipientType;
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Multipart;
|
||||||
import javax.mail.Session;
|
import javax.mail.Session;
|
||||||
import javax.mail.Transport;
|
import javax.mail.internet.InternetAddress;
|
||||||
|
import javax.mail.internet.MimeBodyPart;
|
||||||
import javax.mail.internet.MimeMessage;
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import javax.mail.internet.MimeMultipart;
|
||||||
|
|
||||||
/** Wrapper around javax.mail's Transport.send that can be mocked for testing purposes. */
|
/**
|
||||||
|
* Wrapper around javax.mail's email creation and sending functionality. Encompasses a retry policy
|
||||||
|
* as well.
|
||||||
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SendEmailService {
|
public class SendEmailService {
|
||||||
|
|
||||||
@Inject
|
private final Retrier retrier;
|
||||||
SendEmailService() {};
|
private final TransportEmailSender transportEmailSender;
|
||||||
|
|
||||||
/** Returns a new MimeMessage using default App Engine transport settings. */
|
@Inject
|
||||||
public Message createMessage() {
|
SendEmailService(Retrier retrier, TransportEmailSender transportEmailSender) {
|
||||||
return new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
this.retrier = retrier;
|
||||||
|
this.transportEmailSender = transportEmailSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sends a message using default App Engine transport. */
|
/**
|
||||||
public void sendMessage(Message msg) throws MessagingException {
|
* Converts the provided message content into a {@link javax.mail.Message} and sends it with
|
||||||
Transport.send(msg);
|
* retry on transient failures.
|
||||||
|
*/
|
||||||
|
public void sendEmail(EmailMessage emailMessage) {
|
||||||
|
retrier.callWithRetry(
|
||||||
|
() -> {
|
||||||
|
Message msg =
|
||||||
|
new MimeMessage(
|
||||||
|
Session.getDefaultInstance(new Properties(), /* authenticator= */ null));
|
||||||
|
msg.setFrom(emailMessage.from());
|
||||||
|
msg.addRecipients(
|
||||||
|
RecipientType.TO, toArray(emailMessage.recipients(), InternetAddress.class));
|
||||||
|
msg.setSubject(emailMessage.subject());
|
||||||
|
|
||||||
|
Multipart multipart = new MimeMultipart();
|
||||||
|
BodyPart bodyPart = new MimeBodyPart();
|
||||||
|
bodyPart.setContent(
|
||||||
|
emailMessage.body(),
|
||||||
|
emailMessage.contentType().orElse(MediaType.PLAIN_TEXT_UTF_8).toString());
|
||||||
|
multipart.addBodyPart(bodyPart);
|
||||||
|
|
||||||
|
if (emailMessage.attachment().isPresent()) {
|
||||||
|
Attachment attachment = emailMessage.attachment().get();
|
||||||
|
BodyPart attachmentPart = new MimeBodyPart();
|
||||||
|
attachmentPart.setContent(attachment.content(), attachment.contentType().toString());
|
||||||
|
attachmentPart.setFileName(attachment.filename());
|
||||||
|
multipart.addBodyPart(attachmentPart);
|
||||||
|
}
|
||||||
|
if (emailMessage.bcc().isPresent()) {
|
||||||
|
msg.addRecipient(RecipientType.BCC, emailMessage.bcc().get());
|
||||||
|
}
|
||||||
|
msg.setContent(multipart);
|
||||||
|
msg.saveChanges();
|
||||||
|
transportEmailSender.sendMessage(msg);
|
||||||
|
},
|
||||||
|
IOException.class,
|
||||||
|
MessagingException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
java/google/registry/util/TransportEmailSender.java
Normal file
34
java/google/registry/util/TransportEmailSender.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.util;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javax.mail.Message;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.Transport;
|
||||||
|
|
||||||
|
/** Wrapper for sending email so that we can test {@link google.registry.util.SendEmailService}. */
|
||||||
|
@Singleton
|
||||||
|
class TransportEmailSender {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TransportEmailSender() {}
|
||||||
|
|
||||||
|
/** Sends a message using default App Engine transport. */
|
||||||
|
void sendMessage(Message msg) throws MessagingException {
|
||||||
|
Transport.send(msg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,132 +15,94 @@
|
||||||
package google.registry.reporting.billing;
|
package google.registry.reporting.billing;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth8.assertThat;
|
||||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
import google.registry.gcs.GcsUtils;
|
import google.registry.gcs.GcsUtils;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.testing.FakeSleeper;
|
import google.registry.util.EmailMessage.Attachment;
|
||||||
import google.registry.util.Retrier;
|
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Properties;
|
|
||||||
import javax.mail.BodyPart;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.Multipart;
|
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import org.joda.time.YearMonth;
|
import org.joda.time.YearMonth;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
/** Unit tests for {@link google.registry.reporting.billing.BillingEmailUtils}. */
|
/** Unit tests for {@link google.registry.reporting.billing.BillingEmailUtils}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class BillingEmailUtilsTest {
|
public class BillingEmailUtilsTest {
|
||||||
|
|
||||||
private static final int RETRY_COUNT = 2;
|
|
||||||
|
|
||||||
private SendEmailService emailService;
|
private SendEmailService emailService;
|
||||||
private BillingEmailUtils emailUtils;
|
private BillingEmailUtils emailUtils;
|
||||||
private GcsUtils gcsUtils;
|
private GcsUtils gcsUtils;
|
||||||
private ArgumentCaptor<Message> msgCaptor;
|
private ArgumentCaptor<EmailMessage> contentCaptor;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws Exception {
|
||||||
emailService = mock(SendEmailService.class);
|
emailService = mock(SendEmailService.class);
|
||||||
when(emailService.createMessage())
|
|
||||||
.thenReturn(new MimeMessage(Session.getDefaultInstance(new Properties(), null)));
|
|
||||||
gcsUtils = mock(GcsUtils.class);
|
gcsUtils = mock(GcsUtils.class);
|
||||||
when(gcsUtils.openInputStream(new GcsFilename("test-bucket", "results/REG-INV-2017-10.csv")))
|
when(gcsUtils.openInputStream(new GcsFilename("test-bucket", "results/REG-INV-2017-10.csv")))
|
||||||
.thenReturn(
|
.thenReturn(
|
||||||
new ByteArrayInputStream("test,data\nhello,world".getBytes(StandardCharsets.UTF_8)));
|
new ByteArrayInputStream("test,data\nhello,world".getBytes(StandardCharsets.UTF_8)));
|
||||||
msgCaptor = ArgumentCaptor.forClass(Message.class);
|
contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
|
|
||||||
emailUtils =
|
emailUtils =
|
||||||
new BillingEmailUtils(
|
new BillingEmailUtils(
|
||||||
emailService,
|
emailService,
|
||||||
new YearMonth(2017, 10),
|
new YearMonth(2017, 10),
|
||||||
"my-sender@test.com",
|
new InternetAddress("my-sender@test.com"),
|
||||||
"my-receiver@test.com",
|
new InternetAddress("my-receiver@test.com"),
|
||||||
ImmutableList.of("hello@world.com", "hola@mundo.com"),
|
ImmutableList.of(
|
||||||
|
new InternetAddress("hello@world.com"), new InternetAddress("hola@mundo.com")),
|
||||||
"test-bucket",
|
"test-bucket",
|
||||||
"REG-INV",
|
"REG-INV",
|
||||||
"results/",
|
"results/",
|
||||||
gcsUtils,
|
gcsUtils);
|
||||||
new Retrier(new FakeSleeper(new FakeClock()), RETRY_COUNT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_emailOverallInvoice() throws MessagingException, IOException {
|
public void testSuccess_emailOverallInvoice() throws MessagingException {
|
||||||
emailUtils.emailOverallInvoice();
|
emailUtils.emailOverallInvoice();
|
||||||
// We inspect individual parameters because Message doesn't implement equals().
|
|
||||||
verify(emailService).sendMessage(msgCaptor.capture());
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
Message expectedMsg = msgCaptor.getValue();
|
EmailMessage emailMessage = contentCaptor.getValue();
|
||||||
assertThat(expectedMsg.getFrom())
|
EmailMessage expectedContent =
|
||||||
.asList()
|
EmailMessage.newBuilder()
|
||||||
.containsExactly(new InternetAddress("my-sender@test.com"));
|
.setFrom(new InternetAddress("my-sender@test.com"))
|
||||||
assertThat(expectedMsg.getAllRecipients())
|
.setRecipients(
|
||||||
.asList()
|
ImmutableList.of(
|
||||||
.containsExactly(
|
new InternetAddress("hello@world.com"), new InternetAddress("hola@mundo.com")))
|
||||||
new InternetAddress("hello@world.com"), new InternetAddress("hola@mundo.com"));
|
.setSubject("Domain Registry invoice data 2017-10")
|
||||||
assertThat(expectedMsg.getSubject()).isEqualTo("Domain Registry invoice data 2017-10");
|
.setBody("Attached is the 2017-10 invoice for the domain registry.")
|
||||||
assertThat(expectedMsg.getContent()).isInstanceOf(Multipart.class);
|
.setAttachment(
|
||||||
Multipart contents = (Multipart) expectedMsg.getContent();
|
Attachment.newBuilder()
|
||||||
assertThat(contents.getCount()).isEqualTo(2);
|
.setContent("test,data\nhello,world")
|
||||||
assertThat(contents.getBodyPart(0)).isInstanceOf(BodyPart.class);
|
.setContentType(MediaType.CSV_UTF_8)
|
||||||
BodyPart textPart = contents.getBodyPart(0);
|
.setFilename("REG-INV-2017-10.csv")
|
||||||
assertThat(textPart.getContentType()).isEqualTo("text/plain; charset=us-ascii");
|
.build())
|
||||||
assertThat(textPart.getContent().toString())
|
.build();
|
||||||
.isEqualTo("Attached is the 2017-10 invoice for the domain registry.");
|
assertThat(emailMessage).isEqualTo(expectedContent);
|
||||||
assertThat(contents.getBodyPart(1)).isInstanceOf(BodyPart.class);
|
|
||||||
BodyPart attachmentPart = contents.getBodyPart(1);
|
|
||||||
assertThat(attachmentPart.getContentType()).endsWith("name=REG-INV-2017-10.csv");
|
|
||||||
assertThat(attachmentPart.getContent().toString()).isEqualTo("test,data\nhello,world");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_tooManyRetries_emailsAlert() throws MessagingException, IOException {
|
public void testFailure_emailsAlert() throws MessagingException {
|
||||||
// This message throws whenever it tries to set content, to force the overall invoice to fail.
|
doThrow(new RuntimeException(new MessagingException("expected")))
|
||||||
Message throwingMessage = mock(Message.class);
|
.doNothing()
|
||||||
doThrow(new MessagingException("expected"))
|
.when(emailService)
|
||||||
.when(throwingMessage)
|
.sendEmail(contentCaptor.capture());
|
||||||
.setContent(any(Multipart.class));
|
|
||||||
when(emailService.createMessage()).thenAnswer(
|
|
||||||
new Answer<Message>() {
|
|
||||||
private int count = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Message answer(InvocationOnMock invocation) {
|
|
||||||
// Once we've failed the retry limit for the original invoice, return a normal message
|
|
||||||
// so we can properly check its contents.
|
|
||||||
if (count < RETRY_COUNT) {
|
|
||||||
count++;
|
|
||||||
return throwingMessage;
|
|
||||||
} else if (count == RETRY_COUNT) {
|
|
||||||
return new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
} else {
|
|
||||||
assertWithMessage("Attempted to generate too many messages!").fail();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
RuntimeException thrown =
|
RuntimeException thrown =
|
||||||
assertThrows(RuntimeException.class, () -> emailUtils.emailOverallInvoice());
|
assertThrows(RuntimeException.class, () -> emailUtils.emailOverallInvoice());
|
||||||
assertThat(thrown).hasMessageThat().isEqualTo("Emailing invoice failed");
|
assertThat(thrown).hasMessageThat().isEqualTo("Emailing invoice failed");
|
||||||
|
@ -149,26 +111,24 @@ public class BillingEmailUtilsTest {
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.isEqualTo("javax.mail.MessagingException: expected");
|
.isEqualTo("javax.mail.MessagingException: expected");
|
||||||
// Verify we sent an e-mail alert
|
// Verify we sent an e-mail alert
|
||||||
verify(emailService).sendMessage(msgCaptor.capture());
|
verify(emailService, times(2)).sendEmail(contentCaptor.capture());
|
||||||
validateAlertMessage(msgCaptor.getValue(), "Emailing invoice failed due to expected");
|
validateAlertMessage(contentCaptor.getValue(), "Emailing invoice failed due to expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_sendAlertEmail() throws MessagingException, IOException {
|
public void testSuccess_sendAlertEmail() throws MessagingException {
|
||||||
emailUtils.sendAlertEmail("Alert!");
|
emailUtils.sendAlertEmail("Alert!");
|
||||||
verify(emailService).sendMessage(msgCaptor.capture());
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
validateAlertMessage(msgCaptor.getValue(), "Alert!");
|
validateAlertMessage(contentCaptor.getValue(), "Alert!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateAlertMessage(Message msg, String body)
|
private void validateAlertMessage(EmailMessage emailMessage, String body)
|
||||||
throws MessagingException, IOException {
|
throws MessagingException {
|
||||||
assertThat(msg.getFrom()).hasLength(1);
|
assertThat(emailMessage.from()).isEqualTo(new InternetAddress("my-sender@test.com"));
|
||||||
assertThat(msg.getFrom()[0]).isEqualTo(new InternetAddress("my-sender@test.com"));
|
assertThat(emailMessage.recipients())
|
||||||
assertThat(msg.getAllRecipients())
|
|
||||||
.asList()
|
|
||||||
.containsExactly(new InternetAddress("my-receiver@test.com"));
|
.containsExactly(new InternetAddress("my-receiver@test.com"));
|
||||||
assertThat(msg.getSubject()).isEqualTo("Billing Pipeline Alert: 2017-10");
|
assertThat(emailMessage.subject()).isEqualTo("Billing Pipeline Alert: 2017-10");
|
||||||
assertThat(msg.getContentType()).isEqualTo("text/plain");
|
assertThat(emailMessage.contentType()).isEmpty();
|
||||||
assertThat(msg.getContent().toString()).isEqualTo(body);
|
assertThat(emailMessage.body()).isEqualTo(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,11 @@ import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.FakeResponse;
|
import google.registry.testing.FakeResponse;
|
||||||
import google.registry.testing.FakeSleeper;
|
import google.registry.testing.FakeSleeper;
|
||||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.Retrier;
|
||||||
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
import org.joda.time.YearMonth;
|
import org.joda.time.YearMonth;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -48,7 +51,6 @@ public class IcannReportingStagingActionTest {
|
||||||
|
|
||||||
FakeResponse response = new FakeResponse();
|
FakeResponse response = new FakeResponse();
|
||||||
IcannReportingStager stager = mock(IcannReportingStager.class);
|
IcannReportingStager stager = mock(IcannReportingStager.class);
|
||||||
ReportingEmailUtils emailUtils = mock(ReportingEmailUtils.class);
|
|
||||||
YearMonth yearMonth = new YearMonth(2017, 6);
|
YearMonth yearMonth = new YearMonth(2017, 6);
|
||||||
String subdir = "default/dir";
|
String subdir = "default/dir";
|
||||||
IcannReportingStagingAction action;
|
IcannReportingStagingAction action;
|
||||||
|
@ -69,7 +71,9 @@ public class IcannReportingStagingActionTest {
|
||||||
action.response = response;
|
action.response = response;
|
||||||
action.stager = stager;
|
action.stager = stager;
|
||||||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||||
action.emailUtils = emailUtils;
|
action.sender = new InternetAddress("sender@example.com");
|
||||||
|
action.recipient = new InternetAddress("recipient@example.com");
|
||||||
|
action.emailService = mock(SendEmailService.class);
|
||||||
|
|
||||||
when(stager.stageReports(yearMonth, subdir, ReportType.ACTIVITY))
|
when(stager.stageReports(yearMonth, subdir, ReportType.ACTIVITY))
|
||||||
.thenReturn(ImmutableList.of("a", "b"));
|
.thenReturn(ImmutableList.of("a", "b"));
|
||||||
|
@ -92,10 +96,13 @@ public class IcannReportingStagingActionTest {
|
||||||
action.run();
|
action.run();
|
||||||
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b"));
|
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b"));
|
||||||
verify(emailUtils)
|
verify(action.emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report staging summary [SUCCESS]",
|
"ICANN Monthly report staging summary [SUCCESS]",
|
||||||
"Completed staging the following 2 ICANN reports:\na\nb");
|
"Completed staging the following 2 ICANN reports:\na\nb",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
assertUploadTaskEnqueued("default/dir");
|
assertUploadTaskEnqueued("default/dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +112,13 @@ public class IcannReportingStagingActionTest {
|
||||||
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||||
verify(stager).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
verify(stager).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
||||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
||||||
verify(emailUtils)
|
verify(action.emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report staging summary [SUCCESS]",
|
"ICANN Monthly report staging summary [SUCCESS]",
|
||||||
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd");
|
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
assertUploadTaskEnqueued("default/dir");
|
assertUploadTaskEnqueued("default/dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +131,13 @@ public class IcannReportingStagingActionTest {
|
||||||
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||||
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
||||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
||||||
verify(emailUtils)
|
verify(action.emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report staging summary [SUCCESS]",
|
"ICANN Monthly report staging summary [SUCCESS]",
|
||||||
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd");
|
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
assertUploadTaskEnqueued("default/dir");
|
assertUploadTaskEnqueued("default/dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,11 +151,14 @@ public class IcannReportingStagingActionTest {
|
||||||
assertThat(thrown).hasMessageThat().isEqualTo("Staging action failed.");
|
assertThat(thrown).hasMessageThat().isEqualTo("Staging action failed.");
|
||||||
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Expected failure");
|
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("Expected failure");
|
||||||
verify(stager, times(3)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
verify(stager, times(3)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||||
verify(emailUtils)
|
verify(action.emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report staging summary [FAILURE]",
|
"ICANN Monthly report staging summary [FAILURE]",
|
||||||
"Staging failed due to BigqueryJobFailureException: Expected failure,"
|
"Staging failed due to BigqueryJobFailureException: Expected failure,"
|
||||||
+ " check logs for more details.");
|
+ " check logs for more details.",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
// Assert no upload task enqueued
|
// Assert no upload task enqueued
|
||||||
assertNoTasksEnqueued("retryable-cron-tasks");
|
assertNoTasksEnqueued("retryable-cron-tasks");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,11 @@ import google.registry.testing.AppEngineRule;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.FakeResponse;
|
import google.registry.testing.FakeResponse;
|
||||||
import google.registry.testing.FakeSleeper;
|
import google.registry.testing.FakeSleeper;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.Retrier;
|
import google.registry.util.Retrier;
|
||||||
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -51,18 +54,20 @@ public class IcannReportingUploadActionTest {
|
||||||
private static final byte[] MANIFEST_PAYLOAD =
|
private static final byte[] MANIFEST_PAYLOAD =
|
||||||
"test-transactions-201706.csv\na-activity-201706.csv\n".getBytes(UTF_8);
|
"test-transactions-201706.csv\na-activity-201706.csv\n".getBytes(UTF_8);
|
||||||
private final IcannHttpReporter mockReporter = mock(IcannHttpReporter.class);
|
private final IcannHttpReporter mockReporter = mock(IcannHttpReporter.class);
|
||||||
private final ReportingEmailUtils emailUtils = mock(ReportingEmailUtils.class);
|
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||||
private final FakeResponse response = new FakeResponse();
|
private final FakeResponse response = new FakeResponse();
|
||||||
private final GcsService gcsService = GcsServiceFactory.createGcsService();
|
private final GcsService gcsService = GcsServiceFactory.createGcsService();
|
||||||
|
|
||||||
private IcannReportingUploadAction createAction() {
|
private IcannReportingUploadAction createAction() throws Exception {
|
||||||
IcannReportingUploadAction action = new IcannReportingUploadAction();
|
IcannReportingUploadAction action = new IcannReportingUploadAction();
|
||||||
action.icannReporter = mockReporter;
|
action.icannReporter = mockReporter;
|
||||||
action.gcsUtils = new GcsUtils(gcsService, 1024);
|
action.gcsUtils = new GcsUtils(gcsService, 1024);
|
||||||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||||
action.subdir = "icann/monthly/2017-06";
|
action.subdir = "icann/monthly/2017-06";
|
||||||
action.reportingBucket = "basin";
|
action.reportingBucket = "basin";
|
||||||
action.emailUtils = emailUtils;
|
action.emailService = emailService;
|
||||||
|
action.sender = new InternetAddress("sender@example.com");
|
||||||
|
action.recipient = new InternetAddress("recipient@example.com");
|
||||||
action.response = response;
|
action.response = response;
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
@ -94,12 +99,15 @@ public class IcannReportingUploadActionTest {
|
||||||
verifyNoMoreInteractions(mockReporter);
|
verifyNoMoreInteractions(mockReporter);
|
||||||
assertThat(((FakeResponse) action.response).getPayload())
|
assertThat(((FakeResponse) action.response).getPayload())
|
||||||
.isEqualTo("OK, attempted uploading 2 reports");
|
.isEqualTo("OK, attempted uploading 2 reports");
|
||||||
verify(emailUtils)
|
verify(emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report upload summary: 1/2 succeeded",
|
"ICANN Monthly report upload summary: 1/2 succeeded",
|
||||||
"Report Filename - Upload status:\n"
|
"Report Filename - Upload status:\n"
|
||||||
+ "test-transactions-201706.csv - SUCCESS\n"
|
+ "test-transactions-201706.csv - SUCCESS\n"
|
||||||
+ "a-activity-201706.csv - FAILURE");
|
+ "a-activity-201706.csv - FAILURE",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -114,12 +122,15 @@ public class IcannReportingUploadActionTest {
|
||||||
verifyNoMoreInteractions(mockReporter);
|
verifyNoMoreInteractions(mockReporter);
|
||||||
assertThat(((FakeResponse) action.response).getPayload())
|
assertThat(((FakeResponse) action.response).getPayload())
|
||||||
.isEqualTo("OK, attempted uploading 2 reports");
|
.isEqualTo("OK, attempted uploading 2 reports");
|
||||||
verify(emailUtils)
|
verify(emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report upload summary: 1/2 succeeded",
|
"ICANN Monthly report upload summary: 1/2 succeeded",
|
||||||
"Report Filename - Upload status:\n"
|
"Report Filename - Upload status:\n"
|
||||||
+ "test-transactions-201706.csv - SUCCESS\n"
|
+ "test-transactions-201706.csv - SUCCESS\n"
|
||||||
+ "a-activity-201706.csv - FAILURE");
|
+ "a-activity-201706.csv - FAILURE",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -133,15 +144,18 @@ public class IcannReportingUploadActionTest {
|
||||||
verifyNoMoreInteractions(mockReporter);
|
verifyNoMoreInteractions(mockReporter);
|
||||||
assertThat(((FakeResponse) action.response).getPayload())
|
assertThat(((FakeResponse) action.response).getPayload())
|
||||||
.isEqualTo("OK, attempted uploading 2 reports");
|
.isEqualTo("OK, attempted uploading 2 reports");
|
||||||
verify(emailUtils)
|
verify(emailService)
|
||||||
.emailResults(
|
.sendEmail(
|
||||||
|
EmailMessage.create(
|
||||||
"ICANN Monthly report upload summary: 0/2 succeeded",
|
"ICANN Monthly report upload summary: 0/2 succeeded",
|
||||||
"Report Filename - Upload status:\n"
|
"Report Filename - Upload status:\n"
|
||||||
+ "test-transactions-201706.csv - FAILURE\n"
|
+ "test-transactions-201706.csv - FAILURE\n"
|
||||||
+ "a-activity-201706.csv - FAILURE");
|
+ "a-activity-201706.csv - FAILURE",
|
||||||
|
new InternetAddress("recipient@example.com"),
|
||||||
|
new InternetAddress("sender@example.com")));
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testFail_FileNotFound() {
|
public void testFail_FileNotFound() throws Exception {
|
||||||
IcannReportingUploadAction action = createAction();
|
IcannReportingUploadAction action = createAction();
|
||||||
action.subdir = "somewhere/else";
|
action.subdir = "somewhere/else";
|
||||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
|
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package google.registry.reporting.icann;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import google.registry.util.SendEmailService;
|
|
||||||
import java.util.Properties;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link google.registry.reporting.icann.ReportingEmailUtils}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class ReportingEmailUtilsTest {
|
|
||||||
private Message msg;
|
|
||||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
msg = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
when(emailService.createMessage()).thenReturn(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReportingEmailUtils createEmailUtil() {
|
|
||||||
ReportingEmailUtils emailUtils = new ReportingEmailUtils();
|
|
||||||
emailUtils.sender = "test-project.appspotmail.com";
|
|
||||||
emailUtils.recipient = "email@example.com";
|
|
||||||
emailUtils.emailService = emailService;
|
|
||||||
return emailUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_sendsEmail() throws Exception {
|
|
||||||
ReportingEmailUtils emailUtils = createEmailUtil();
|
|
||||||
emailUtils.emailResults("Subject", "Body");
|
|
||||||
|
|
||||||
assertThat(msg.getFrom()).hasLength(1);
|
|
||||||
assertThat(msg.getFrom()[0])
|
|
||||||
.isEqualTo(new InternetAddress("test-project.appspotmail.com"));
|
|
||||||
assertThat(msg.getRecipients(RecipientType.TO)).hasLength(1);
|
|
||||||
assertThat(msg.getRecipients(RecipientType.TO)[0])
|
|
||||||
.isEqualTo(new InternetAddress("email@example.com"));
|
|
||||||
assertThat(msg.getSubject()).isEqualTo("Subject");
|
|
||||||
assertThat(msg.getContentType()).isEqualTo("text/plain");
|
|
||||||
assertThat(msg.getContent().toString()).isEqualTo("Body");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,10 +15,8 @@
|
||||||
package google.registry.reporting.spec11;
|
package google.registry.reporting.spec11;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
|
||||||
import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches;
|
import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches;
|
||||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
|
@ -26,63 +24,47 @@ import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.testing.FakeSleeper;
|
|
||||||
import google.registry.util.Retrier;
|
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Optional;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
/** Unit tests for {@link Spec11EmailUtils}. */
|
/** Unit tests for {@link Spec11EmailUtils}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class Spec11EmailUtilsTest {
|
public class Spec11EmailUtilsTest {
|
||||||
|
|
||||||
private static final int RETRY_COUNT = 2;
|
|
||||||
private static final ImmutableList<String> FAKE_RESOURCES = ImmutableList.of("foo");
|
private static final ImmutableList<String> FAKE_RESOURCES = ImmutableList.of("foo");
|
||||||
|
|
||||||
private SendEmailService emailService;
|
private SendEmailService emailService;
|
||||||
private Spec11EmailUtils emailUtils;
|
private Spec11EmailUtils emailUtils;
|
||||||
private Spec11RegistrarThreatMatchesParser parser;
|
private Spec11RegistrarThreatMatchesParser parser;
|
||||||
private ArgumentCaptor<Message> gotMessage;
|
private ArgumentCaptor<EmailMessage> contentCaptor;
|
||||||
private final LocalDate date = new LocalDate(2018, 7, 15);
|
private final LocalDate date = new LocalDate(2018, 7, 15);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
emailService = mock(SendEmailService.class);
|
emailService = mock(SendEmailService.class);
|
||||||
when(emailService.createMessage())
|
|
||||||
.thenAnswer((args) -> new MimeMessage(Session.getInstance(new Properties(), null)));
|
|
||||||
|
|
||||||
parser = mock(Spec11RegistrarThreatMatchesParser.class);
|
parser = mock(Spec11RegistrarThreatMatchesParser.class);
|
||||||
when(parser.getRegistrarThreatMatches(date)).thenReturn(sampleThreatMatches());
|
when(parser.getRegistrarThreatMatches(date)).thenReturn(sampleThreatMatches());
|
||||||
|
contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
gotMessage = ArgumentCaptor.forClass(Message.class);
|
|
||||||
|
|
||||||
emailUtils =
|
emailUtils =
|
||||||
new Spec11EmailUtils(
|
new Spec11EmailUtils(
|
||||||
emailService,
|
emailService,
|
||||||
"my-sender@test.com",
|
new InternetAddress("my-sender@test.com"),
|
||||||
"my-receiver@test.com",
|
new InternetAddress("my-receiver@test.com"),
|
||||||
"my-reply-to@test.com",
|
new InternetAddress("my-reply-to@test.com"),
|
||||||
FAKE_RESOURCES,
|
FAKE_RESOURCES,
|
||||||
"Super Cool Registry",
|
"Super Cool Registry");
|
||||||
new Retrier(new FakeSleeper(new FakeClock()), RETRY_COUNT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -93,8 +75,8 @@ public class Spec11EmailUtilsTest {
|
||||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||||
sampleThreatMatches());
|
sampleThreatMatches());
|
||||||
// We inspect individual parameters because Message doesn't implement equals().
|
// We inspect individual parameters because Message doesn't implement equals().
|
||||||
verify(emailService, times(3)).sendMessage(gotMessage.capture());
|
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
|
||||||
List<Message> capturedMessages = gotMessage.getAllValues();
|
List<EmailMessage> capturedContents = contentCaptor.getAllValues();
|
||||||
String emailFormat =
|
String emailFormat =
|
||||||
"Dear registrar partner,<p>Super Cool Registry previously notified you when the following "
|
"Dear registrar partner,<p>Super Cool Registry previously notified you when the following "
|
||||||
+ "domains managed by your registrar were flagged for potential security concerns."
|
+ "domains managed by your registrar were flagged for potential security concerns."
|
||||||
|
@ -113,31 +95,31 @@ public class Spec11EmailUtilsTest {
|
||||||
+ "uestions regarding this notice, please contact my-reply-to@test.com.</p>";
|
+ "uestions regarding this notice, please contact my-reply-to@test.com.</p>";
|
||||||
|
|
||||||
validateMessage(
|
validateMessage(
|
||||||
capturedMessages.get(0),
|
capturedContents.get(0),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"a@fake.com",
|
"a@fake.com",
|
||||||
"my-reply-to@test.com",
|
Optional.of("my-reply-to@test.com"),
|
||||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||||
String.format(emailFormat, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
String.format(emailFormat, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||||
"text/html");
|
Optional.of(MediaType.HTML_UTF_8));
|
||||||
validateMessage(
|
validateMessage(
|
||||||
capturedMessages.get(1),
|
capturedContents.get(1),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"b@fake.com",
|
"b@fake.com",
|
||||||
"my-reply-to@test.com",
|
Optional.of("my-reply-to@test.com"),
|
||||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||||
String.format(
|
String.format(
|
||||||
emailFormat,
|
emailFormat,
|
||||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||||
"text/html");
|
Optional.of(MediaType.HTML_UTF_8));
|
||||||
validateMessage(
|
validateMessage(
|
||||||
capturedMessages.get(2),
|
capturedContents.get(2),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"my-receiver@test.com",
|
"my-receiver@test.com",
|
||||||
null,
|
Optional.empty(),
|
||||||
"Spec11 Pipeline Success 2018-07-15",
|
"Spec11 Pipeline Success 2018-07-15",
|
||||||
"Spec11 reporting completed successfully.",
|
"Spec11 reporting completed successfully.",
|
||||||
"text/plain");
|
Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -148,8 +130,8 @@ public class Spec11EmailUtilsTest {
|
||||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||||
sampleThreatMatches());
|
sampleThreatMatches());
|
||||||
// We inspect individual parameters because Message doesn't implement equals().
|
// We inspect individual parameters because Message doesn't implement equals().
|
||||||
verify(emailService, times(3)).sendMessage(gotMessage.capture());
|
verify(emailService, times(3)).sendEmail(contentCaptor.capture());
|
||||||
List<Message> capturedMessages = gotMessage.getAllValues();
|
List<EmailMessage> capturedMessages = contentCaptor.getAllValues();
|
||||||
String emailFormat =
|
String emailFormat =
|
||||||
"Dear registrar partner,<p>"
|
"Dear registrar partner,<p>"
|
||||||
+ "Super Cool Registry conducts a daily analysis of all domains registered in its TLDs "
|
+ "Super Cool Registry conducts a daily analysis of all domains registered in its TLDs "
|
||||||
|
@ -172,54 +154,36 @@ public class Spec11EmailUtilsTest {
|
||||||
capturedMessages.get(0),
|
capturedMessages.get(0),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"a@fake.com",
|
"a@fake.com",
|
||||||
"my-reply-to@test.com",
|
Optional.of("my-reply-to@test.com"),
|
||||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||||
String.format(emailFormat, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
String.format(emailFormat, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||||
"text/html");
|
Optional.of(MediaType.HTML_UTF_8));
|
||||||
validateMessage(
|
validateMessage(
|
||||||
capturedMessages.get(1),
|
capturedMessages.get(1),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"b@fake.com",
|
"b@fake.com",
|
||||||
"my-reply-to@test.com",
|
Optional.of("my-reply-to@test.com"),
|
||||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||||
String.format(
|
String.format(
|
||||||
emailFormat,
|
emailFormat,
|
||||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||||
"text/html");
|
Optional.of(MediaType.HTML_UTF_8));
|
||||||
validateMessage(
|
validateMessage(
|
||||||
capturedMessages.get(2),
|
capturedMessages.get(2),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"my-receiver@test.com",
|
"my-receiver@test.com",
|
||||||
null,
|
Optional.empty(),
|
||||||
"Spec11 Pipeline Success 2018-07-15",
|
"Spec11 Pipeline Success 2018-07-15",
|
||||||
"Spec11 reporting completed successfully.",
|
"Spec11 reporting completed successfully.",
|
||||||
"text/plain");
|
Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_tooManyRetries_emailsAlert() throws MessagingException, IOException {
|
public void testFailure_emailsAlert() throws MessagingException {
|
||||||
Message throwingMessage = mock(Message.class);
|
doThrow(new RuntimeException(new MessagingException("expected")))
|
||||||
doThrow(new MessagingException("expected")).when(throwingMessage).setSubject(any(String.class));
|
.doNothing()
|
||||||
// Only return the throwingMessage enough times to force failure. The last invocation will
|
.when(emailService)
|
||||||
// be for the alert e-mail we're looking to verify.
|
.sendEmail(contentCaptor.capture());
|
||||||
when(emailService.createMessage())
|
|
||||||
.thenAnswer(
|
|
||||||
new Answer<Message>() {
|
|
||||||
private int count = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Message answer(InvocationOnMock invocation) {
|
|
||||||
if (count < RETRY_COUNT) {
|
|
||||||
count++;
|
|
||||||
return throwingMessage;
|
|
||||||
} else if (count == RETRY_COUNT) {
|
|
||||||
return new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
} else {
|
|
||||||
assertWithMessage("Attempted to generate too many messages!").fail();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RuntimeException thrown =
|
RuntimeException thrown =
|
||||||
assertThrows(
|
assertThrows(
|
||||||
RuntimeException.class,
|
RuntimeException.class,
|
||||||
|
@ -231,57 +195,52 @@ public class Spec11EmailUtilsTest {
|
||||||
.hasCauseThat()
|
.hasCauseThat()
|
||||||
.hasMessageThat()
|
.hasMessageThat()
|
||||||
.isEqualTo("javax.mail.MessagingException: expected");
|
.isEqualTo("javax.mail.MessagingException: expected");
|
||||||
// We should have created RETRY_COUNT failing messages and one final alert message
|
|
||||||
verify(emailService, times(RETRY_COUNT + 1)).createMessage();
|
|
||||||
// Verify we sent an e-mail alert
|
// Verify we sent an e-mail alert
|
||||||
verify(emailService).sendMessage(gotMessage.capture());
|
verify(emailService, times(2)).sendEmail(contentCaptor.capture());
|
||||||
validateMessage(
|
validateMessage(
|
||||||
gotMessage.getValue(),
|
contentCaptor.getValue(),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"my-receiver@test.com",
|
"my-receiver@test.com",
|
||||||
null,
|
Optional.empty(),
|
||||||
"Spec11 Emailing Failure 2018-07-15",
|
"Spec11 Emailing Failure 2018-07-15",
|
||||||
"Emailing spec11 reports failed due to expected",
|
"Emailing spec11 reports failed due to expected",
|
||||||
"text/plain");
|
Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_sendAlertEmail() throws MessagingException, IOException {
|
public void testSuccess_sendAlertEmail() throws MessagingException {
|
||||||
emailUtils.sendAlertEmail("Spec11 Pipeline Alert: 2018-07", "Alert!");
|
emailUtils.sendAlertEmail("Spec11 Pipeline Alert: 2018-07", "Alert!");
|
||||||
verify(emailService).sendMessage(gotMessage.capture());
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
validateMessage(
|
validateMessage(
|
||||||
gotMessage.getValue(),
|
contentCaptor.getValue(),
|
||||||
"my-sender@test.com",
|
"my-sender@test.com",
|
||||||
"my-receiver@test.com",
|
"my-receiver@test.com",
|
||||||
null,
|
Optional.empty(),
|
||||||
"Spec11 Pipeline Alert: 2018-07",
|
"Spec11 Pipeline Alert: 2018-07",
|
||||||
"Alert!",
|
"Alert!",
|
||||||
"text/plain");
|
Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMessage(
|
private void validateMessage(
|
||||||
Message message,
|
EmailMessage message,
|
||||||
String from,
|
String from,
|
||||||
String recipient,
|
String recipient,
|
||||||
@Nullable String replyTo,
|
Optional<String> replyTo,
|
||||||
String subject,
|
String subject,
|
||||||
String body,
|
String body,
|
||||||
String contentType)
|
Optional<MediaType> contentType)
|
||||||
throws MessagingException, IOException {
|
throws MessagingException {
|
||||||
assertThat(message.getFrom()).asList().containsExactly(new InternetAddress(from));
|
EmailMessage.Builder expectedContentBuilder =
|
||||||
assertThat(message.getRecipients(RecipientType.TO))
|
EmailMessage.newBuilder()
|
||||||
.asList()
|
.setFrom(new InternetAddress(from))
|
||||||
.containsExactly(new InternetAddress(recipient));
|
.addRecipient(new InternetAddress(recipient))
|
||||||
if (replyTo == null) {
|
.setSubject(subject)
|
||||||
assertThat(message.getRecipients(RecipientType.BCC)).isNull();
|
.setBody(body);
|
||||||
} else {
|
|
||||||
assertThat(message.getRecipients(RecipientType.BCC))
|
if (replyTo.isPresent()) {
|
||||||
.asList()
|
expectedContentBuilder.setBcc(new InternetAddress(replyTo.get()));
|
||||||
.containsExactly(new InternetAddress(replyTo));
|
|
||||||
}
|
}
|
||||||
assertThat(message.getRecipients(RecipientType.CC)).isNull();
|
contentType.ifPresent(expectedContentBuilder::setContentType);
|
||||||
assertThat(message.getSubject()).isEqualTo(subject);
|
assertThat(message).isEqualTo(expectedContentBuilder.build());
|
||||||
assertThat(message.getContentType()).isEqualTo(contentType);
|
|
||||||
assertThat(message.getContent()).isEqualTo(body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,41 +20,28 @@ import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.Properties;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Message.RecipientType;
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
/** Unit tests for {@link SendEmailUtils}. */
|
/** Unit tests for {@link SendEmailUtils}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class SendEmailUtilsTest {
|
public class SendEmailUtilsTest {
|
||||||
|
|
||||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||||
|
|
||||||
private Message message;
|
|
||||||
private SendEmailUtils sendEmailUtils;
|
private SendEmailUtils sendEmailUtils;
|
||||||
|
|
||||||
@Before
|
private void setRecipients(ImmutableList<String> recipients) throws Exception {
|
||||||
public void init() {
|
|
||||||
message = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
when(emailService.createMessage()).thenReturn(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRecipients(ImmutableList<String> recipients) {
|
|
||||||
sendEmailUtils =
|
sendEmailUtils =
|
||||||
new SendEmailUtils(
|
new SendEmailUtils(
|
||||||
"outgoing@registry.example",
|
new InternetAddress("outgoing@registry.example"),
|
||||||
"outgoing display name",
|
"outgoing display name",
|
||||||
recipients,
|
recipients,
|
||||||
emailService);
|
emailService);
|
||||||
|
@ -69,11 +56,7 @@ public class SendEmailUtilsTest {
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
verifyMessageSent();
|
verifyMessageSent("johnny@fakesite.tld");
|
||||||
assertThat(message.getRecipients(RecipientType.TO)).asList()
|
|
||||||
.containsExactly(new InternetAddress("johnny@fakesite.tld"));
|
|
||||||
assertThat(message.getAllRecipients()).asList()
|
|
||||||
.containsExactly(new InternetAddress("johnny@fakesite.tld"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -85,10 +68,7 @@ public class SendEmailUtilsTest {
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
verifyMessageSent();
|
verifyMessageSent("foo@example.com", "bar@example.com");
|
||||||
assertThat(message.getAllRecipients()).asList().containsExactly(
|
|
||||||
new InternetAddress("foo@example.com"),
|
|
||||||
new InternetAddress("bar@example.com"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,9 +80,7 @@ public class SendEmailUtilsTest {
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
verifyMessageSent();
|
verifyMessageSent("foo@example.com");
|
||||||
assertThat(message.getAllRecipients()).asList()
|
|
||||||
.containsExactly(new InternetAddress("foo@example.com"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -114,7 +92,7 @@ public class SendEmailUtilsTest {
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
verify(emailService, never()).sendMessage(any(Message.class));
|
verify(emailService, never()).sendEmail(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -126,23 +104,22 @@ public class SendEmailUtilsTest {
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
verify(emailService, never()).sendMessage(any(Message.class));
|
verify(emailService, never()).sendEmail(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_exceptionThrownDuringSend() throws Exception {
|
public void testFailure_exceptionThrownDuringSend() throws Exception {
|
||||||
setRecipients(ImmutableList.of("foo@example.com"));
|
setRecipients(ImmutableList.of("foo@example.com"));
|
||||||
assertThat(sendEmailUtils.hasRecipients()).isTrue();
|
assertThat(sendEmailUtils.hasRecipients()).isTrue();
|
||||||
doThrow(new MessagingException()).when(emailService).sendMessage(any(Message.class));
|
doThrow(new RuntimeException(new MessagingException("expected")))
|
||||||
|
.when(emailService)
|
||||||
|
.sendEmail(any());
|
||||||
assertThat(
|
assertThat(
|
||||||
sendEmailUtils.sendEmail(
|
sendEmailUtils.sendEmail(
|
||||||
"Welcome to the Internet",
|
"Welcome to the Internet",
|
||||||
"It is a dark and scary place."))
|
"It is a dark and scary place."))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
verifyMessageSent();
|
verifyMessageSent("foo@example.com");
|
||||||
assertThat(message.getAllRecipients())
|
|
||||||
.asList()
|
|
||||||
.containsExactly(new InternetAddress("foo@example.com"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -155,13 +132,7 @@ public class SendEmailUtilsTest {
|
||||||
"It is a dark and scary place.",
|
"It is a dark and scary place.",
|
||||||
ImmutableList.of("bar@example.com", "baz@example.com")))
|
ImmutableList.of("bar@example.com", "baz@example.com")))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
verifyMessageSent();
|
verifyMessageSent("foo@example.com", "bar@example.com", "baz@example.com");
|
||||||
assertThat(message.getAllRecipients())
|
|
||||||
.asList()
|
|
||||||
.containsExactly(
|
|
||||||
new InternetAddress("foo@example.com"),
|
|
||||||
new InternetAddress("bar@example.com"),
|
|
||||||
new InternetAddress("baz@example.com"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -174,16 +145,24 @@ public class SendEmailUtilsTest {
|
||||||
"It is a dark and scary place.",
|
"It is a dark and scary place.",
|
||||||
ImmutableList.of("bar@example.com", "baz@example.com")))
|
ImmutableList.of("bar@example.com", "baz@example.com")))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
verifyMessageSent();
|
verifyMessageSent("bar@example.com", "baz@example.com");
|
||||||
assertThat(message.getAllRecipients())
|
|
||||||
.asList()
|
|
||||||
.containsExactly(
|
|
||||||
new InternetAddress("bar@example.com"), new InternetAddress("baz@example.com"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyMessageSent() throws Exception {
|
private void verifyMessageSent(String... expectedRecipients) throws Exception {
|
||||||
verify(emailService).sendMessage(message);
|
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
assertThat(message.getSubject()).isEqualTo("Welcome to the Internet");
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
assertThat(message.getContent()).isEqualTo("It is a dark and scary place.");
|
EmailMessage emailMessage = contentCaptor.getValue();
|
||||||
|
ImmutableList.Builder<InternetAddress> recipientBuilder = ImmutableList.builder();
|
||||||
|
for (String expectedRecipient : expectedRecipients) {
|
||||||
|
recipientBuilder.add(new InternetAddress(expectedRecipient));
|
||||||
|
}
|
||||||
|
EmailMessage expectedContent =
|
||||||
|
EmailMessage.newBuilder()
|
||||||
|
.setSubject("Welcome to the Internet")
|
||||||
|
.setBody("It is a dark and scary place.")
|
||||||
|
.setFrom(new InternetAddress("outgoing@registry.example"))
|
||||||
|
.setRecipients(recipientBuilder.build())
|
||||||
|
.build();
|
||||||
|
assertThat(emailMessage).isEqualTo(expectedContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import static google.registry.model.registrar.Registrar.loadByClientId;
|
||||||
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
||||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
|
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.appengine.api.users.User;
|
import com.google.appengine.api.users.User;
|
||||||
|
@ -40,18 +41,17 @@ import google.registry.testing.DeterministicStringGenerator;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.FakeResponse;
|
import google.registry.testing.FakeResponse;
|
||||||
import google.registry.ui.server.SendEmailUtils;
|
import google.registry.ui.server.SendEmailUtils;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
@ -72,10 +72,9 @@ public final class ConsoleOteSetupActionTest {
|
||||||
|
|
||||||
@Mock HttpServletRequest request;
|
@Mock HttpServletRequest request;
|
||||||
@Mock SendEmailService emailService;
|
@Mock SendEmailService emailService;
|
||||||
Message message;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws Exception {
|
||||||
persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
|
persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
|
||||||
|
|
||||||
action.req = request;
|
action.req = request;
|
||||||
|
@ -90,7 +89,7 @@ public final class ConsoleOteSetupActionTest {
|
||||||
action.registryEnvironment = RegistryEnvironment.UNITTEST;
|
action.registryEnvironment = RegistryEnvironment.UNITTEST;
|
||||||
action.sendEmailUtils =
|
action.sendEmailUtils =
|
||||||
new SendEmailUtils(
|
new SendEmailUtils(
|
||||||
"outgoing@registry.example",
|
new InternetAddress("outgoing@registry.example"),
|
||||||
"UnitTest Registry",
|
"UnitTest Registry",
|
||||||
ImmutableList.of("notification@test.example", "notification2@test.example"),
|
ImmutableList.of("notification@test.example", "notification2@test.example"),
|
||||||
emailService);
|
emailService);
|
||||||
|
@ -101,9 +100,6 @@ public final class ConsoleOteSetupActionTest {
|
||||||
|
|
||||||
action.optionalPassword = Optional.empty();
|
action.optionalPassword = Optional.empty();
|
||||||
action.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
action.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||||
|
|
||||||
message = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
when(emailService.createMessage()).thenReturn(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -136,7 +132,7 @@ public final class ConsoleOteSetupActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized() throws Exception {
|
public void testPost_authorized() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.email = Optional.of("contact@registry.example");
|
action.email = Optional.of("contact@registry.example");
|
||||||
action.method = Method.POST;
|
action.method = Method.POST;
|
||||||
|
@ -150,8 +146,12 @@ public final class ConsoleOteSetupActionTest {
|
||||||
.isEqualTo("contact@registry.example");
|
.isEqualTo("contact@registry.example");
|
||||||
assertThat(response.getPayload())
|
assertThat(response.getPayload())
|
||||||
.contains("<h1>OT&E successfully created for registrar myclientid!</h1>");
|
.contains("<h1>OT&E successfully created for registrar myclientid!</h1>");
|
||||||
assertThat(message.getSubject()).isEqualTo("OT&E for registrar myclientid created in unittest");
|
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
assertThat(message.getContent())
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
|
EmailMessage emailMessage = contentCaptor.getValue();
|
||||||
|
assertThat(emailMessage.subject())
|
||||||
|
.isEqualTo("OT&E for registrar myclientid created in unittest");
|
||||||
|
assertThat(emailMessage.body())
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
""
|
""
|
||||||
+ "The following entities were created in unittest by TestUserId:\n"
|
+ "The following entities were created in unittest by TestUserId:\n"
|
||||||
|
@ -163,7 +163,7 @@ public final class ConsoleOteSetupActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_setPassword() throws Exception {
|
public void testPost_authorized_setPassword() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.email = Optional.of("contact@registry.example");
|
action.email = Optional.of("contact@registry.example");
|
||||||
action.optionalPassword = Optional.of("SomePassword");
|
action.optionalPassword = Optional.of("SomePassword");
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static google.registry.model.common.GaeUserIdConverter.convertEmailAddres
|
||||||
import static google.registry.model.registrar.Registrar.loadByClientId;
|
import static google.registry.model.registrar.Registrar.loadByClientId;
|
||||||
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
import static google.registry.testing.DatastoreHelper.persistPremiumList;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
|
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.google.appengine.api.users.User;
|
import com.google.appengine.api.users.User;
|
||||||
|
@ -41,12 +42,10 @@ import google.registry.testing.DeterministicStringGenerator;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.FakeResponse;
|
import google.registry.testing.FakeResponse;
|
||||||
import google.registry.ui.server.SendEmailUtils;
|
import google.registry.ui.server.SendEmailUtils;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.joda.money.CurrencyUnit;
|
import org.joda.money.CurrencyUnit;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -54,6 +53,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
@ -74,10 +74,9 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
|
|
||||||
@Mock HttpServletRequest request;
|
@Mock HttpServletRequest request;
|
||||||
@Mock SendEmailService emailService;
|
@Mock SendEmailService emailService;
|
||||||
Message message;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws Exception {
|
||||||
persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
|
persistPremiumList("default_sandbox_list", "sandbox,USD 1000");
|
||||||
|
|
||||||
action.req = request;
|
action.req = request;
|
||||||
|
@ -92,7 +91,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
action.registryEnvironment = RegistryEnvironment.UNITTEST;
|
action.registryEnvironment = RegistryEnvironment.UNITTEST;
|
||||||
action.sendEmailUtils =
|
action.sendEmailUtils =
|
||||||
new SendEmailUtils(
|
new SendEmailUtils(
|
||||||
"outgoing@registry.example",
|
new InternetAddress("outgoing@registry.example"),
|
||||||
"UnitTest Registry",
|
"UnitTest Registry",
|
||||||
ImmutableList.of("notification@test.example", "notification2@test.example"),
|
ImmutableList.of("notification@test.example", "notification2@test.example"),
|
||||||
emailService);
|
emailService);
|
||||||
|
@ -120,9 +119,6 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
|
|
||||||
action.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
action.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||||
action.passcodeGenerator = new DeterministicStringGenerator("314159265");
|
action.passcodeGenerator = new DeterministicStringGenerator("314159265");
|
||||||
|
|
||||||
message = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
when(emailService.createMessage()).thenReturn(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -156,7 +152,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_minimalAddress() throws Exception {
|
public void testPost_authorized_minimalAddress() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.billingAccount = Optional.of("USD=billing-account");
|
action.billingAccount = Optional.of("USD=billing-account");
|
||||||
|
@ -176,8 +172,11 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
assertThat(response.getPayload())
|
assertThat(response.getPayload())
|
||||||
.contains("<h1>Successfully created Registrar myclientid</h1>");
|
.contains("<h1>Successfully created Registrar myclientid</h1>");
|
||||||
|
|
||||||
assertThat(message.getSubject()).isEqualTo("Registrar myclientid created in unittest");
|
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
assertThat(message.getContent())
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
|
EmailMessage emailMessage = contentCaptor.getValue();
|
||||||
|
assertThat(emailMessage.subject()).isEqualTo("Registrar myclientid created in unittest");
|
||||||
|
assertThat(emailMessage.body())
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
""
|
""
|
||||||
+ "The following registrar was created in unittest by TestUserId:\n"
|
+ "The following registrar was created in unittest by TestUserId:\n"
|
||||||
|
@ -221,7 +220,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_allAddress() throws Exception {
|
public void testPost_authorized_allAddress() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.billingAccount = Optional.of("USD=billing-account");
|
action.billingAccount = Optional.of("USD=billing-account");
|
||||||
|
@ -257,7 +256,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_multipleBillingLines() throws Exception {
|
public void testPost_authorized_multipleBillingLines() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.ianaId = Optional.of(12321);
|
action.ianaId = Optional.of(12321);
|
||||||
|
@ -294,7 +293,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_repeatingCurrency_fails() throws Exception {
|
public void testPost_authorized_repeatingCurrency_fails() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.ianaId = Optional.of(12321);
|
action.ianaId = Optional.of(12321);
|
||||||
|
@ -322,7 +321,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_badCurrency_fails() throws Exception {
|
public void testPost_authorized_badCurrency_fails() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.ianaId = Optional.of(12321);
|
action.ianaId = Optional.of(12321);
|
||||||
|
@ -349,7 +348,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_badBillingLine_fails() throws Exception {
|
public void testPost_authorized_badBillingLine_fails() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.ianaId = Optional.of(12321);
|
action.ianaId = Optional.of(12321);
|
||||||
|
@ -378,7 +377,7 @@ public final class ConsoleRegistrarCreatorActionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPost_authorized_setPassword() throws Exception {
|
public void testPost_authorized_setPassword() {
|
||||||
action.clientId = Optional.of("myclientid");
|
action.clientId = Optional.of("myclientid");
|
||||||
action.name = Optional.of("registrar name");
|
action.name = Optional.of("registrar name");
|
||||||
action.billingAccount = Optional.of("USD=billing-account");
|
action.billingAccount = Optional.of("USD=billing-account");
|
||||||
|
|
|
@ -37,6 +37,7 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||||
import google.registry.testing.CertificateSamples;
|
import google.registry.testing.CertificateSamples;
|
||||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||||
import google.registry.util.CidrAddressBlock;
|
import google.registry.util.CidrAddressBlock;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -45,6 +46,7 @@ import org.json.simple.parser.ParseException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
/** Tests for {@link RegistrarSettingsAction}. */
|
/** Tests for {@link RegistrarSettingsAction}. */
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
|
@ -56,7 +58,9 @@ public class RegistrarSettingsActionTest extends RegistrarSettingsActionTestCase
|
||||||
action.handleJsonRequest(readJsonFromFile("update_registrar.json", getLastUpdateTime()));
|
action.handleJsonRequest(readJsonFromFile("update_registrar.json", getLastUpdateTime()));
|
||||||
verify(rsp, never()).setStatus(anyInt());
|
verify(rsp, never()).setStatus(anyInt());
|
||||||
verifyContactsNotified();
|
verifyContactsNotified();
|
||||||
assertThat(message.getContent()).isEqualTo(expectedEmailBody);
|
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
|
verify(emailService).sendEmail(contentCaptor.capture());
|
||||||
|
assertThat(contentCaptor.getValue().body()).isEqualTo(expectedEmailBody);
|
||||||
assertTasksEnqueued("sheet", new TaskMatcher()
|
assertTasksEnqueued("sheet", new TaskMatcher()
|
||||||
.url(SyncRegistrarsSheetAction.PATH)
|
.url(SyncRegistrarsSheetAction.PATH)
|
||||||
.method("GET")
|
.method("GET")
|
||||||
|
|
|
@ -44,14 +44,11 @@ import google.registry.testing.FakeClock;
|
||||||
import google.registry.testing.InjectRule;
|
import google.registry.testing.InjectRule;
|
||||||
import google.registry.ui.server.SendEmailUtils;
|
import google.registry.ui.server.SendEmailUtils;
|
||||||
import google.registry.util.AppEngineServiceUtils;
|
import google.registry.util.AppEngineServiceUtils;
|
||||||
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.SendEmailService;
|
import google.registry.util.SendEmailService;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Properties;
|
|
||||||
import javax.mail.Message;
|
|
||||||
import javax.mail.Session;
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -60,6 +57,7 @@ import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
@ -81,9 +79,6 @@ public class RegistrarSettingsActionTestCase {
|
||||||
@Mock HttpServletRequest req;
|
@Mock HttpServletRequest req;
|
||||||
@Mock HttpServletResponse rsp;
|
@Mock HttpServletResponse rsp;
|
||||||
@Mock SendEmailService emailService;
|
@Mock SendEmailService emailService;
|
||||||
final User user = new User("user", "gmail.com");
|
|
||||||
|
|
||||||
Message message;
|
|
||||||
|
|
||||||
final RegistrarSettingsAction action = new RegistrarSettingsAction();
|
final RegistrarSettingsAction action = new RegistrarSettingsAction();
|
||||||
final StringWriter writer = new StringWriter();
|
final StringWriter writer = new StringWriter();
|
||||||
|
@ -115,8 +110,6 @@ public class RegistrarSettingsActionTestCase {
|
||||||
AuthLevel.USER,
|
AuthLevel.USER,
|
||||||
UserAuthInfo.create(new User("user@email.com", "email.com", "12345"), false));
|
UserAuthInfo.create(new User("user@email.com", "email.com", "12345"), false));
|
||||||
inject.setStaticField(Ofy.class, "clock", clock);
|
inject.setStaticField(Ofy.class, "clock", clock);
|
||||||
message = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
|
||||||
when(emailService.createMessage()).thenReturn(message);
|
|
||||||
when(req.getMethod()).thenReturn("POST");
|
when(req.getMethod()).thenReturn("POST");
|
||||||
when(rsp.getWriter()).thenReturn(new PrintWriter(writer));
|
when(rsp.getWriter()).thenReturn(new PrintWriter(writer));
|
||||||
when(req.getContentType()).thenReturn("application/json");
|
when(req.getContentType()).thenReturn("application/json");
|
||||||
|
@ -128,7 +121,7 @@ public class RegistrarSettingsActionTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() {
|
||||||
assertThat(RegistrarConsoleMetrics.settingsRequestMetric).hasNoOtherValues();
|
assertThat(RegistrarConsoleMetrics.settingsRequestMetric).hasNoOtherValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,10 +155,9 @@ public class RegistrarSettingsActionTestCase {
|
||||||
|
|
||||||
/** Verifies that the original contact of TheRegistrar is among those notified of a change. */
|
/** Verifies that the original contact of TheRegistrar is among those notified of a change. */
|
||||||
protected void verifyContactsNotified() throws Exception {
|
protected void verifyContactsNotified() throws Exception {
|
||||||
verify(emailService).createMessage();
|
ArgumentCaptor<EmailMessage> captor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
verify(emailService).sendMessage(message);
|
verify(emailService).sendEmail(captor.capture());
|
||||||
Truth.assertThat(message.getAllRecipients())
|
Truth.assertThat(captor.getValue().recipients())
|
||||||
.asList()
|
|
||||||
.containsExactly(
|
.containsExactly(
|
||||||
new InternetAddress("notification@test.example"),
|
new InternetAddress("notification@test.example"),
|
||||||
new InternetAddress("notification2@test.example"),
|
new InternetAddress("notification2@test.example"),
|
||||||
|
|
162
javatests/google/registry/util/SendEmailServiceTest.java
Normal file
162
javatests/google/registry/util/SendEmailServiceTest.java
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package google.registry.util;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import com.google.common.net.MediaType;
|
||||||
|
import google.registry.testing.FakeClock;
|
||||||
|
import google.registry.testing.FakeSleeper;
|
||||||
|
import google.registry.util.EmailMessage.Attachment;
|
||||||
|
import javax.mail.BodyPart;
|
||||||
|
import javax.mail.Message;
|
||||||
|
import javax.mail.Message.RecipientType;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.internet.InternetAddress;
|
||||||
|
import javax.mail.internet.MimeMultipart;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
/** Unit tests for {@link SendEmailService}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class SendEmailServiceTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final MockitoRule mocks = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
private final Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 2);
|
||||||
|
private final TransportEmailSender wrapper = mock(TransportEmailSender.class);
|
||||||
|
private final SendEmailService sendEmailService = new SendEmailService(retrier, wrapper);
|
||||||
|
|
||||||
|
@Captor private ArgumentCaptor<Message> messageCaptor;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccess_simple() throws Exception {
|
||||||
|
EmailMessage content = createBuilder().build();
|
||||||
|
sendEmailService.sendEmail(content);
|
||||||
|
Message message = getMessage();
|
||||||
|
assertThat(message.getAllRecipients())
|
||||||
|
.asList()
|
||||||
|
.containsExactly(new InternetAddress("fake@example.com"));
|
||||||
|
assertThat(message.getFrom())
|
||||||
|
.asList()
|
||||||
|
.containsExactly(new InternetAddress("registry@example.com"));
|
||||||
|
assertThat(message.getRecipients(RecipientType.BCC)).isNull();
|
||||||
|
assertThat(message.getSubject()).isEqualTo("Subject");
|
||||||
|
assertThat(message.getContentType()).startsWith("multipart/mixed");
|
||||||
|
assertThat(getInternalContent(message).getContent().toString()).isEqualTo("body");
|
||||||
|
assertThat(getInternalContent(message).getContentType()).isEqualTo("text/plain; charset=utf-8");
|
||||||
|
assertThat(((MimeMultipart) message.getContent()).getCount()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccess_bcc() throws Exception {
|
||||||
|
EmailMessage content = createBuilder().setBcc(new InternetAddress("bcc@example.com")).build();
|
||||||
|
sendEmailService.sendEmail(content);
|
||||||
|
Message message = getMessage();
|
||||||
|
assertThat(message.getRecipients(RecipientType.BCC))
|
||||||
|
.asList()
|
||||||
|
.containsExactly(new InternetAddress("bcc@example.com"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccess_contentType() throws Exception {
|
||||||
|
EmailMessage content = createBuilder().setContentType(MediaType.HTML_UTF_8).build();
|
||||||
|
sendEmailService.sendEmail(content);
|
||||||
|
Message message = getMessage();
|
||||||
|
assertThat(getInternalContent(message).getContentType())
|
||||||
|
.isEqualTo("text/html; charset=utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccess_attachment() throws Exception {
|
||||||
|
EmailMessage content =
|
||||||
|
createBuilder()
|
||||||
|
.setAttachment(
|
||||||
|
Attachment.newBuilder()
|
||||||
|
.setFilename("filename")
|
||||||
|
.setContent("foo,bar\nbaz,qux")
|
||||||
|
.setContentType(MediaType.CSV_UTF_8)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
sendEmailService.sendEmail(content);
|
||||||
|
Message message = getMessage();
|
||||||
|
assertThat(((MimeMultipart) message.getContent()).getCount()).isEqualTo(2);
|
||||||
|
BodyPart attachment = ((MimeMultipart) message.getContent()).getBodyPart(1);
|
||||||
|
assertThat(attachment.getContent()).isEqualTo("foo,bar\nbaz,qux");
|
||||||
|
assertThat(attachment.getContentType()).endsWith("name=filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccess_retry() throws Exception {
|
||||||
|
doThrow(new MessagingException("hi"))
|
||||||
|
.doNothing()
|
||||||
|
.when(wrapper)
|
||||||
|
.sendMessage(messageCaptor.capture());
|
||||||
|
EmailMessage content = createBuilder().build();
|
||||||
|
sendEmailService.sendEmail(content);
|
||||||
|
assertThat(messageCaptor.getValue().getSubject()).isEqualTo("Subject");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_wrongExceptionType() throws Exception {
|
||||||
|
doThrow(new RuntimeException("this is a runtime exception")).when(wrapper).sendMessage(any());
|
||||||
|
EmailMessage content = createBuilder().build();
|
||||||
|
RuntimeException thrown =
|
||||||
|
assertThrows(RuntimeException.class, () -> sendEmailService.sendEmail(content));
|
||||||
|
assertThat(thrown).hasMessageThat().isEqualTo("this is a runtime exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_tooManyTries() throws Exception {
|
||||||
|
doThrow(new MessagingException("hi"))
|
||||||
|
.doThrow(new MessagingException("second"))
|
||||||
|
.when(wrapper)
|
||||||
|
.sendMessage(any());
|
||||||
|
EmailMessage content = createBuilder().build();
|
||||||
|
RuntimeException thrown =
|
||||||
|
assertThrows(RuntimeException.class, () -> sendEmailService.sendEmail(content));
|
||||||
|
assertThat(thrown).hasCauseThat().hasMessageThat().isEqualTo("second");
|
||||||
|
assertThat(thrown).hasCauseThat().isInstanceOf(MessagingException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmailMessage.Builder createBuilder() throws Exception {
|
||||||
|
return EmailMessage.newBuilder()
|
||||||
|
.setFrom(new InternetAddress("registry@example.com"))
|
||||||
|
.addRecipient(new InternetAddress("fake@example.com"))
|
||||||
|
.setSubject("Subject")
|
||||||
|
.setBody("body");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Message getMessage() throws MessagingException {
|
||||||
|
verify(wrapper).sendMessage(messageCaptor.capture());
|
||||||
|
return messageCaptor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BodyPart getInternalContent(Message message) throws Exception {
|
||||||
|
return ((MimeMultipart) message.getContent()).getBodyPart(0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue