mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 19:20:44 +02:00
Replace invoice email attachement with bucket link (#2299)
This commit is contained in:
parent
b21e1a1935
commit
f9e0908022
7 changed files with 61 additions and 41 deletions
|
@ -163,7 +163,12 @@ public class InvoicingPipeline implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Saves the billing events to a single overall invoice CSV file. */
|
/**
|
||||||
|
* Saves the billing events to a single overall invoice CSV file. TextIO always produces the file
|
||||||
|
* of type text/plain, which we then update to desired text/csv before sending an email to billing
|
||||||
|
* team {@link google.registry.reporting.billing.BillingEmailUtils#emailOverallInvoice()
|
||||||
|
* emailOverallInvoice}
|
||||||
|
*/
|
||||||
static void saveInvoiceCsv(
|
static void saveInvoiceCsv(
|
||||||
PCollection<google.registry.beam.billing.BillingEvent> billingEvents,
|
PCollection<google.registry.beam.billing.BillingEvent> billingEvents,
|
||||||
InvoicingPipelineOptions options) {
|
InvoicingPipelineOptions options) {
|
||||||
|
|
|
@ -721,6 +721,17 @@ public final class RegistryConfig {
|
||||||
return "gs://" + billingBucket;
|
return "gs://" + billingBucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns origin part of the URL of the billing invoice file
|
||||||
|
*
|
||||||
|
* @see google.registry.beam.billing.InvoicingPipeline
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Config("billingInvoiceOriginUrl")
|
||||||
|
public static String provideBillingInvoiceOriginUrl(RegistryConfigSettings config) {
|
||||||
|
return config.billing.billingInvoiceOriginUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not we should publish invoices to partners automatically by default.
|
* Returns whether or not we should publish invoices to partners automatically by default.
|
||||||
*
|
*
|
||||||
|
|
|
@ -173,6 +173,7 @@ public class RegistryConfigSettings {
|
||||||
public List<String> invoiceEmailRecipients;
|
public List<String> invoiceEmailRecipients;
|
||||||
public String invoiceReplyToEmailAddress;
|
public String invoiceReplyToEmailAddress;
|
||||||
public String invoiceFilePrefix;
|
public String invoiceFilePrefix;
|
||||||
|
public String billingInvoiceOriginUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configuration for Registry Data Escrow (RDE). */
|
/** Configuration for Registry Data Escrow (RDE). */
|
||||||
|
|
|
@ -381,6 +381,7 @@ billing:
|
||||||
# Optional return address that overrides the default.
|
# Optional return address that overrides the default.
|
||||||
invoiceReplyToEmailAddress: null
|
invoiceReplyToEmailAddress: null
|
||||||
invoiceFilePrefix: REG-INV
|
invoiceFilePrefix: REG-INV
|
||||||
|
billingInvoiceOriginUrl: https://billing-origin-url/
|
||||||
|
|
||||||
rde:
|
rde:
|
||||||
# URL prefix of ICANN's server to upload RDE reports to. Nomulus adds /TLD/ID
|
# URL prefix of ICANN's server to upload RDE reports to. Nomulus adds /TLD/ID
|
||||||
|
|
|
@ -118,6 +118,14 @@ public class GcsUtils implements Serializable {
|
||||||
storage().delete(blobId);
|
storage().delete(blobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update file content type on existing GCS file */
|
||||||
|
public void updateContentType(BlobId blobId, String contentType) throws StorageException {
|
||||||
|
if (existsAndNotEmpty(blobId)) {
|
||||||
|
Blob blob = storage().get(blobId);
|
||||||
|
blob.toBuilder().setContentType(contentType).build().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all object names within a bucket for a given prefix.
|
* Returns a list of all object names within a bucket for a given prefix.
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,20 +15,17 @@
|
||||||
package google.registry.reporting.billing;
|
package google.registry.reporting.billing;
|
||||||
|
|
||||||
import static com.google.common.base.Throwables.getRootCause;
|
import static com.google.common.base.Throwables.getRootCause;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
import com.google.cloud.storage.BlobId;
|
import com.google.cloud.storage.BlobId;
|
||||||
|
import com.google.cloud.storage.StorageException;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.flogger.FluentLogger;
|
||||||
import com.google.common.net.MediaType;
|
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.groups.GmailClient;
|
import google.registry.groups.GmailClient;
|
||||||
import google.registry.reporting.billing.BillingModule.InvoiceDirectoryPrefix;
|
import google.registry.reporting.billing.BillingModule.InvoiceDirectoryPrefix;
|
||||||
import google.registry.util.EmailMessage;
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.EmailMessage.Attachment;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
|
@ -37,6 +34,7 @@ import org.joda.time.YearMonth;
|
||||||
/** Utility functions for sending emails involving monthly invoices. */
|
/** Utility functions for sending emails involving monthly invoices. */
|
||||||
public class BillingEmailUtils {
|
public class BillingEmailUtils {
|
||||||
|
|
||||||
|
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||||
private final GmailClient gmailClient;
|
private final GmailClient gmailClient;
|
||||||
private final YearMonth yearMonth;
|
private final YearMonth yearMonth;
|
||||||
private final InternetAddress outgoingEmailAddress;
|
private final InternetAddress outgoingEmailAddress;
|
||||||
|
@ -46,6 +44,7 @@ public class BillingEmailUtils {
|
||||||
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 String billingInvoiceOriginUrl;
|
||||||
private final GcsUtils gcsUtils;
|
private final GcsUtils gcsUtils;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -58,6 +57,7 @@ public class BillingEmailUtils {
|
||||||
@Config("invoiceReplyToEmailAddress") Optional<InternetAddress> replyToEmailAddress,
|
@Config("invoiceReplyToEmailAddress") Optional<InternetAddress> replyToEmailAddress,
|
||||||
@Config("billingBucket") String billingBucket,
|
@Config("billingBucket") String billingBucket,
|
||||||
@Config("invoiceFilePrefix") String invoiceFilePrefix,
|
@Config("invoiceFilePrefix") String invoiceFilePrefix,
|
||||||
|
@Config("billingInvoiceOriginUrl") String billingInvoiceOriginUrl,
|
||||||
@InvoiceDirectoryPrefix String invoiceDirectoryPrefix,
|
@InvoiceDirectoryPrefix String invoiceDirectoryPrefix,
|
||||||
GcsUtils gcsUtils) {
|
GcsUtils gcsUtils) {
|
||||||
this.gmailClient = gmailClient;
|
this.gmailClient = gmailClient;
|
||||||
|
@ -69,31 +69,36 @@ public class BillingEmailUtils {
|
||||||
this.billingBucket = billingBucket;
|
this.billingBucket = billingBucket;
|
||||||
this.invoiceFilePrefix = invoiceFilePrefix;
|
this.invoiceFilePrefix = invoiceFilePrefix;
|
||||||
this.invoiceDirectoryPrefix = invoiceDirectoryPrefix;
|
this.invoiceDirectoryPrefix = invoiceDirectoryPrefix;
|
||||||
|
this.billingInvoiceOriginUrl = billingInvoiceOriginUrl;
|
||||||
this.gcsUtils = gcsUtils;
|
this.gcsUtils = gcsUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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() {
|
public void emailOverallInvoice() {
|
||||||
try {
|
try {
|
||||||
String invoiceFile = String.format("%s-%s.csv", invoiceFilePrefix, yearMonth);
|
String invoiceFile = String.format("%s-%s.csv", invoiceFilePrefix, yearMonth);
|
||||||
|
String fileUrl = billingInvoiceOriginUrl + invoiceDirectoryPrefix + invoiceFile;
|
||||||
BlobId invoiceFilename = BlobId.of(billingBucket, invoiceDirectoryPrefix + invoiceFile);
|
BlobId invoiceFilename = BlobId.of(billingBucket, invoiceDirectoryPrefix + invoiceFile);
|
||||||
try (InputStream in = gcsUtils.openInputStream(invoiceFilename)) {
|
try {
|
||||||
gmailClient.sendEmail(
|
gcsUtils.updateContentType(invoiceFilename, "text/csv");
|
||||||
EmailMessage.newBuilder()
|
} catch (StorageException e) {
|
||||||
.setSubject(String.format("Domain Registry invoice data %s", yearMonth))
|
// We want to continue with email anyway, it just will be less convenient for billing team
|
||||||
.setBody(
|
// to process the file.
|
||||||
String.format("Attached is the %s invoice for the domain registry.", yearMonth))
|
logger.atWarning().withCause(e).log("Failed to update invoice file type");
|
||||||
.setFrom(outgoingEmailAddress)
|
|
||||||
.setRecipients(invoiceEmailRecipients)
|
|
||||||
.setReplyToEmailAddress(replyToEmailAddress)
|
|
||||||
.setAttachment(
|
|
||||||
Attachment.newBuilder()
|
|
||||||
.setContent(CharStreams.toString(new InputStreamReader(in, UTF_8)))
|
|
||||||
.setContentType(MediaType.CSV_UTF_8)
|
|
||||||
.setFilename(invoiceFile)
|
|
||||||
.build())
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
gmailClient.sendEmail(
|
||||||
|
EmailMessage.newBuilder()
|
||||||
|
.setSubject(String.format("Domain Registry invoice data %s", yearMonth))
|
||||||
|
.setBody(
|
||||||
|
String.format(
|
||||||
|
"<p>Use the following link to download %s invoice for the domain registry -"
|
||||||
|
+ " <a href=\"%s\">invoice</a>.</p>",
|
||||||
|
yearMonth, fileUrl))
|
||||||
|
.setFrom(outgoingEmailAddress)
|
||||||
|
.setRecipients(invoiceEmailRecipients)
|
||||||
|
.setReplyToEmailAddress(replyToEmailAddress)
|
||||||
|
.setContentType(MediaType.HTML_UTF_8)
|
||||||
|
.build());
|
||||||
} 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(
|
||||||
|
|
|
@ -21,17 +21,12 @@ 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;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import com.google.cloud.storage.BlobId;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.net.MediaType;
|
import com.google.common.net.MediaType;
|
||||||
import google.registry.gcs.GcsUtils;
|
import google.registry.gcs.GcsUtils;
|
||||||
import google.registry.groups.GmailClient;
|
import google.registry.groups.GmailClient;
|
||||||
import google.registry.util.EmailMessage;
|
import google.registry.util.EmailMessage;
|
||||||
import google.registry.util.EmailMessage.Attachment;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.mail.internet.InternetAddress;
|
import javax.mail.internet.InternetAddress;
|
||||||
|
@ -45,18 +40,14 @@ class BillingEmailUtilsTest {
|
||||||
|
|
||||||
private GmailClient gmailClient;
|
private GmailClient gmailClient;
|
||||||
private BillingEmailUtils emailUtils;
|
private BillingEmailUtils emailUtils;
|
||||||
private GcsUtils gcsUtils;
|
|
||||||
private ArgumentCaptor<EmailMessage> contentCaptor;
|
private ArgumentCaptor<EmailMessage> contentCaptor;
|
||||||
|
private GcsUtils gcsUtils;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws Exception {
|
void beforeEach() throws Exception {
|
||||||
gmailClient = mock(GmailClient.class);
|
gmailClient = mock(GmailClient.class);
|
||||||
gcsUtils = mock(GcsUtils.class);
|
|
||||||
when(gcsUtils.openInputStream(BlobId.of("test-bucket", "results/REG-INV-2017-10.csv")))
|
|
||||||
.thenReturn(
|
|
||||||
new ByteArrayInputStream("test,data\nhello,world".getBytes(StandardCharsets.UTF_8)));
|
|
||||||
contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||||
|
gcsUtils = mock(GcsUtils.class);
|
||||||
emailUtils = getEmailUtils(Optional.of(new InternetAddress("reply-to@test.com")));
|
emailUtils = getEmailUtils(Optional.of(new InternetAddress("reply-to@test.com")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +63,7 @@ class BillingEmailUtilsTest {
|
||||||
replyToAddress,
|
replyToAddress,
|
||||||
"test-bucket",
|
"test-bucket",
|
||||||
"REG-INV",
|
"REG-INV",
|
||||||
|
"www.google.com/",
|
||||||
"results/",
|
"results/",
|
||||||
gcsUtils);
|
gcsUtils);
|
||||||
}
|
}
|
||||||
|
@ -89,14 +81,11 @@ class BillingEmailUtilsTest {
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
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")
|
.setSubject("Domain Registry invoice data 2017-10")
|
||||||
.setBody("Attached is the 2017-10 invoice for the domain registry.")
|
.setBody(
|
||||||
|
"<p>Use the following link to download 2017-10 invoice for the domain registry -"
|
||||||
|
+ " <a href=\"www.google.com/results/REG-INV-2017-10.csv\">invoice</a>.</p>")
|
||||||
.setReplyToEmailAddress(new InternetAddress("reply-to@test.com"))
|
.setReplyToEmailAddress(new InternetAddress("reply-to@test.com"))
|
||||||
.setAttachment(
|
.setContentType(MediaType.HTML_UTF_8)
|
||||||
Attachment.newBuilder()
|
|
||||||
.setContent("test,data\nhello,world")
|
|
||||||
.setContentType(MediaType.CSV_UTF_8)
|
|
||||||
.setFilename("REG-INV-2017-10.csv")
|
|
||||||
.build())
|
|
||||||
.build();
|
.build();
|
||||||
assertThat(emailMessage).isEqualTo(expectedContent);
|
assertThat(emailMessage).isEqualTo(expectedContent);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue