mirror of
https://github.com/google/nomulus
synced 2026-05-24 16:51:49 +00:00
Compare commits
9 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1beeb4016 | ||
|
|
5c33286056 | ||
|
|
603a95d719 | ||
|
|
0a3774d3f7 | ||
|
|
cc60b27dd3 | ||
|
|
52c18f9967 | ||
|
|
5339b3cb6c | ||
|
|
d18dab3327 | ||
|
|
61932c1809 |
@@ -44,7 +44,6 @@ def outcastTestPatterns = [
|
||||
"google/registry/flows/domain/DomainCreateFlowTest.*",
|
||||
"google/registry/flows/domain/DomainUpdateFlowTest.*",
|
||||
"google/registry/tools/CreateDomainCommandTest.*",
|
||||
"google/registry/tools/server/CreatePremiumListActionTest.*",
|
||||
]
|
||||
|
||||
// Tests that fail when running Gradle in a docker container, e. g. when
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
// Copyright 2021 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.batch;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/** An action that sends notification emails to registrars whose certificates are expiring soon. */
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = SendExpiringCertificateNotificationEmailAction.PATH,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class SendExpiringCertificateNotificationEmailAction implements Runnable {
|
||||
public static final String PATH = "/_dr/task/sendExpiringCertificateNotificationEmail";
|
||||
/**
|
||||
* Used as an offset when storing the last notification email sent date.
|
||||
*
|
||||
* <p>This is used to handle edges cases when the update happens in between the day switch. For
|
||||
* instance,if the job starts at 2:00 am every day and it finishes at 2:03 of the same day, then
|
||||
* next day at 2am, the date difference will be less than a day, which will lead to the date
|
||||
* difference between two successive email sent date being the expected email interval days + 1;
|
||||
*/
|
||||
protected static final Duration UPDATE_TIME_OFFSET = Duration.standardMinutes(10);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
|
||||
private final CertificateChecker certificateChecker;
|
||||
private final String expirationWarningEmailBodyText;
|
||||
private final SendEmailService sendEmailService;
|
||||
private final String expirationWarningEmailSubjectText;
|
||||
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
public SendExpiringCertificateNotificationEmailAction(
|
||||
@Config("expirationWarningEmailBodyText") String expirationWarningEmailBodyText,
|
||||
@Config("expirationWarningEmailSubjectText") String expirationWarningEmailSubjectText,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||
SendEmailService sendEmailService,
|
||||
CertificateChecker certificateChecker,
|
||||
Response response) {
|
||||
this.certificateChecker = certificateChecker;
|
||||
this.expirationWarningEmailSubjectText = expirationWarningEmailSubjectText;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
|
||||
this.expirationWarningEmailBodyText = expirationWarningEmailBodyText;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
try {
|
||||
sendNotificationEmails();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Exception thrown when sending expiring certificate notification emails.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Exception thrown with cause: %s", e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of registrars that should receive expiring notification emails. There are two
|
||||
* certificates that should be considered (the main certificate and failOver certificate). The
|
||||
* registrars should receive notifications if one of the certificate checks returns true.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
ImmutableList<RegistrarInfo> getRegistrarsWithExpiringCertificates() {
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
.map(
|
||||
registrar ->
|
||||
RegistrarInfo.create(
|
||||
registrar,
|
||||
registrar.getClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
registrar.getClientCertificate().get()),
|
||||
registrar.getFailoverClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
registrar.getFailoverClientCertificate().get())))
|
||||
.filter(
|
||||
registrarInfo ->
|
||||
registrarInfo.isCertExpiring() || registrarInfo.isFailOverCertExpiring())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a notification email to the registrar regarding the expiring certificate and returns true
|
||||
* if it's sent successfully.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean sendNotificationEmail(
|
||||
Registrar registrar,
|
||||
DateTime lastExpiringCertNotificationSentDate,
|
||||
CertificateType certificateType,
|
||||
Optional<String> certificate) {
|
||||
if (!certificate.isPresent()
|
||||
|| !certificateChecker.shouldReceiveExpiringNotification(
|
||||
lastExpiringCertNotificationSentDate, certificate.get())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH);
|
||||
if (recipients.isEmpty()) {
|
||||
logger.atWarning().log(
|
||||
"Registrar %s contains no email addresses to receive notification email.",
|
||||
registrar.getRegistrarName());
|
||||
return false;
|
||||
}
|
||||
sendEmailService.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setSubject(expirationWarningEmailSubjectText)
|
||||
.setBody(
|
||||
getEmailBody(
|
||||
registrar.getRegistrarName(),
|
||||
certificateType,
|
||||
certificateChecker.getCertificate(certificate.get()).getNotAfter()))
|
||||
.setRecipients(recipients)
|
||||
.setCcs(getEmailAddresses(registrar, Type.ADMIN))
|
||||
.build());
|
||||
/*
|
||||
* A duration time offset is used here to ensure that date comparison between two
|
||||
* successive dates is always greater than 1 day. This date is set as last updated date,
|
||||
* for applicable certificate.
|
||||
*/
|
||||
updateLastNotificationSentDate(
|
||||
registrar,
|
||||
DateTime.now(UTC).minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
|
||||
certificateType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Failed to send expiring certificate notification email to registrar %s.",
|
||||
registrar.getRegistrarName()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the last notification sent date in database. */
|
||||
@VisibleForTesting
|
||||
void updateLastNotificationSentDate(
|
||||
Registrar registrar, DateTime now, CertificateType certificateType) {
|
||||
try {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Registrar.Builder newRegistrar = tm().loadByEntity(registrar).asBuilder();
|
||||
switch (certificateType) {
|
||||
case PRIMARY:
|
||||
newRegistrar.setLastExpiringCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
certificateType.getDisplayName(), registrar.getRegistrarName());
|
||||
break;
|
||||
case FAILOVER:
|
||||
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
certificateType.getDisplayName(), registrar.getRegistrarName());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Unsupported certificate type: %s being passed in when updating "
|
||||
+ "the last notification sent date to registrar %s.",
|
||||
certificateType.toString(), registrar.getRegistrarName()));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Failed to update the last notification sent date to Registrar %s for the %s "
|
||||
+ "certificate.",
|
||||
registrar.getRegistrarName(), certificateType.getDisplayName()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends notification emails to registrars with expiring certificates. */
|
||||
@VisibleForTesting
|
||||
int sendNotificationEmails() {
|
||||
int emailsSent = 0;
|
||||
for (RegistrarInfo registrarInfo : getRegistrarsWithExpiringCertificates()) {
|
||||
Registrar registrar = registrarInfo.registrar();
|
||||
if (registrarInfo.isCertExpiring()) {
|
||||
sendNotificationEmail(
|
||||
registrar,
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
CertificateType.PRIMARY,
|
||||
registrar.getClientCertificate());
|
||||
emailsSent++;
|
||||
}
|
||||
if (registrarInfo.isFailOverCertExpiring()) {
|
||||
sendNotificationEmail(
|
||||
registrar,
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
CertificateType.FAILOVER,
|
||||
registrar.getFailoverClientCertificate());
|
||||
emailsSent++;
|
||||
}
|
||||
}
|
||||
logger.atInfo().log(
|
||||
"Sent %d expiring certificate notification emails to registrars.", emailsSent);
|
||||
return emailsSent;
|
||||
}
|
||||
|
||||
/** Returns a list of email addresses of the registrar that should receive a notification email */
|
||||
@VisibleForTesting
|
||||
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
|
||||
ImmutableSortedSet<RegistrarContact> contacts = registrar.getContactsOfType(contactType);
|
||||
ImmutableSet.Builder<InternetAddress> recipientEmails = new ImmutableSet.Builder<>();
|
||||
for (RegistrarContact contact : contacts) {
|
||||
try {
|
||||
recipientEmails.add(new InternetAddress(contact.getEmailAddress()));
|
||||
} catch (AddressException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Registrar Contact email address %s of Registrar %s is invalid; skipping.",
|
||||
contact.getEmailAddress(), registrar.getRegistrarName());
|
||||
}
|
||||
}
|
||||
return recipientEmails.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates email content by taking registrar name, certificate type and expiration date as
|
||||
* parameters.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings("lgtm[java/dereferenced-value-may-be-null]")
|
||||
String getEmailBody(String registrarName, CertificateType type, Date expirationDate) {
|
||||
checkArgumentNotNull(expirationDate, "Expiration date cannot be null");
|
||||
checkArgumentNotNull(type, "Certificate type cannot be null");
|
||||
return String.format(
|
||||
expirationWarningEmailBodyText,
|
||||
registrarName,
|
||||
type.getDisplayName(),
|
||||
DATE_FORMATTER.print(new DateTime(expirationDate)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Certificate types for X509Certificate.
|
||||
*
|
||||
* <p><b>Note:</b> These types are only used to indicate the type of expiring certificate in
|
||||
* notification emails.
|
||||
*/
|
||||
protected enum CertificateType {
|
||||
PRIMARY("primary"),
|
||||
FAILOVER("fail-over");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
CertificateType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class RegistrarInfo {
|
||||
static RegistrarInfo create(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {
|
||||
return new AutoValue_SendExpiringCertificateNotificationEmailAction_RegistrarInfo(
|
||||
registrar, isCertExpiring, isFailOverCertExpiring);
|
||||
}
|
||||
|
||||
public abstract Registrar registrar();
|
||||
|
||||
public abstract boolean isCertExpiring();
|
||||
|
||||
public abstract boolean isFailOverCertExpiring();
|
||||
}
|
||||
}
|
||||
@@ -355,6 +355,12 @@
|
||||
<url-pattern>/_dr/task/deleteExpiredDomains</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Background action to send notification emails to registrars with expiring certificate. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/sendExpiringCertificateNotificationEmail</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Mapreduce to import contacts from escrow file -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
It also enqueues a new task to wait on the completion of that job and then load the resulting
|
||||
snapshot into bigquery.
|
||||
</description>
|
||||
<!--
|
||||
<!- -
|
||||
Keep google.registry.export.CheckBackupAction.MAXIMUM_BACKUP_RUNNING_TIME less than
|
||||
this interval. - ->
|
||||
<schedule>every day 06:00</schedule>
|
||||
|
||||
@@ -330,4 +330,13 @@
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=replay-commit-logs-to-sql&endpoint=/_dr/task/replayCommitLogsToSql&runInEmpty]]></url>
|
||||
<description>
|
||||
Replays recent commit logs from Datastore to the SQL secondary backend.
|
||||
</description>
|
||||
<schedule>every 3 minutes</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
@@ -168,6 +168,15 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/sendExpiringCertificateNotificationEmail]]></url>
|
||||
<description>
|
||||
This job runs an action that sends emails to partners if their certificates are expiring soon.
|
||||
</description>
|
||||
<schedule>every day 04:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=export-snapshot&endpoint=/_dr/task/backupDatastore&runInEmpty]]></url>
|
||||
<description>
|
||||
|
||||
@@ -43,7 +43,6 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.model.server.ServerSecret;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsList.ClaimsListRevision;
|
||||
import google.registry.model.tmch.ClaimsList.ClaimsListSingleton;
|
||||
@@ -92,7 +91,6 @@ public final class EntityClasses {
|
||||
Registrar.class,
|
||||
RegistrarContact.class,
|
||||
Registry.class,
|
||||
ReservedList.class,
|
||||
ServerSecret.class,
|
||||
TmchCrl.class);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class RdeNamingUtils {
|
||||
}
|
||||
|
||||
/** Returns same thing as {@link #makeRydeFilename} except without the series and revision. */
|
||||
static String makePartialName(String tld, DateTime date, RdeMode mode) {
|
||||
public static String makePartialName(String tld, DateTime date, RdeMode mode) {
|
||||
return String.format("%s_%s_%s",
|
||||
checkNotNull(tld), formatDate(date), mode.getFilenameComponent());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.request.Action.Method.GET;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.ReplayDirection;
|
||||
@@ -64,7 +65,7 @@ public class ReplicateToDatastoreAction implements Runnable {
|
||||
@VisibleForTesting
|
||||
public List<TransactionEntity> getTransactionBatch() {
|
||||
// Get the next batch of transactions that we haven't replicated.
|
||||
LastSqlTransaction lastSqlTxnBeforeBatch = ofyTm().transact(() -> LastSqlTransaction.load());
|
||||
LastSqlTransaction lastSqlTxnBeforeBatch = ofyTm().transact(LastSqlTransaction::load);
|
||||
try {
|
||||
return jpaTm()
|
||||
.transactWithoutBackup(
|
||||
@@ -85,53 +86,60 @@ public class ReplicateToDatastoreAction implements Runnable {
|
||||
/**
|
||||
* Apply a transaction to Datastore, returns true if there was a fatal error and the batch should
|
||||
* be aborted.
|
||||
*
|
||||
* <p>TODO(gbrodman): this should throw an exception on error instead since it gives more
|
||||
* information and we can't rely on the caller checking the boolean result.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public boolean applyTransaction(TransactionEntity txnEntity) {
|
||||
logger.atInfo().log("Applying a single transaction Cloud SQL -> Cloud Datastore");
|
||||
return ofyTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Reload the last transaction id, which could possibly have changed.
|
||||
LastSqlTransaction lastSqlTxn = LastSqlTransaction.load();
|
||||
long nextTxnId = lastSqlTxn.getTransactionId() + 1;
|
||||
if (nextTxnId < txnEntity.getId()) {
|
||||
// We're missing a transaction. This is bad. Transaction ids are supposed to
|
||||
// increase monotonically, so we abort rather than applying anything out of
|
||||
// order.
|
||||
logger.atSevere().log(
|
||||
"Missing transaction: last transaction id = %s, next available transaction "
|
||||
+ "= %s",
|
||||
nextTxnId - 1, txnEntity.getId());
|
||||
return true;
|
||||
} else if (nextTxnId > txnEntity.getId()) {
|
||||
// We've already replayed this transaction. This shouldn't happen, as GAE cron
|
||||
// is supposed to avoid overruns and this action shouldn't be executed from any
|
||||
// other context, but it's not harmful as we can just ignore the transaction. Log
|
||||
// it so that we know about it and move on.
|
||||
logger.atWarning().log(
|
||||
"Ignoring transaction %s, which appears to have already been applied.",
|
||||
txnEntity.getId());
|
||||
try (UpdateAutoTimestamp.DisableAutoUpdateResource disabler =
|
||||
UpdateAutoTimestamp.disableAutoUpdate()) {
|
||||
return ofyTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Reload the last transaction id, which could possibly have changed.
|
||||
LastSqlTransaction lastSqlTxn = LastSqlTransaction.load();
|
||||
long nextTxnId = lastSqlTxn.getTransactionId() + 1;
|
||||
if (nextTxnId < txnEntity.getId()) {
|
||||
// We're missing a transaction. This is bad. Transaction ids are supposed to
|
||||
// increase monotonically, so we abort rather than applying anything out of
|
||||
// order.
|
||||
logger.atSevere().log(
|
||||
"Missing transaction: last transaction id = %s, next available transaction "
|
||||
+ "= %s",
|
||||
nextTxnId - 1, txnEntity.getId());
|
||||
return true;
|
||||
} else if (nextTxnId > txnEntity.getId()) {
|
||||
// We've already replayed this transaction. This shouldn't happen, as GAE cron
|
||||
// is supposed to avoid overruns and this action shouldn't be executed from any
|
||||
// other context, but it's not harmful as we can just ignore the transaction. Log
|
||||
// it so that we know about it and move on.
|
||||
logger.atWarning().log(
|
||||
"Ignoring transaction %s, which appears to have already been applied.",
|
||||
txnEntity.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.atInfo().log(
|
||||
"Applying transaction %s to Cloud Datastore", txnEntity.getId());
|
||||
|
||||
// At this point, we know txnEntity is the correct next transaction, so write it
|
||||
// to datastore.
|
||||
try {
|
||||
Transaction.deserialize(txnEntity.getContents()).writeToDatastore();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error during transaction deserialization.", e);
|
||||
}
|
||||
|
||||
// Write the updated last transaction id to datastore as part of this datastore
|
||||
// transaction.
|
||||
auditedOfy().save().entity(lastSqlTxn.cloneWithNewTransactionId(nextTxnId));
|
||||
logger.atInfo().log(
|
||||
"Finished applying single transaction Cloud SQL -> Cloud Datastore");
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.atInfo().log("Applying transaction %s to Cloud Datastore", txnEntity.getId());
|
||||
|
||||
// At this point, we know txnEntity is the correct next transaction, so write it
|
||||
// to datastore.
|
||||
try {
|
||||
Transaction.deserialize(txnEntity.getContents()).writeToDatastore();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error during transaction deserialization.", e);
|
||||
}
|
||||
|
||||
// Write the updated last transaction id to datastore as part of this datastore
|
||||
// transaction.
|
||||
auditedOfy().save().entity(lastSqlTxn.cloneWithNewTransactionId(nextTxnId));
|
||||
logger.atInfo().log(
|
||||
"Finished applying single transaction Cloud SQL -> Cloud Datastore");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,13 +33,8 @@ import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Mapify;
|
||||
import com.googlecode.objectify.mapper.Mapper;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.replay.NonReplicatedEntity;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.DomainLabelMetrics.MetricsReservedListMatch;
|
||||
import java.io.Serializable;
|
||||
@@ -65,13 +60,11 @@ import org.joda.time.DateTime;
|
||||
* succeeds, we will end up with having two exact same reserved lists that differ only by
|
||||
* revisionId. This is fine though, because we only use the list with the highest revisionId.
|
||||
*/
|
||||
@Entity
|
||||
@ReportedOn
|
||||
@javax.persistence.Entity
|
||||
@Table(indexes = {@Index(columnList = "name", name = "reservedlist_name_idx")})
|
||||
public final class ReservedList
|
||||
extends BaseDomainLabelList<ReservationType, ReservedList.ReservedListEntry>
|
||||
implements NonReplicatedEntity {
|
||||
implements SqlOnlyEntity {
|
||||
|
||||
/**
|
||||
* Mapping from domain name to its reserved list info.
|
||||
@@ -80,7 +73,6 @@ public final class ReservedList
|
||||
* from the immutability contract so we can modify it after construction and we have to handle the
|
||||
* database processing on our own so we can detach it after load.
|
||||
*/
|
||||
@Mapify(ReservedListEntry.LabelMapper.class)
|
||||
@Insignificant
|
||||
@Transient
|
||||
Map<String, ReservedListEntry> reservedListMap;
|
||||
@@ -121,10 +113,9 @@ public final class ReservedList
|
||||
* A reserved list entry entity, persisted to Datastore, that represents a single label and its
|
||||
* reservation type.
|
||||
*/
|
||||
@Embed
|
||||
@javax.persistence.Entity(name = "ReservedEntry")
|
||||
public static class ReservedListEntry extends DomainLabelEntry<ReservationType, ReservedListEntry>
|
||||
implements Buildable, NonReplicatedEntity, Serializable {
|
||||
implements Buildable, SqlOnlyEntity, Serializable {
|
||||
|
||||
@Insignificant @Id Long revisionId;
|
||||
|
||||
@@ -133,15 +124,6 @@ public final class ReservedList
|
||||
|
||||
String comment;
|
||||
|
||||
/** Mapper for use with @Mapify */
|
||||
static class LabelMapper implements Mapper<String, ReservedListEntry> {
|
||||
|
||||
@Override
|
||||
public String getKey(ReservedListEntry entry) {
|
||||
return entry.getDomainLabel();
|
||||
}
|
||||
}
|
||||
|
||||
public String getComment(String comment) {
|
||||
return comment;
|
||||
}
|
||||
@@ -239,7 +221,7 @@ public final class ReservedList
|
||||
* @return An Optional<ReservedList> that has a value if a reserved list exists by the given
|
||||
* name, or absent if not.
|
||||
* @throws UncheckedExecutionException if some other error occurs while trying to load the
|
||||
* ReservedList from the cache or Datastore.
|
||||
* ReservedList from the cache or database.
|
||||
*/
|
||||
public static Optional<ReservedList> get(String listName) {
|
||||
return getFromCache(listName, cache);
|
||||
|
||||
@@ -157,10 +157,12 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
|
||||
DomainBase.restoreOfyFrom(rootKey, billingCancellationId, billingCancellationHistoryId);
|
||||
|
||||
// Reconstruct server approve entities. We currently have to call postLoad() a _second_ time
|
||||
// if the billing cancellation id has been reconstituted, as it is part of that set.
|
||||
// if any of the billing objects have been reconstituted, as they are part of that set.
|
||||
// TODO(b/183010623): Normalize the approaches to VKey reconstitution for the TransferData
|
||||
// hierarchy (the logic currently lives either in PostLoad or here, depending on the key).
|
||||
if (billingCancellationId != null) {
|
||||
if (billingCancellationId != null
|
||||
|| serverApproveBillingEvent != null
|
||||
|| serverApproveAutorenewEvent != null) {
|
||||
serverApproveEntities = null;
|
||||
postLoad();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.batch.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutCloudSqlAction;
|
||||
import google.registry.batch.WipeoutDatastoreAction;
|
||||
import google.registry.cron.CommitLogFanoutAction;
|
||||
@@ -193,6 +194,8 @@ interface BackendRequestComponent {
|
||||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
SendExpiringCertificateNotificationEmailAction sendExpiringCertificateNotificationEmailAction();
|
||||
|
||||
SyncGroupMembersAction syncGroupMembersAction();
|
||||
|
||||
SyncRegistrarsSheetAction syncRegistrarsSheetAction();
|
||||
|
||||
@@ -30,7 +30,6 @@ import google.registry.request.RequestComponentBuilder;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.tools.server.CreateGroupsAction;
|
||||
import google.registry.tools.server.CreatePremiumListAction;
|
||||
import google.registry.tools.server.GenerateZoneFilesAction;
|
||||
import google.registry.tools.server.KillAllCommitLogsAction;
|
||||
import google.registry.tools.server.KillAllEppResourcesAction;
|
||||
@@ -43,7 +42,6 @@ import google.registry.tools.server.ListTldsAction;
|
||||
import google.registry.tools.server.RefreshDnsForAllDomainsAction;
|
||||
import google.registry.tools.server.ResaveAllHistoryEntriesAction;
|
||||
import google.registry.tools.server.ToolsServerModule;
|
||||
import google.registry.tools.server.UpdatePremiumListAction;
|
||||
import google.registry.tools.server.VerifyOteAction;
|
||||
|
||||
/** Dagger component with per-request lifetime for "tools" App Engine module. */
|
||||
@@ -61,7 +59,6 @@ import google.registry.tools.server.VerifyOteAction;
|
||||
})
|
||||
interface ToolsRequestComponent {
|
||||
CreateGroupsAction createGroupsAction();
|
||||
CreatePremiumListAction createPremiumListAction();
|
||||
EppToolAction eppToolAction();
|
||||
FlowComponent.Builder flowComponentBuilder();
|
||||
GenerateZoneFilesAction generateZoneFilesAction();
|
||||
@@ -77,7 +74,6 @@ interface ToolsRequestComponent {
|
||||
RefreshDnsForAllDomainsAction refreshDnsForAllDomainsAction();
|
||||
ResaveAllHistoryEntriesAction resaveAllHistoryEntriesAction();
|
||||
RestoreCommitLogsAction restoreCommitLogsAction();
|
||||
UpdatePremiumListAction updatePremiumListAction();
|
||||
VerifyOteAction verifyOteAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2020 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.persistence.converter;
|
||||
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA converter for a set of {@link Key} containing a {@link ReservedList} */
|
||||
@Converter(autoApply = true)
|
||||
public class ReservedListKeySetConverter extends StringSetConverterBase<Key<ReservedList>> {
|
||||
|
||||
@Override
|
||||
String toString(Key<ReservedList> key) {
|
||||
return key.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
Key<ReservedList> fromString(String value) {
|
||||
return Key.create(getCrossTldKey(), ReservedList.class, value);
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,12 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
|
||||
import com.google.appengine.api.datastore.Entity;
|
||||
import com.google.appengine.api.datastore.EntityTranslator;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.replay.SqlEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -57,6 +59,11 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
|
||||
private transient ImmutableList<Mutation> mutations;
|
||||
|
||||
@VisibleForTesting
|
||||
public ImmutableList<Mutation> getMutations() {
|
||||
return mutations;
|
||||
}
|
||||
|
||||
/** Write the entire transaction to the datastore in a datastore transaction. */
|
||||
public void writeToDatastore() {
|
||||
ofyTm()
|
||||
@@ -128,7 +135,7 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
* Returns true if we are serializing a transaction in the current thread.
|
||||
*
|
||||
* <p>This should be checked by any Ofy translators prior to making any changes to an entity's
|
||||
* state representation based on the assumption that we are currently pseristing the entity to
|
||||
* state representation based on the assumption that we are currently persisting the entity to
|
||||
* datastore.
|
||||
*/
|
||||
public static boolean inSerializationMode() {
|
||||
@@ -146,7 +153,7 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
|
||||
public static class Builder extends GenericBuilder<Transaction, Builder> {
|
||||
|
||||
ImmutableList.Builder listBuilder = new ImmutableList.Builder();
|
||||
ImmutableList.Builder<Mutation> listBuilder = new ImmutableList.Builder<>();
|
||||
|
||||
Builder() {}
|
||||
|
||||
@@ -224,7 +231,8 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
private Object entity;
|
||||
|
||||
Update(Object entity) {
|
||||
this.entity = entity;
|
||||
this.entity =
|
||||
(entity instanceof SqlEntity) ? ((SqlEntity) entity).toDatastoreEntity().get() : entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,6 +249,11 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
proto.writeDelimitedTo(out);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Object getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static Update deserializeFrom(ObjectInputStream in) throws IOException {
|
||||
EntityProto proto = new EntityProto();
|
||||
proto.parseDelimitedFrom(in);
|
||||
@@ -273,9 +286,14 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
out.writeObject(key);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public VKey<?> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public static Delete deserializeFrom(ObjectInputStream in) throws IOException {
|
||||
try {
|
||||
return new Delete((VKey) in.readObject());
|
||||
return new Delete((VKey<?>) in.readObject());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@@ -28,7 +29,7 @@ import javax.persistence.Table;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "Transaction")
|
||||
public class TransactionEntity implements SqlOnlyEntity {
|
||||
public class TransactionEntity extends ImmutableObject implements SqlOnlyEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
@@ -26,7 +26,8 @@ import javax.annotation.Nullable;
|
||||
* Base class for specification of command line parameters common to creating and updating reserved
|
||||
* lists.
|
||||
*/
|
||||
public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand {
|
||||
public abstract class CreateOrUpdateReservedListCommand extends ConfirmingCommand
|
||||
implements CommandWithRemoteApi {
|
||||
|
||||
static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
String currencyUnit;
|
||||
|
||||
@Override
|
||||
// Using CreatePremiumListAction.java as reference;
|
||||
protected String prompt() throws Exception {
|
||||
currency = CurrencyUnit.of(currencyUnit);
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
@@ -51,7 +50,6 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
!PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"A premium list already exists by this name");
|
||||
if (!override) {
|
||||
// refer to CreatePremiumListAction.java
|
||||
assertTldExists(
|
||||
name,
|
||||
"Premium names must match the name of the TLD they are intended to be used on"
|
||||
|
||||
@@ -25,9 +25,7 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -48,7 +46,7 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||
boolean override;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
protected String prompt() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
|
||||
checkArgument(
|
||||
!ReservedList.get(name).isPresent(), "A reserved list already exists by this name");
|
||||
@@ -66,35 +64,11 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||
.setCreationTimestamp(now)
|
||||
.build();
|
||||
|
||||
// calls the stageEntityChange method that takes old entity, new entity and a new vkey;
|
||||
// Because ReservedList is a sqlEntity and its primary key field (revisionId) is only set when
|
||||
// it's being persisted; a vkey has to be created here explicitly for ReservedList instances.
|
||||
stageEntityChange(
|
||||
null, reservedList, VKey.createOfy(ReservedList.class, Key.create(reservedList)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return getChangedEntities().isEmpty()
|
||||
? "No entity changes to apply."
|
||||
: getChangedEntities().stream()
|
||||
.map(
|
||||
entity -> {
|
||||
if (entity instanceof ReservedList) {
|
||||
// Format the entries of the reserved list as well.
|
||||
String entries =
|
||||
((ReservedList) entity)
|
||||
.getReservedListEntries().entrySet().stream()
|
||||
.map(
|
||||
entry ->
|
||||
String.format("%s=%s", entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.joining(", "));
|
||||
return String.format("%s\nreservedListMap={%s}\n", entity, entries);
|
||||
} else {
|
||||
return entity.toString();
|
||||
}
|
||||
})
|
||||
.collect(Collectors.joining("\n"));
|
||||
String entries =
|
||||
reservedList.getReservedListEntries().entrySet().stream()
|
||||
.map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.joining(", "));
|
||||
return String.format("%s\nreservedListMap={%s}\n", reservedList, entries);
|
||||
}
|
||||
|
||||
private static void validateListName(String name) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.tools.Injector.injectReflectively;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -244,7 +244,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
ObjectifyService.initOfy();
|
||||
// Make sure we start the command with a clean cache, so that any previous command won't
|
||||
// interfere with this one.
|
||||
tm().clearSessionCache();
|
||||
ofyTm().clearSessionCache();
|
||||
|
||||
// Enable Cloud SQL for command that needs remote API as they will very likely use
|
||||
// Cloud SQL after the database migration. Note that the DB password is stored in Datastore
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.Optional;
|
||||
class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
@Override
|
||||
// Using UpdatePremiumListAction.java as reference;
|
||||
protected String prompt() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
Optional<PremiumList> list = PremiumListDao.getLatestRevision(name);
|
||||
|
||||
@@ -19,18 +19,17 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Strings;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Command to safely update {@link ReservedList}. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a ReservedList.")
|
||||
final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
protected String prompt() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
|
||||
ReservedList existingReservedList =
|
||||
ReservedList.get(name)
|
||||
@@ -53,15 +52,18 @@ final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||
if (!existingReservedList
|
||||
.getReservedListEntries()
|
||||
.equals(reservedList.getReservedListEntries())) {
|
||||
// calls the stageEntityChange method that takes old entity, new entity and a new vkey;
|
||||
// a vkey has to be created here explicitly for ReservedList instances.
|
||||
// ReservedList is a sqlEntity; it triggers the static method Vkey.create(Key<?> ofyCall),
|
||||
// which invokes a static ReservedList.createVkey(Key ofyKey) method that does not exist.
|
||||
// the sql primary key field (revisionId) is only set when it's being persisted;
|
||||
stageEntityChange(
|
||||
existingReservedList,
|
||||
reservedList,
|
||||
VKey.createOfy(ReservedList.class, Key.create(existingReservedList)));
|
||||
String oldEntries =
|
||||
existingReservedList.getReservedListEntries().entrySet().stream()
|
||||
.map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.joining(", "));
|
||||
String newEntries =
|
||||
reservedList.getReservedListEntries().entrySet().stream()
|
||||
.map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.joining(", "));
|
||||
return String.format(
|
||||
"Update reserved list for %s?\nOld List: %s\n New List: %s",
|
||||
name, oldEntries, newEntries);
|
||||
}
|
||||
return "No entity changes to apply.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +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.tools.server;
|
||||
|
||||
import static com.google.common.flogger.LazyArgs.lazy;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.Parameter;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Abstract base class for actions that update premium lists. */
|
||||
public abstract class CreateOrUpdatePremiumListAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final int MAX_LOGGING_PREMIUM_LIST_LENGTH = 1000;
|
||||
|
||||
public static final String NAME_PARAM = "name";
|
||||
public static final String INPUT_PARAM = "inputData";
|
||||
|
||||
@Inject JsonResponse response;
|
||||
|
||||
@Inject
|
||||
@Parameter("premiumListName")
|
||||
String name;
|
||||
|
||||
@Inject
|
||||
@Parameter(INPUT_PARAM)
|
||||
String inputData;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
checkArgumentNotNull(inputData, "Input data must not be null");
|
||||
save();
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.atInfo().withCause(e).log(
|
||||
"Usage error in attempting to save premium list from nomulus tool command");
|
||||
response.setPayload(ImmutableMap.of("error", e.getMessage(), "status", "error"));
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Unexpected error saving premium list to Datastore from nomulus tool command");
|
||||
response.setPayload(ImmutableMap.of("error", e.getMessage(), "status", "error"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs the premium list data at INFO, truncated if too long. */
|
||||
void logInputData() {
|
||||
String logData = (inputData == null) ? "(null)" : inputData;
|
||||
logger.atInfo().log(
|
||||
"Received the following input data: %s",
|
||||
lazy(
|
||||
() ->
|
||||
(logData.length() < MAX_LOGGING_PREMIUM_LIST_LENGTH)
|
||||
? logData
|
||||
: (logData.substring(0, MAX_LOGGING_PREMIUM_LIST_LENGTH) + "<truncated>")));
|
||||
}
|
||||
|
||||
/** Saves the premium list to both Datastore and Cloud SQL. */
|
||||
protected abstract void save();
|
||||
}
|
||||
@@ -1,79 +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.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* An action that creates a premium list, for use by the {@code nomulus create_premium_list}
|
||||
* command.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.TOOLS,
|
||||
path = CreatePremiumListAction.PATH,
|
||||
method = POST,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static final String OVERRIDE_PARAM = "override";
|
||||
public static final String PATH = "/_dr/admin/createPremiumList";
|
||||
public static final String CURRENCY = "currency";
|
||||
|
||||
@Inject @Parameter(OVERRIDE_PARAM) boolean override;
|
||||
|
||||
@Inject
|
||||
@Parameter("currency")
|
||||
CurrencyUnit currency;
|
||||
|
||||
@Inject CreatePremiumListAction() {}
|
||||
|
||||
@Override
|
||||
protected void save() {
|
||||
checkArgument(
|
||||
!PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"A premium list of this name already exists: %s",
|
||||
name);
|
||||
if (!override) {
|
||||
assertTldExists(
|
||||
name,
|
||||
"Premium names must match the name of the TLD they are intended to be used on"
|
||||
+ " (unless --override is specified), yet TLD %s does not exist");
|
||||
}
|
||||
logger.atInfo().log("Saving premium list for TLD %s", name);
|
||||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumListDao.save(name, currency, inputDataPreProcessed);
|
||||
String message =
|
||||
String.format("Saved premium list %s with %d entries", name, inputDataPreProcessed.size());
|
||||
logger.atInfo().log(message);
|
||||
response.setPayload(ImmutableMap.of("status", "success", "message", message));
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static google.registry.request.RequestParameters.extractBooleanParameter;
|
||||
import static google.registry.request.RequestParameters.extractIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
@@ -28,7 +27,6 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Dagger module for the tools package.
|
||||
@@ -55,30 +53,6 @@ public class ToolsServerModule {
|
||||
return (s == null) ? Optional.empty() : Optional.of(Boolean.parseBoolean(s));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("inputData")
|
||||
static String provideInput(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, CreatePremiumListAction.INPUT_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("premiumListName")
|
||||
static String provideName(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, CreatePremiumListAction.NAME_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("currency")
|
||||
static CurrencyUnit provideCurrency(HttpServletRequest req) {
|
||||
return CurrencyUnit.of(extractRequiredParameter(req, CreatePremiumListAction.CURRENCY));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("override")
|
||||
static boolean provideOverride(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, CreatePremiumListAction.OVERRIDE_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("printHeaderRow")
|
||||
static Optional<Boolean> providePrintHeaderRow(HttpServletRequest req) {
|
||||
|
||||
@@ -1,70 +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.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An action that creates a premium list, for use by the {@code nomulus create_premium_list}
|
||||
* command.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.TOOLS,
|
||||
path = UpdatePremiumListAction.PATH,
|
||||
method = POST,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static final String PATH = "/_dr/admin/updatePremiumList";
|
||||
|
||||
@Inject UpdatePremiumListAction() {}
|
||||
|
||||
@Override
|
||||
protected void save() {
|
||||
Optional<PremiumList> existingList = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
existingList.isPresent(),
|
||||
"Could not update premium list %s because it doesn't exist.",
|
||||
name);
|
||||
|
||||
logger.atInfo().log("Updating premium list for TLD %s", name);
|
||||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumList newPremiumList =
|
||||
PremiumListDao.save(name, existingList.get().getCurrency(), inputDataPreProcessed);
|
||||
|
||||
String message =
|
||||
String.format(
|
||||
"Updated premium list %s with %d entries.",
|
||||
newPremiumList.getName(), inputDataPreProcessed.size());
|
||||
logger.atInfo().log(message);
|
||||
response.setPayload(ImmutableMap.of("status", "success", "message", message));
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,6 @@
|
||||
<class>google.registry.persistence.converter.LocalDateConverter</class>
|
||||
<class>google.registry.persistence.converter.PostalInfoChoiceListConverter</class>
|
||||
<class>google.registry.persistence.converter.RegistrarPocSetConverter</class>
|
||||
<class>google.registry.persistence.converter.ReservedListKeySetConverter</class>
|
||||
<class>google.registry.persistence.converter.Spec11ThreatMatchThreatTypeSetConverter</class>
|
||||
<class>google.registry.persistence.converter.StatusValueSetConverter</class>
|
||||
<class>google.registry.persistence.converter.StringListConverter</class>
|
||||
|
||||
@@ -38,7 +38,6 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.truth.Truth8;
|
||||
@@ -55,12 +54,14 @@ import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.ofy.CommitLogBucket;
|
||||
import google.registry.model.ofy.CommitLogManifest;
|
||||
import google.registry.model.ofy.CommitLogMutation;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.replay.SqlReplayCheckpoint;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumList.PremiumEntry;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.translators.VKeyTranslatorFactory;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -419,8 +420,9 @@ public class ReplayCommitLogsToSqlActionTest {
|
||||
|
||||
createTld("tld");
|
||||
// Have a commit log with a couple objects that shouldn't be replayed
|
||||
ReservedList reservedList =
|
||||
new ReservedList.Builder().setReservedListMap(ImmutableMap.of()).setName("name").build();
|
||||
String triplet = RdeNamingUtils.makePartialName("tld", fakeClock.nowUtc(), RdeMode.FULL);
|
||||
RdeRevision rdeRevision =
|
||||
RdeRevision.create(triplet, "tld", fakeClock.nowUtc().toLocalDate(), RdeMode.FULL, 1);
|
||||
ForeignKeyIndex<DomainBase> fki = ForeignKeyIndex.create(newDomainBase("foo.tld"), now);
|
||||
tm().transact(
|
||||
() -> {
|
||||
@@ -430,8 +432,8 @@ public class ReplayCommitLogsToSqlActionTest {
|
||||
createCheckpoint(now.minusMinutes(1)),
|
||||
CommitLogManifest.create(
|
||||
getBucketKey(1), now.minusMinutes(1), ImmutableSet.of()),
|
||||
// Reserved list is dually-written non-replicated
|
||||
CommitLogMutation.create(manifestKey, reservedList),
|
||||
// RDE Revisions are not replicated
|
||||
CommitLogMutation.create(manifestKey, rdeRevision),
|
||||
// FKIs aren't replayed to SQL at all
|
||||
CommitLogMutation.create(manifestKey, fki));
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -85,7 +85,7 @@ public class ExpandRecurringBillingEventsActionTest
|
||||
|
||||
@Order(Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
public final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
public final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private DomainBase domain;
|
||||
private DomainHistory historyEntry;
|
||||
|
||||
@@ -0,0 +1,607 @@
|
||||
// Copyright 2021 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.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.AppEngineExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction.CertificateType;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction.RegistrarInfo;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link SendExpiringCertificateNotificationEmailAction}. */
|
||||
@DualDatabaseTest
|
||||
class SendExpiringCertificateNotificationEmailActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-05-24T20:21:22Z"));
|
||||
private final SendEmailService sendEmailService = mock(SendEmailService.class);
|
||||
private CertificateChecker certificateChecker;
|
||||
private SendExpiringCertificateNotificationEmailAction action;
|
||||
private Registrar sampleRegistrar;
|
||||
private Response response;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
certificateChecker =
|
||||
new CertificateChecker(
|
||||
ImmutableSortedMap.of(START_OF_TIME, 825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
|
||||
30,
|
||||
15,
|
||||
2048,
|
||||
ImmutableSet.of("secp256r1", "secp384r1"),
|
||||
clock);
|
||||
String expirationWarningEmailBodyText =
|
||||
" Hello Registrar %s,\n" + " The %s certificate is expiring on %s.";
|
||||
String expirationWarningEmailSubjectText = "expiring certificate notification email";
|
||||
|
||||
action =
|
||||
new SendExpiringCertificateNotificationEmailAction(
|
||||
expirationWarningEmailBodyText,
|
||||
expirationWarningEmailSubjectText,
|
||||
new InternetAddress("test@example.com"),
|
||||
sendEmailService,
|
||||
certificateChecker,
|
||||
response);
|
||||
|
||||
sampleRegistrar =
|
||||
persistResource(createRegistrar("clientId", "sampleRegistrar", null, null).build());
|
||||
}
|
||||
|
||||
/** Returns a sample registrar with a customized registrar name, client id and certificate* */
|
||||
private Registrar.Builder createRegistrar(
|
||||
String clientId,
|
||||
String registrarName,
|
||||
@Nullable X509Certificate certificate,
|
||||
@Nullable X509Certificate failOverCertificate)
|
||||
throws Exception {
|
||||
// set up only required fields sample test data
|
||||
Registrar.Builder builder =
|
||||
new Registrar.Builder()
|
||||
.setClientId(clientId)
|
||||
.setRegistrarName(registrarName)
|
||||
.setType(Registrar.Type.REAL)
|
||||
.setIanaIdentifier(8L)
|
||||
.setState(Registrar.State.ACTIVE)
|
||||
.setInternationalizedAddress(
|
||||
new RegistrarAddress.Builder()
|
||||
.setStreet(ImmutableList.of("very fake street"))
|
||||
.setCity("city")
|
||||
.setState("state")
|
||||
.setZip("99999")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.setPhoneNumber("+0.000000000")
|
||||
.setFaxNumber("+9.999999999")
|
||||
.setEmailAddress("contact-us@test.example")
|
||||
.setWhoisServer("whois.registrar.example")
|
||||
.setUrl("http://www.test.example");
|
||||
|
||||
if (failOverCertificate != null) {
|
||||
builder.setFailoverClientCertificate(
|
||||
certificateChecker.serializeCertificate(failOverCertificate), clock.nowUtc());
|
||||
}
|
||||
if (certificate != null) {
|
||||
builder.setClientCertificate(
|
||||
certificateChecker.serializeCertificate(certificate), clock.nowUtc());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_returnsTrue() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Optional<String> cert =
|
||||
Optional.of(certificateChecker.serializeCertificate(expiringCertificate));
|
||||
Registrar registrar =
|
||||
persistResource(
|
||||
makeRegistrar1()
|
||||
.asBuilder()
|
||||
.setFailoverClientCertificate(cert.get(), clock.nowUtc())
|
||||
.build());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
ImmutableList.of(
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("Will Doe")
|
||||
.setEmailAddress("will@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551213")
|
||||
.setFaxNumber("+1.3105551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
persistResource(registrar);
|
||||
assertThat(
|
||||
action.sendNotificationEmail(registrar, START_OF_TIME, CertificateType.FAILOVER, cert))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_returnsFalse_noEmailRecipients() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-02T00:00:00Z"))
|
||||
.cert();
|
||||
Optional<String> cert =
|
||||
Optional.of(certificateChecker.serializeCertificate(expiringCertificate));
|
||||
assertThat(
|
||||
action.sendNotificationEmail(
|
||||
sampleRegistrar, START_OF_TIME, CertificateType.FAILOVER, cert))
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_throwsRunTimeException() throws Exception {
|
||||
doThrow(new RuntimeException("this is a runtime exception"))
|
||||
.when(sendEmailService)
|
||||
.sendEmail(any());
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Optional<String> cert =
|
||||
Optional.of(certificateChecker.serializeCertificate(expiringCertificate));
|
||||
Registrar registrar =
|
||||
persistResource(
|
||||
makeRegistrar1()
|
||||
.asBuilder()
|
||||
.setFailoverClientCertificate(cert.get(), clock.nowUtc())
|
||||
.build());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
ImmutableList.of(
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("Will Doe")
|
||||
.setEmailAddress("will@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551213")
|
||||
.setFaxNumber("+1.3105551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
RuntimeException thrown =
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() ->
|
||||
action.sendNotificationEmail(
|
||||
registrar, START_OF_TIME, CertificateType.FAILOVER, cert));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains(
|
||||
String.format(
|
||||
"Failed to send expiring certificate notification email to registrar %s",
|
||||
registrar.getRegistrarName()));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_returnsFalse_noCertificate() {
|
||||
assertThat(
|
||||
action.sendNotificationEmail(
|
||||
sampleRegistrar, START_OF_TIME, CertificateType.FAILOVER, Optional.empty()))
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyMainCertificates()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
for (int i = 1; i <= numOfRegistrars; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrars);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyFailOverCertificates()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
for (int i = 1; i <= numOfRegistrars; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, null, expiringCertificate).build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrars);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_mixedOfCertificates() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfExpiringFailOverOnly = 2;
|
||||
int numOfExpiringPrimaryOnly = 3;
|
||||
for (int i = 1; i <= numOfExpiringFailOverOnly; i++) {
|
||||
persistResource(
|
||||
createRegistrar("cl" + i, "expiringFailOverOnly" + i, null, expiringCertificate).build());
|
||||
}
|
||||
for (int i = 1; i <= numOfExpiringPrimaryOnly; i++) {
|
||||
persistResource(
|
||||
createRegistrar("cli" + i, "expiringPrimaryOnly" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly + 1;
|
||||
i <= numOfRegistrars;
|
||||
i++) {
|
||||
persistResource(
|
||||
createRegistrar("client" + i, "regularReg" + i, expiringCertificate, expiringCertificate)
|
||||
.build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails())
|
||||
.isEqualTo(numOfRegistrars + numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_updatedSuccessfully_primaryCertificate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-02T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", expiringCertificate, null).build();
|
||||
persistResource(registrar);
|
||||
action.updateLastNotificationSentDate(registrar, clock.nowUtc(), CertificateType.PRIMARY);
|
||||
assertThat(loadByEntity(registrar).getLastExpiringCertNotificationSentDate())
|
||||
.isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_updatedSuccessfully_failOverCertificate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
action.updateLastNotificationSentDate(registrar, clock.nowUtc(), CertificateType.FAILOVER);
|
||||
assertThat(loadByEntity(registrar).getLastExpiringFailoverCertNotificationSentDate())
|
||||
.isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_noUpdates_noLastNotificationSentDate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
RuntimeException thrown =
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() -> action.updateLastNotificationSentDate(registrar, null, CertificateType.FAILOVER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Failed to update the last notification sent date to Registrar");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_noUpdates_invalidCertificateType() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
action.updateLastNotificationSentDate(
|
||||
registrar, clock.nowUtc(), CertificateType.valueOf("randomType")));
|
||||
assertThat(thrown).hasMessageThat().contains("No enum constant");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsPartOfRegistrars() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
|
||||
ImmutableList<RegistrarInfo> results = action.getRegistrarsWithExpiringCertificates();
|
||||
assertThat(results.size()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsPartOfRegistrars_failOverCertificateBranch()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, null, expiringCertificate).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, null, certificate).build());
|
||||
}
|
||||
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates().size())
|
||||
.isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsAllRegistrars() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
|
||||
int numOfRegistrarsWithExpiringCertificates = 5;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates().size())
|
||||
.isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsNoRegistrars() throws Exception {
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
for (int i = 1; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_noRegistrarsInDatabase() {
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_success_returnsAnEmptyList() {
|
||||
assertThat(action.getEmailAddresses(sampleRegistrar, Type.TECH)).isEmpty();
|
||||
assertThat(action.getEmailAddresses(sampleRegistrar, Type.ADMIN)).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_success_returnsAListOfEmails() throws Exception {
|
||||
Registrar registrar = persistResource(makeRegistrar1());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
ImmutableList.of(
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John Doe")
|
||||
.setEmailAddress("jd@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551213")
|
||||
.setFaxNumber("+1.3105551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John Smith")
|
||||
.setEmailAddress("js@example-registrar.tld")
|
||||
.setPhoneNumber("+1.1111111111")
|
||||
.setFaxNumber("+1.1111111111")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("Will Doe")
|
||||
.setEmailAddress("will@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551213")
|
||||
.setFaxNumber("+1.3105551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("Mike Doe")
|
||||
.setEmailAddress("mike@example-registrar.tld")
|
||||
.setPhoneNumber("+1.1111111111")
|
||||
.setFaxNumber("+1.1111111111")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John T")
|
||||
.setEmailAddress("john@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551215")
|
||||
.setFaxNumber("+1.3105551216")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
|
||||
.setVisibleInWhoisAsTech(true)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
assertThat(action.getEmailAddresses(registrar, Type.TECH))
|
||||
.containsExactly(
|
||||
new InternetAddress("will@example-registrar.tld"),
|
||||
new InternetAddress("jd@example-registrar.tld"),
|
||||
new InternetAddress("js@example-registrar.tld"));
|
||||
assertThat(action.getEmailAddresses(registrar, Type.ADMIN))
|
||||
.containsExactly(
|
||||
new InternetAddress("janedoe@theregistrar.com"), // comes with makeRegistrar1()
|
||||
new InternetAddress("mike@example-registrar.tld"),
|
||||
new InternetAddress("john@example-registrar.tld"));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_failure_returnsPartialListOfEmails_skipInvalidEmails() {
|
||||
// when building a new RegistrarContact object, there's already an email validation process.
|
||||
// if the registrarContact is created successful, the email address of the contact object
|
||||
// should already be validated. Ideally, there should not be an AddressException when creating
|
||||
// a new InternetAddress using the email address string of the contact object.
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_returnsEmailBodyText() {
|
||||
String registrarName = "good registrar";
|
||||
String certExpirationDateStr = "2021-06-15";
|
||||
CertificateType certificateType = CertificateType.PRIMARY;
|
||||
String emailBody =
|
||||
action.getEmailBody(
|
||||
registrarName, certificateType, DateTime.parse(certExpirationDateStr).toDate());
|
||||
assertThat(emailBody).contains(registrarName);
|
||||
assertThat(emailBody).contains(certificateType.getDisplayName());
|
||||
assertThat(emailBody).contains(certExpirationDateStr);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_throwsIllegalArgumentException_noExpirationDate() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.getEmailBody("good registrar", CertificateType.FAILOVER, null));
|
||||
assertThat(thrown).hasMessageThat().contains("Expiration date cannot be null");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_throwsIllegalArgumentException_noCertificateType() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
action.getEmailBody("good registrar", null, DateTime.parse("2021-06-15").toDate()));
|
||||
assertThat(thrown).hasMessageThat().contains("Certificate type cannot be null");
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@ import org.mockito.Mockito;
|
||||
class FlowRunnerTest {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngineRule = new AppEngineExtension.Builder().build();
|
||||
final AppEngineExtension appEngineRule =
|
||||
new AppEngineExtension.Builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
private final FlowRunner flowRunner = new FlowRunner();
|
||||
private final EppMetric.Builder eppMetricBuilder = EppMetric.builderForRequest(new FakeClock());
|
||||
|
||||
@@ -36,7 +36,7 @@ class ContactCheckFlowTest extends ResourceCheckFlowTestCase<ContactCheckFlow, C
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
ContactCheckFlowTest() {
|
||||
setEppInput("contact_check.xml");
|
||||
|
||||
@@ -45,7 +45,7 @@ class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, Cont
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
ContactCreateFlowTest() {
|
||||
setEppInput("contact_create.xml");
|
||||
|
||||
@@ -66,7 +66,7 @@ class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, Cont
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void initFlowTest() {
|
||||
|
||||
@@ -51,7 +51,7 @@ class ContactInfoFlowTest extends ResourceFlowTestCase<ContactInfoFlow, ContactR
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
ContactInfoFlowTest() {
|
||||
setEppInput("contact_info.xml");
|
||||
|
||||
@@ -54,7 +54,7 @@ class ContactTransferApproveFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -51,7 +51,7 @@ class ContactTransferCancelFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -46,7 +46,7 @@ class ContactTransferQueryFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -53,7 +53,7 @@ class ContactTransferRejectFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -65,7 +65,7 @@ class ContactTransferRequestFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
ContactTransferRequestFlowTest() {
|
||||
// We need the transfer to happen at exactly this time in order for the response to match up.
|
||||
|
||||
@@ -53,7 +53,7 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
ContactUpdateFlowTest() {
|
||||
setEppInput("contact_update.xml");
|
||||
|
||||
@@ -93,7 +93,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
DomainCheckFlowTest() {
|
||||
setEppInput("domain_check_one_tld.xml");
|
||||
|
||||
@@ -50,7 +50,7 @@ public class DomainClaimsCheckFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
DomainClaimsCheckFlowTest() {
|
||||
setEppInput("domain_check_claims.xml");
|
||||
|
||||
@@ -188,7 +188,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
DomainCreateFlowTest() {
|
||||
setEppInput("domain_create.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
|
||||
@@ -119,7 +119,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private DomainBase domain;
|
||||
private DomainHistory earlierHistoryEntry;
|
||||
|
||||
@@ -87,7 +87,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
/**
|
||||
* The domain_info_fee.xml default substitutions common to most tests.
|
||||
|
||||
@@ -112,7 +112,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, DomainBa
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void initDomainTest() {
|
||||
|
||||
@@ -90,7 +90,7 @@ class DomainRestoreRequestFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private static final ImmutableMap<String, String> FEE_06_MAP =
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee", "CURRENCY", "USD");
|
||||
|
||||
@@ -91,7 +91,7 @@ class DomainTransferApproveFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -71,7 +71,7 @@ class DomainTransferCancelFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -48,7 +48,7 @@ class DomainTransferQueryFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
|
||||
@@ -73,7 +73,7 @@ class DomainTransferRejectFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
@@ -126,7 +126,7 @@ class DomainTransferRequestFlowTest
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private static final ImmutableMap<String, String> BASE_FEE_MAP =
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
|
||||
@@ -120,7 +120,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void initDomainTest() {
|
||||
|
||||
@@ -36,7 +36,7 @@ class HostCheckFlowTest extends ResourceCheckFlowTestCase<HostCheckFlow, HostRes
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
HostCheckFlowTest() {
|
||||
setEppInput("host_check.xml");
|
||||
|
||||
@@ -67,7 +67,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, HostResour
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private void setEppHostCreateInput(String hostName, String hostAddrs) {
|
||||
setEppInput(
|
||||
|
||||
@@ -65,7 +65,7 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, HostResour
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
@BeforeEach
|
||||
void initFlowTest() {
|
||||
|
||||
@@ -93,7 +93,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, HostResour
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private void setEppHostUpdateInput(
|
||||
String oldHostName, String newHostName, String ipOrStatusToAdd, String ipOrStatusToRem) {
|
||||
|
||||
@@ -51,7 +51,7 @@ class PollAckFlowTest extends FlowTestCase<PollAckFlow> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
/** This is the message id being sent in the ACK request. */
|
||||
private static final long MESSAGE_ID = 3;
|
||||
|
||||
@@ -58,7 +58,7 @@ class PollRequestFlowTest extends FlowTestCase<PollRequestFlow> {
|
||||
|
||||
@Order(value = Order.DEFAULT - 2)
|
||||
@RegisterExtension
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
|
||||
final ReplayExtension replayExtension = ReplayExtension.createWithDoubleReplay(clock);
|
||||
|
||||
private DomainBase domain;
|
||||
private ContactResource contact;
|
||||
|
||||
@@ -33,15 +33,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.label.PremiumList.PremiumEntry;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@@ -50,19 +46,9 @@ class ReservedListTest {
|
||||
|
||||
private FakeClock clock = new FakeClock(DateTime.parse("2010-01-01T10:00:00Z"));
|
||||
|
||||
@Order(value = Order.DEFAULT - 1)
|
||||
@RegisterExtension
|
||||
final InjectExtension inject =
|
||||
new InjectExtension().withStaticFieldOverride(Ofy.class, "clock", clock);
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder()
|
||||
.withClock(clock)
|
||||
.withJpaUnitTestEntities(
|
||||
PremiumList.class, PremiumEntry.class, ReservedList.class, ReservedListEntry.class)
|
||||
.withDatastoreAndCloudSql()
|
||||
.build();
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withClock(clock).withCloudSql().build();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2020 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.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Unit tests for {@link ReservedListKeySetConverter}. */
|
||||
class ReservedListKeySetConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withJpaUnitTestEntities(ReservedListSetEntity.class)
|
||||
.build();
|
||||
|
||||
@Test
|
||||
void roundTripConversion_returnsSameSet() {
|
||||
Key<ReservedList> key1 = Key.create(getCrossTldKey(), ReservedList.class, "test1");
|
||||
Key<ReservedList> key2 = Key.create(getCrossTldKey(), ReservedList.class, "test2");
|
||||
Key<ReservedList> key3 = Key.create(getCrossTldKey(), ReservedList.class, "test3");
|
||||
|
||||
Set<Key<ReservedList>> reservedLists = ImmutableSet.of(key1, key2, key3);
|
||||
ReservedListSetEntity testEntity = new ReservedListSetEntity(reservedLists);
|
||||
jpaTm().transact(() -> jpaTm().insert(testEntity));
|
||||
ReservedListSetEntity persisted =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(ReservedListSetEntity.class, "id"));
|
||||
assertThat(persisted.reservedList).containsExactly(key1, key2, key3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
ReservedListSetEntity testEntity = new ReservedListSetEntity(null);
|
||||
jpaTm().transact(() -> jpaTm().insert(testEntity));
|
||||
ReservedListSetEntity persisted =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(ReservedListSetEntity.class, "id"));
|
||||
assertThat(persisted.reservedList).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyCollection_writesAndReadsEmptyCollectionSuccessfully() {
|
||||
ReservedListSetEntity testEntity = new ReservedListSetEntity(ImmutableSet.of());
|
||||
jpaTm().transact(() -> jpaTm().insert(testEntity));
|
||||
ReservedListSetEntity persisted =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(ReservedListSetEntity.class, "id"));
|
||||
assertThat(persisted.reservedList).isEmpty();
|
||||
}
|
||||
|
||||
@Entity(name = "ReservedListSetEntity")
|
||||
private static class ReservedListSetEntity extends ImmutableObject {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
Set<Key<ReservedList>> reservedList;
|
||||
|
||||
public ReservedListSetEntity() {}
|
||||
|
||||
ReservedListSetEntity(Set<Key<ReservedList>> reservedList) {
|
||||
this.reservedList = reservedList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
class GenerateSpec11ReportActionTest extends BeamActionTestBase {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine = AppEngineExtension.builder().withTaskQueue().build();
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2018-06-11T12:23:56Z"));
|
||||
private GenerateSpec11ReportAction action;
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
package google.registry.testing;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -28,9 +30,16 @@ import google.registry.model.ofy.ReplayQueue;
|
||||
import google.registry.model.ofy.TransactionInfo;
|
||||
import google.registry.model.replay.DatastoreEntity;
|
||||
import google.registry.model.replay.ReplicateToDatastoreAction;
|
||||
import google.registry.model.replay.SqlEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerImpl;
|
||||
import google.registry.persistence.transaction.Transaction;
|
||||
import google.registry.persistence.transaction.Transaction.Delete;
|
||||
import google.registry.persistence.transaction.Transaction.Mutation;
|
||||
import google.registry.persistence.transaction.Transaction.Update;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
@@ -186,12 +195,50 @@ public class ReplayExtension implements BeforeEachCallback, AfterEachCallback {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(mmuller): Verify that all entities are the same across both databases.
|
||||
for (TransactionEntity txn : sqlToDsReplicator.getTransactionBatch()) {
|
||||
if (sqlToDsReplicator.applyTransaction(txn)) {
|
||||
break;
|
||||
List<TransactionEntity> transactionBatch;
|
||||
do {
|
||||
transactionBatch = sqlToDsReplicator.getTransactionBatch();
|
||||
for (TransactionEntity txn : transactionBatch) {
|
||||
if (sqlToDsReplicator.applyTransaction(txn)) {
|
||||
throw new RuntimeException(
|
||||
"Error when replaying to Datastore in tests; see logs for more details");
|
||||
}
|
||||
if (compare) {
|
||||
ofyTm().transact(() -> compareSqlTransaction(txn));
|
||||
}
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
} while (!transactionBatch.isEmpty());
|
||||
}
|
||||
|
||||
/** Verifies that the replaying the SQL transaction created the same entities in Datastore. */
|
||||
private void compareSqlTransaction(TransactionEntity transactionEntity) {
|
||||
Transaction transaction;
|
||||
try {
|
||||
transaction = Transaction.deserialize(transactionEntity.getContents());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error during transaction deserialization.", e);
|
||||
}
|
||||
for (Mutation mutation : transaction.getMutations()) {
|
||||
if (mutation instanceof Update) {
|
||||
Update update = (Update) mutation;
|
||||
ImmutableObject fromTransactionEntity = (ImmutableObject) update.getEntity();
|
||||
ImmutableObject fromDatastore = ofyTm().loadByEntity(fromTransactionEntity);
|
||||
if (fromDatastore instanceof SqlEntity) {
|
||||
// We store the Datastore entity in the transaction, so use that if necessary
|
||||
fromDatastore = (ImmutableObject) ((SqlEntity) fromDatastore).toDatastoreEntity().get();
|
||||
}
|
||||
assertAboutImmutableObjects().that(fromDatastore).hasCorrectHashValue();
|
||||
assertAboutImmutableObjects()
|
||||
.that(fromDatastore)
|
||||
.isEqualAcrossDatabases(fromTransactionEntity);
|
||||
} else {
|
||||
Delete delete = (Delete) mutation;
|
||||
VKey<?> key = delete.getKey();
|
||||
assertWithMessage(String.format("Expected key %s to not exist in Datastore", key))
|
||||
.that(ofyTm().exists(key))
|
||||
.isFalse();
|
||||
}
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,6 @@ class UpdateReservedListCommandTest
|
||||
command.input = Paths.get(reservedTermsPath);
|
||||
command.init();
|
||||
|
||||
assertThat(command.prompt()).contains("Update ReservedList@xn--q9jyb4c_common-reserved");
|
||||
assertThat(command.prompt()).contains("Update reserved list for xn--q9jyb4c_common-reserved?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,109 +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.tools.server;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.loadPremiumEntries;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeJsonResponse;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CreatePremiumListAction}.
|
||||
*/
|
||||
public class CreatePremiumListActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
private CreatePremiumListAction action;
|
||||
private FakeJsonResponse response;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTlds("foo", "xn--q9jyb4c", "how");
|
||||
PremiumListDao.delete(PremiumListDao.getLatestRevision("foo").get());
|
||||
action = new CreatePremiumListAction();
|
||||
response = new FakeJsonResponse();
|
||||
action.response = response;
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_invalidRequest_missingInput_returnsErrorStatus() {
|
||||
action.name = "foo";
|
||||
action.run();
|
||||
assertThat(response.getResponseMap().get("status")).isEqualTo("error");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_invalidRequest_listAlreadyExists_returnsErrorStatus() {
|
||||
action.name = "how";
|
||||
action.inputData = "richer,JPY 5000";
|
||||
action.run();
|
||||
assertThat(response.getResponseMap().get("status")).isEqualTo("error");
|
||||
Object obj = response.getResponseMap().get("error");
|
||||
assertThat(obj).isInstanceOf(String.class);
|
||||
String error = obj.toString();
|
||||
assertThat(error).contains("A premium list of this name already exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_nonExistentTld_fails() {
|
||||
action.name = "zanzibar";
|
||||
action.inputData = "zanzibar,USD 100";
|
||||
action.run();
|
||||
assertThat(response.getResponseMap().get("status")).isEqualTo("error");
|
||||
assertThat(response.getResponseMap().get("error").toString())
|
||||
.isEqualTo(
|
||||
"Premium names must match the name of the TLD they are intended to be used on"
|
||||
+ " (unless --override is specified), yet TLD zanzibar does not exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_nonExistentTld_successWithOverride() {
|
||||
action.name = "zanzibar";
|
||||
action.inputData = "zanzibar,USD 100";
|
||||
action.override = true;
|
||||
action.currency = USD;
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(loadPremiumEntries(PremiumListDao.getLatestRevision("zanzibar").get())).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_success() {
|
||||
action.name = "foo";
|
||||
action.inputData = "rich,USD 25\nricher,USD 1000\n";
|
||||
action.currency = USD;
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
PremiumList premiumList = PremiumListDao.getLatestRevision("foo").get();
|
||||
assertThat(loadPremiumEntries(premiumList)).hasSize(2);
|
||||
assertThat(PremiumListDao.getPremiumPrice(premiumList.getName(), "rich"))
|
||||
.hasValue(Money.parse("USD 25"));
|
||||
assertThat(PremiumListDao.getPremiumPrice(premiumList.getName(), "diamond")).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,110 +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.tools.server;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.loadPremiumEntries;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeJsonResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link UpdatePremiumListAction}. */
|
||||
class UpdatePremiumListActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
private UpdatePremiumListAction action;
|
||||
private FakeJsonResponse response;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTlds("foo", "xn--q9jyb4c", "how");
|
||||
action = new UpdatePremiumListAction();
|
||||
response = new FakeJsonResponse();
|
||||
action.response = response;
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_invalidRequest_missingInput_returnsErrorStatus() {
|
||||
action.name = "foo";
|
||||
action.run();
|
||||
assertThat(response.getResponseMap().get("status")).isEqualTo("error");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_invalidRequest_listDoesNotExist_returnsErrorStatus() {
|
||||
action.name = "bamboozle";
|
||||
action.inputData = "richer,JPY 5000";
|
||||
action.run();
|
||||
assertThat(response.getResponseMap().get("status")).isEqualTo("error");
|
||||
Object obj = response.getResponseMap().get("error");
|
||||
assertThat(obj).isInstanceOf(String.class);
|
||||
String error = obj.toString();
|
||||
assertThat(error).contains("Could not update premium list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_success() {
|
||||
List<String> inputLines =
|
||||
Splitter.on('\n')
|
||||
.omitEmptyStrings()
|
||||
.splitToList(
|
||||
readResourceUtf8(DatabaseHelper.class, "default_premium_list_testdata.csv"));
|
||||
PremiumListDao.save("foo", USD, inputLines);
|
||||
action.name = "foo";
|
||||
action.inputData = "rich,USD 75\nricher,USD 5000\npoor, USD 0.99";
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(loadPremiumEntries(PremiumListDao.getLatestRevision("foo").get())).hasSize(3);
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "rich"))
|
||||
.hasValue(Money.parse("USD 75"));
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "richer"))
|
||||
.hasValue(Money.parse("USD 5000"));
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "poor"))
|
||||
.hasValue(Money.parse("USD 0.99"));
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "diamond")).isEmpty();
|
||||
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
PremiumList persistedList = PremiumListDao.getLatestRevision("foo").get();
|
||||
assertThat(persistedList.getLabelsToPrices())
|
||||
.containsEntry("rich", new BigDecimal("75.00"));
|
||||
assertThat(persistedList.getLabelsToPrices())
|
||||
.containsEntry("richer", new BigDecimal("5000.00"));
|
||||
assertThat(persistedList.getLabelsToPrices())
|
||||
.containsEntry("poor", BigDecimal.valueOf(0.99));
|
||||
assertThat(persistedList.getLabelsToPrices()).doesNotContainKey("diamond");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -19,4 +19,3 @@ Recurring
|
||||
Registrar
|
||||
RegistrarContact
|
||||
Registry
|
||||
ReservedList
|
||||
|
||||
@@ -4,6 +4,5 @@ Cursor
|
||||
Registrar
|
||||
RegistrarContact
|
||||
Registry
|
||||
ReservedList
|
||||
ServerSecret
|
||||
TmchCrl
|
||||
|
||||
@@ -15,4 +15,3 @@ Recurring
|
||||
Registrar
|
||||
RegistrarContact
|
||||
Registry
|
||||
ReservedList
|
||||
|
||||
@@ -789,26 +789,6 @@ enum google.registry.model.tld.Registry$TldType {
|
||||
REAL;
|
||||
TEST;
|
||||
}
|
||||
enum google.registry.model.tld.label.ReservationType {
|
||||
ALLOWED_IN_SUNRISE;
|
||||
FULLY_BLOCKED;
|
||||
NAME_COLLISION;
|
||||
RESERVED_FOR_ANCHOR_TENANT;
|
||||
RESERVED_FOR_SPECIFIC_USE;
|
||||
}
|
||||
class google.registry.model.tld.label.ReservedList {
|
||||
@Id java.lang.String name;
|
||||
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
|
||||
boolean shouldPublish;
|
||||
java.util.Map<java.lang.String, google.registry.model.tld.label.ReservedList$ReservedListEntry> reservedListMap;
|
||||
org.joda.time.DateTime creationTimestamp;
|
||||
}
|
||||
class google.registry.model.tld.label.ReservedList$ReservedListEntry {
|
||||
@Id java.lang.String domainLabel;
|
||||
google.registry.model.tld.label.ReservationType reservationType;
|
||||
java.lang.Long revisionId;
|
||||
java.lang.String comment;
|
||||
}
|
||||
class google.registry.model.tmch.ClaimsList {
|
||||
@Id long id;
|
||||
@Parent com.googlecode.objectify.Key<google.registry.model.tmch.ClaimsList$ClaimsListRevision> parent;
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/cron/commitLogCheckpoint CommitLogCheckpointAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/commitLogFanout CommitLogFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/fanout TldFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/readDnsQueue ReadDnsQueueAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/dnsRefresh RefreshDnsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/backupDatastore BackupDatastoreAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/brdaCopy BrdaCopyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/checkDatastoreBackup CheckBackupAction POST,GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/copyDetailReports CopyDetailReportsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/createSyntheticHistoryEntries CreateSyntheticHistoryEntriesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteContactsAndHosts DeleteContactsAndHostsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteOldCommitLogs DeleteOldCommitLogsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteProberData DeleteProberDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/expandRecurringBillingEvents ExpandRecurringBillingEventsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportCommitLogDiff ExportCommitLogDiffAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportDomainLists ExportDomainListsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateInvoices GenerateInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnUpload NordnUploadAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnVerify NordnVerifyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/pollBigqueryJob BigqueryPollJobAction GET,POST y INTERNAL APP IGNORED
|
||||
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishInvoices PublishInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeReport RdeReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeStaging RdeStagingAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeUpload RdeUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/relockDomain RelockDomainAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/replayCommitLogsToSql ReplayCommitLogsToSqlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveAllEppResources ResaveAllEppResourcesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveEntity ResaveEntityAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchCrl TmchCrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchDnl TmchDnlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchSmdrl TmchSmdrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateSnapshotView UpdateSnapshotViewAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/uploadDatastoreBackup UploadDatastoreBackupAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutCloudSql WipeOutCloudSqlAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutDatastore WipeoutDatastoreAction GET n INTERNAL,API APP ADMIN
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/cron/commitLogCheckpoint CommitLogCheckpointAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/commitLogFanout CommitLogFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/fanout TldFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/readDnsQueue ReadDnsQueueAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/dnsRefresh RefreshDnsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/backupDatastore BackupDatastoreAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/brdaCopy BrdaCopyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/checkDatastoreBackup CheckBackupAction POST,GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/copyDetailReports CopyDetailReportsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/createSyntheticHistoryEntries CreateSyntheticHistoryEntriesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteContactsAndHosts DeleteContactsAndHostsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteOldCommitLogs DeleteOldCommitLogsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteProberData DeleteProberDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/expandRecurringBillingEvents ExpandRecurringBillingEventsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportCommitLogDiff ExportCommitLogDiffAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportDomainLists ExportDomainListsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateInvoices GenerateInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnUpload NordnUploadAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnVerify NordnVerifyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/pollBigqueryJob BigqueryPollJobAction GET,POST y INTERNAL APP IGNORED
|
||||
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishInvoices PublishInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeReport RdeReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeStaging RdeStagingAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeUpload RdeUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/relockDomain RelockDomainAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/replayCommitLogsToSql ReplayCommitLogsToSqlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveAllEppResources ResaveAllEppResourcesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveEntity ResaveEntityAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchCrl TmchCrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchDnl TmchDnlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchSmdrl TmchSmdrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateSnapshotView UpdateSnapshotViewAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/uploadDatastoreBackup UploadDatastoreBackupAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutCloudSql WipeOutCloudSqlAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutDatastore WipeoutDatastoreAction GET n INTERNAL,API APP ADMIN
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/admin/createGroups CreateGroupsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/createPremiumList CreatePremiumListAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/domains ListDomainsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/hosts ListHostsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/registrars ListRegistrarsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/reservedLists ListReservedListsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/list/tlds ListTldsAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/updatePremiumList UpdatePremiumListAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/admin/verifyOte VerifyOteAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/epptool EppToolAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/loadtest LoadTestAction POST y INTERNAL,API APP ADMIN
|
||||
|
||||
@@ -62,8 +62,7 @@ to `ToolsServlet` to execute the action on the server (these commands implement
|
||||
[Remote API](https://cloud.google.com/appengine/docs/java/tools/remoteapi)
|
||||
(these commands implement `RemoteApiCommand`). Server-side commands take more
|
||||
work to implement because they require both a client and a server-side
|
||||
component, e.g. `CreatePremiumListCommand.java` and
|
||||
`CreatePremiumListAction.java` respectively for creating a premium list.
|
||||
component.
|
||||
However, they are fully capable of doing anything that is possible with App
|
||||
Engine, including running a large MapReduce, because they execute on the tools
|
||||
service in the App Engine cloud.
|
||||
|
||||
Reference in New Issue
Block a user