Compare commits

...
Author SHA1 Message Date
Michael MullerandGitHub e386bf5bd8 Get persistence of DomainBase actually working (#438)
* Get persistence of DomainBase actually working

Fix all of the existing problems with DomainBase persistence:
- Remove "final" keywords on getters that cause errors during startup.
- Remove Transient from creationTime (since there's a converter for
  CreateAutoTimestamp)
- Fix DesignatedContext persistence so that it only creates a single table.
  This is a lot more efficient given that these are many-to-one with their
  domains.
- Add a flyway script, update the golden schema.
- Create a unit test, add it to the integration test suite.

* Changes request in review

* Regenerated generated schema file.

* Changes for review

* Persist status value enum set

* Changes in response to review

* Changes requested in review

* Fixes for #456

* Rename Domain "status" column to "statuses"
2020-01-28 11:32:26 -05:00
sarahcaseybotandGitHub 1912453f4e Fix IcannReportingUploadAction to use correct year in January (#452)
* Fix IcannReportingUploadAction to use correct year in January

* small fixes
2020-01-27 17:38:59 -05:00
Shicong HuangandGitHub 43682aa7f4 Enable Cloud SQL for nomulus command implementing CommandWithRemoteApi (#460) 2020-01-27 10:28:38 -05:00
19 changed files with 360 additions and 213 deletions
@@ -50,8 +50,10 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import org.joda.time.Duration;
@@ -87,7 +89,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
// Map the method to XML, not the field, because if we map the field (with an adaptor class) it
// will never be omitted from the xml even if the timestamp inside creationTime is null and we
// return null from the adaptor. (Instead it gets written as an empty tag.)
@Index @Transient CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/**
* The time when this resource was or will be deleted.
@@ -105,7 +107,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
@Index
DateTime deletionTime;
/**
* The time that this resource was last updated.
*
@@ -116,7 +117,10 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
DateTime lastEppUpdateTime;
/** Status values associated with this resource. */
@Transient Set<StatusValue> status;
@Type(type = "google.registry.model.eppcommon.StatusValue$StatusValueSetType")
@Column(name = "statuses")
// TODO(mmuller): rename to "statuses" once we're off datastore.
Set<StatusValue> status;
/**
* Sorted map of {@link DateTime} keys (modified time) to {@link CommitLogManifest} entries.
@@ -126,6 +130,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
*
* @see google.registry.model.translators.CommitLogRevisionsTranslatorFactory
*/
@Transient
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
public final String getRepoId() {
@@ -136,15 +141,15 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
return creationTime.getTimestamp();
}
public final String getCreationClientId() {
public String getCreationClientId() {
return creationClientId;
}
public final DateTime getLastEppUpdateTime() {
public DateTime getLastEppUpdateTime() {
return lastEppUpdateTime;
}
public final String getLastEppUpdateClientId() {
public String getLastEppUpdateClientId() {
return lastEppUpdateClientId;
}
@@ -162,7 +167,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
return nullToEmptyImmutableCopy(status);
}
public final DateTime getDeletionTime() {
public DateTime getDeletionTime() {
return deletionTime;
}
@@ -21,7 +21,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import javax.persistence.Id;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlEnumValue;
/**
@@ -40,7 +40,7 @@ import javax.xml.bind.annotation.XmlEnumValue;
* - Contact and Client Identifiers</a>
*/
@Embed
@javax.persistence.Entity
@Embeddable
public class DesignatedContact extends ImmutableObject {
/**
@@ -67,7 +67,7 @@ public class DesignatedContact extends ImmutableObject {
Type type;
@Index @Id Key<ContactResource> contact;
@Index Key<ContactResource> contact;
public Type getType() {
return type;
@@ -75,6 +75,7 @@ import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Transient;
import org.joda.time.DateTime;
import org.joda.time.Interval;
@@ -89,8 +90,16 @@ import org.joda.time.Interval;
*/
@ReportedOn
@Entity
@javax.persistence.Entity
@javax.persistence.Table(name = "Domain")
@javax.persistence.Entity(name = "Domain")
@javax.persistence.Table(
name = "Domain",
indexes = {
@javax.persistence.Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "currentSponsorClientId"),
@javax.persistence.Index(columnList = "deletionTime"),
@javax.persistence.Index(columnList = "fullyQualifiedDomainName"),
@javax.persistence.Index(columnList = "tld")
})
@ExternalMessagingName("domain")
public class DomainBase extends EppResource
implements ForeignKeyedEppResource, ResourceWithTransferData {
@@ -105,7 +114,7 @@ public class DomainBase extends EppResource
StatusValue.INACTIVE,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_HOLD);
/**
* Fully qualified domain name (puny-coded), which serves as the foreign key for this domain.
*
@@ -115,22 +124,21 @@ public class DomainBase extends EppResource
*
* @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase(Locale.ENGLISH)
*/
@Index
String fullyQualifiedDomainName;
@Index String fullyQualifiedDomainName;
/** The top level domain this is under, dernormalized from {@link #fullyQualifiedDomainName}. */
@Index
String tld;
/** References to hosts that are the nameservers for the domain. */
@Index @ElementCollection Set<Key<HostResource>> nsHosts;
@Index @ElementCollection @Transient Set<Key<HostResource>> nsHosts;
/**
* The union of the contacts visible via {@link #getContacts} and {@link #getRegistrant}.
*
* <p>These are stored in one field so that we can query across all contacts at once.
*/
@ElementCollection Set<DesignatedContact> allContacts;
@Transient Set<DesignatedContact> allContacts;
/** Authorization info (aka transfer secret) of the domain. */
@Embedded
@@ -146,7 +154,7 @@ public class DomainBase extends EppResource
* <p>This is {@literal @}XmlTransient because it needs to be returned under the "extension" tag
* of an info response rather than inside the "infData" tag.
*/
@ElementCollection Set<DelegationSignerData> dsData;
@Transient Set<DelegationSignerData> dsData;
/**
* The claims notice supplied when this application or domain was created, if there was one. It's
@@ -177,7 +185,8 @@ public class DomainBase extends EppResource
String idnTableName;
/** Fully qualified host names of this domain's active subordinate hosts. */
@ElementCollection Set<String> subordinateHosts;
@org.hibernate.annotations.Type(type = "google.registry.persistence.StringSetUserType")
Set<String> subordinateHosts;
/** When this domain's registration will expire. */
DateTime registrationExpirationTime;
@@ -189,7 +198,7 @@ public class DomainBase extends EppResource
* refer to a {@link PollMessage} timed to when the domain is fully deleted. If the domain is
* restored, the message should be deleted.
*/
Key<PollMessage.OneTime> deletePollMessage;
@Transient Key<PollMessage.OneTime> deletePollMessage;
/**
* The recurring billing event associated with this domain's autorenewals.
@@ -199,7 +208,7 @@ public class DomainBase extends EppResource
* {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one
* should be created, and this field should be updated to point to the new one.
*/
Key<BillingEvent.Recurring> autorenewBillingEvent;
@Transient Key<BillingEvent.Recurring> autorenewBillingEvent;
/**
* The recurring poll message associated with this domain's autorenewals.
@@ -209,10 +218,10 @@ public class DomainBase extends EppResource
* {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one
* should be created, and this field should be updated to point to the new one.
*/
Key<PollMessage.Autorenew> autorenewPollMessage;
@Transient Key<PollMessage.Autorenew> autorenewPollMessage;
/** The unexpired grace periods for this domain (some of which may not be active yet). */
@ElementCollection Set<GracePeriod> gracePeriods;
@Transient @ElementCollection Set<GracePeriod> gracePeriods;
/**
* The id of the signed mark that was used to create this domain in sunrise.
@@ -223,31 +232,7 @@ public class DomainBase extends EppResource
String smdId;
/** Data about any pending or past transfers on this domain. */
@Embedded
@AttributeOverrides({
@AttributeOverride(
name = "transferRequestTrid",
column = @Column(name = "transfer_data_request_trid")),
@AttributeOverride(
name = "transferPeriod",
column = @Column(name = "transfer_data_transfer_period")),
@AttributeOverride(
name = "transferredRegistrationExpirationTime",
column = @Column(name = "transfer_data_registration_expiration_time")),
@AttributeOverride(
name = "serverApproveEntities",
column = @Column(name = "transfer_data_server_approve_entities")),
@AttributeOverride(
name = "serverApproveBillingEvent",
column = @Column(name = "transfer_data_server_approve_billing_event")),
@AttributeOverride(
name = "serverApproveAutorenewEvent",
column = @Column(name = "transfer_data_server_approve_autorenrew_event")),
@AttributeOverride(
name = "serverApproveAutorenewPollMessage",
column = @Column(name = "transfer_data_server_approve_autorenrew_poll_message")),
})
TransferData transferData;
@Transient TransferData transferData;
/**
* The time that this resource was last transferred.
@@ -285,7 +270,7 @@ public class DomainBase extends EppResource
}
@Override
public final TransferData getTransferData() {
public TransferData getTransferData() {
return Optional.ofNullable(transferData).orElse(TransferData.EMPTY);
}
@@ -25,6 +25,7 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.model.translators.StatusValueAdapter;
import google.registry.persistence.EnumSetUserType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
@@ -164,4 +165,12 @@ public enum StatusValue implements EppEnum {
public static StatusValue fromXmlName(String xmlName) {
return StatusValue.valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, nullToEmpty(xmlName)));
}
/** Hibernate type for sets of {@link StatusValue}. */
public static class StatusValueSetType extends EnumSetUserType<StatusValue> {
@Override
protected Object convertToElem(Object value) {
return StatusValue.valueOf((String) value);
}
}
}
@@ -156,7 +156,7 @@ public class ClaimsListShard extends ImmutableObject {
try {
loadAndCompareCloudSqlList(datastoreList);
} catch (Throwable t) {
logger.atSevere().withCause(t).log("Error comparing reserved lists.");
logger.atSevere().withCause(t).log("Error comparing claims lists.");
}
return datastoreList;
};
@@ -122,8 +122,7 @@ public final class IcannReportingStagingAction implements Runnable {
TaskOptions uploadTask =
TaskOptions.Builder.withUrl(IcannReportingUploadAction.PATH)
.method(Method.POST)
.countdownMillis(Duration.standardMinutes(2).getMillis())
.param(PARAM_SUBDIR, subdir);
.countdownMillis(Duration.standardMinutes(2).getMillis());
QueueFactory.getQueue(CRON_QUEUE).add(uploadTask);
return null;
},
@@ -20,7 +20,6 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SUBDIR;
import static google.registry.request.Action.Method.POST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
@@ -40,7 +39,6 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
@@ -91,8 +89,6 @@ public final class IcannReportingUploadAction implements Runnable {
@Config("reportingBucket")
String reportingBucket;
@Inject @Parameter(PARAM_SUBDIR) String subdir;
@Inject GcsUtils gcsUtils;
@Inject IcannHttpReporter icannReporter;
@Inject Retrier retrier;
@@ -145,7 +141,12 @@ public final class IcannReportingUploadAction implements Runnable {
CursorType cursorType,
String tldStr,
ImmutableMap.Builder<String, Boolean> reportSummaryBuilder) {
String reportBucketname = String.format("%s/%s", reportingBucket, subdir);
DateTime cursorTimeMinusMonth = cursorTime.withDayOfMonth(1).minusMonths(1);
String reportSubdir =
String.format(
"icann/monthly/%d-%02d",
cursorTimeMinusMonth.getYear(), cursorTimeMinusMonth.getMonthOfYear());
String reportBucketname = String.format("%s/%s", reportingBucket, reportSubdir);
String filename = getFileName(cursorType, cursorTime, tldStr);
final GcsFilename gcsFilename = new GcsFilename(reportBucketname, filename);
logger.atInfo().log("Reading ICANN report %s from bucket %s", filename, reportBucketname);
@@ -195,12 +196,13 @@ public final class IcannReportingUploadAction implements Runnable {
}
private String getFileName(CursorType cursorType, DateTime cursorTime, String tld) {
DateTime cursorTimeMinusMonth = cursorTime.withDayOfMonth(1).minusMonths(1);
return String.format(
"%s%s%d%02d.csv",
tld,
(cursorType.equals(CursorType.ICANN_UPLOAD_ACTIVITY) ? "-activity-" : "-transactions-"),
cursorTime.year().get(),
cursorTime.withDayOfMonth(1).minusMonths(1).monthOfYear().get());
cursorTimeMinusMonth.year().get(),
cursorTimeMinusMonth.monthOfYear().get());
}
/** Returns a map of each cursor to the CursorType and tld. */
@@ -1,23 +0,0 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
/**
* Marker interface for commands that use Cloud Sql.
*
* <p>Just implementing this is sufficient to use Cloud Sql; {@link RegistryTool} will install it as
* needed.
*/
interface CommandWithCloudSql extends CommandWithRemoteApi {}
@@ -34,7 +34,7 @@ import javax.inject.Inject;
/** Shared base class for commands to registry lock or unlock a domain via EPP. */
public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
implements CommandWithCloudSql {
implements CommandWithRemoteApi {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -212,8 +212,6 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
}
// CommandWithRemoteApis need to have the remote api installed to work.
// CommandWithCloudSql extends CommandWithRemoteApi so the command will also get the remote
// api installed. This is because the DB password is stored in Datastore.
if (command instanceof CommandWithRemoteApi) {
if (installer == null) {
installer = new RemoteApiInstaller();
@@ -235,9 +233,10 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
// Make sure we start the command with a clean cache, so that any previous command won't
// interfere with this one.
ofy().clearSessionCache();
}
if (command instanceof CommandWithCloudSql) {
// 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
// and it is already initialized above.
RegistryToolEnvironment.get().enableJpaTm();
}
@@ -29,7 +29,7 @@ import org.joda.time.DateTime;
/** Modifies {@link Cursor} timestamps used by locking rolling cursor tasks, like in RDE. */
@Parameters(separators = " =", commandDescription = "Modifies cursor timestamps used by LRC tasks")
final class UpdateCursorsCommand extends ConfirmingCommand implements CommandWithCloudSql {
final class UpdateCursorsCommand extends ConfirmingCommand implements CommandWithRemoteApi {
@Parameter(description = "TLDs on which to operate. Omit for global cursors.")
private List<String> tlds;
@@ -32,7 +32,7 @@ import java.util.List;
/** A command to upload a {@link ClaimsListShard}. */
@Parameters(separators = " =", commandDescription = "Manually upload a new claims list file")
final class UploadClaimsListCommand extends ConfirmingCommand implements CommandWithCloudSql {
final class UploadClaimsListCommand extends ConfirmingCommand implements CommandWithRemoteApi {
@Parameter(description = "Claims list filename")
private List<String> mainParameters = new ArrayList<>();
@@ -0,0 +1,121 @@
// 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.model.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import javax.persistence.EntityManager;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Verify that we can store/retrieve DomainBase objects from a SQL database. */
@RunWith(JUnit4.class)
public class DomainBaseSqlTest extends EntityTestCase {
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().buildIntegrationWithCoverageRule();
DomainBase domain;
Key<ContactResource> contactKey;
Key<ContactResource> contact2Key;
@Before
public void setUp() {
contactKey = Key.create(ContactResource.class, "contact_id1");
contact2Key = Key.create(ContactResource.class, "contact_id2");
domain =
new DomainBase.Builder()
.setFullyQualifiedDomainName("example.com")
.setRepoId("4-COM")
.setCreationClientId("a registrar")
.setLastEppUpdateTime(clock.nowUtc())
.setLastEppUpdateClientId("AnotherRegistrar")
.setLastTransferTime(clock.nowUtc())
.setStatusValues(
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_HOLD))
.setRegistrant(contactKey)
.setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorClientId("losing")
.setRegistrationExpirationTime(clock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.setLaunchNotice(
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
.setSmdId("smdid")
.build();
}
@Test
public void testDomainBasePersistence() {
jpaTm()
.transact(
() -> {
// Persist the domain.
EntityManager em = jpaTm().getEntityManager();
em.persist(domain);
});
jpaTm()
.transact(
() -> {
// Load the domain in its entirety.
EntityManager em = jpaTm().getEntityManager();
DomainBase result = em.find(DomainBase.class, "4-COM");
// Fix contacts, grace period and DS data, since we can't persist them yet.
result =
result
.asBuilder()
.setRegistrant(contactKey)
.setContacts(
ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setDsData(
ImmutableSet.of(
DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.build();
// Fix the original creation timestamp (this gets initialized on first write)
DomainBase org = domain.asBuilder().setCreationTime(result.getCreationTime()).build();
// Note that the equality comparison forces a lazy load of all fields.
assertThat(result).isEqualTo(org);
});
}
}
@@ -81,12 +81,8 @@ public class IcannReportingStagingActionTest {
.thenReturn(ImmutableList.of("c", "d"));
}
private static void assertUploadTaskEnqueued(String subdir) {
TaskMatcher matcher =
new TaskMatcher()
.url("/_dr/task/icannReportingUpload")
.method("POST")
.param("subdir", subdir);
private static void assertUploadTaskEnqueued() {
TaskMatcher matcher = new TaskMatcher().url("/_dr/task/icannReportingUpload").method("POST");
assertTasksEnqueued("retryable-cron-tasks", matcher);
}
@@ -103,7 +99,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 2 ICANN reports:\na\nb",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -119,7 +115,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -138,7 +134,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -78,7 +78,6 @@ public class IcannReportingUploadActionTest {
action.icannReporter = mockReporter;
action.gcsUtils = new GcsUtils(gcsService, 1024);
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
action.subdir = "icann/monthly/2006-06";
action.reportingBucket = "basin";
action.emailService = emailService;
action.sender = new InternetAddress("sender@example.com");
@@ -151,6 +150,43 @@ public class IcannReportingUploadActionTest {
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_january() throws Exception {
clock.setTo(DateTime.parse("2006-01-22T00:30:00Z"));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_TX, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2005-12", "tld-transactions-200512.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2005-12", "tld-activity-200512.csv"),
PAYLOAD_SUCCESS);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-activity-200512.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-transactions-200512.csv")).thenReturn(true);
IcannReportingUploadAction action = createAction();
action.run();
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-activity-200512.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200512.csv");
verifyNoMoreInteractions(mockReporter);
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 2/2 succeeded",
"Report Filename - Upload status:\n"
+ "tld-activity-200512.csv - SUCCESS\n"
+ "tld-transactions-200512.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_advancesCursor() throws Exception {
writeGcsFile(
@@ -280,15 +316,18 @@ public class IcannReportingUploadActionTest {
@Test
public void testFail_fileNotFound() throws Exception {
clock.setTo(DateTime.parse("2006-01-22T00:30:00Z"));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
IcannReportingUploadAction action = createAction();
action.subdir = "somewhere/else";
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.SEVERE,
"Could not upload ICANN_UPLOAD_ACTIVITY report for foo because file"
+ " foo-activity-200606.csv did not exist");
"Could not upload ICANN_UPLOAD_ACTIVITY report for tld because file"
+ " tld-activity-200512.csv did not exist");
}
@Test
@@ -298,7 +337,6 @@ public class IcannReportingUploadActionTest {
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-08-01TZ"), Registry.get("foo")));
clock.setTo(DateTime.parse("2006-08-01T00:30:00Z"));
IcannReportingUploadAction action = createAction();
action.subdir = "icann/monthly/2006-07";
action.run();
assertAboutLogs()
.that(logHandler)
@@ -15,6 +15,7 @@
package google.registry.schema.integration;
import com.google.common.truth.Expect;
import google.registry.model.domain.DomainBaseSqlTest;
import google.registry.model.registry.RegistryLockDaoTest;
import google.registry.persistence.transaction.JpaEntityCoverage;
import google.registry.schema.cursor.CursorDaoTest;
@@ -55,6 +56,7 @@ import org.junit.runners.Suite.SuiteClasses;
CursorDaoTest.class,
DomainLockUtilsTest.class,
LockDomainCommandTest.class,
DomainBaseSqlTest.class,
PremiumListDaoTest.class,
RegistryLockDaoTest.class,
RegistryLockGetActionTest.class,
@@ -0,0 +1,44 @@
-- 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.
create table "Domain" (
repo_id text not null,
creation_client_id text,
creation_time timestamptz,
current_sponsor_client_id text,
deletion_time timestamptz,
last_epp_update_client_id text,
last_epp_update_time timestamptz,
statuses text[],
auth_info_repo_id text,
auth_info_value text,
fully_qualified_domain_name text,
idn_table_name text,
last_transfer_time timestamptz,
launch_notice_accepted_time timestamptz,
launch_notice_expiration_time timestamptz,
launch_notice_tcn_id text,
launch_notice_validator_id text,
registration_expiration_time timestamptz,
smd_id text,
subordinate_hosts text[],
tld text,
primary key (repo_id)
);
create index IDX8nr0ke9mrrx4ewj6pd2ag4rmr on "Domain" (creation_time);
create index IDX8ffrqm27qtj20jac056j7yq07 on "Domain" (current_sponsor_client_id);
create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
create index IDX1rcgkdd777bpvj0r94sltwd5y on "Domain" (fully_qualified_domain_name);
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
@@ -42,25 +42,17 @@
primary key (key_tag)
);
create table "DesignatedContact" (
contact bytea not null,
type int4,
primary key (contact)
);
create table "Domain" (
repo_id text not null,
creation_client_id text,
creation_time timestamptz,
current_sponsor_client_id text,
deletion_time timestamptz,
last_epp_update_client_id text,
last_epp_update_time timestamptz,
revisions bytea,
statuses text[],
auth_info_repo_id text,
auth_info_value text,
autorenew_billing_event bytea,
autorenew_poll_message bytea,
delete_poll_message bytea,
fully_qualified_domain_name text,
idn_table_name text,
last_transfer_time timestamptz,
@@ -70,56 +62,11 @@
launch_notice_validator_id text,
registration_expiration_time timestamptz,
smd_id text,
subordinate_hosts text[],
tld text,
transfer_data_server_approve_autorenrew_event bytea,
transfer_data_server_approve_autorenrew_poll_message bytea,
transfer_data_server_approve_billing_event bytea,
unit int4,
value int4,
client_transaction_id text,
server_transaction_id text,
transfer_data_registration_expiration_time timestamptz,
gaining_client_id text,
losing_client_id text,
pending_transfer_expiration_time timestamptz,
transfer_request_time timestamptz,
transfer_status int4,
primary key (repo_id)
);
create table "Domain_DelegationSignerData" (
domain_base_repo_id text not null,
ds_data_key_tag int4 not null,
primary key (domain_base_repo_id, ds_data_key_tag)
);
create table "Domain_DesignatedContact" (
domain_base_repo_id text not null,
all_contacts_contact bytea not null,
primary key (domain_base_repo_id, all_contacts_contact)
);
create table "Domain_GracePeriod" (
domain_base_repo_id text not null,
grace_periods_id int8 not null,
primary key (domain_base_repo_id, grace_periods_id)
);
create table "DomainBase_nsHosts" (
domain_base_repo_id text not null,
ns_hosts bytea
);
create table "DomainBase_serverApproveEntities" (
domain_base_repo_id text not null,
transfer_data_server_approve_entities bytea
);
create table "DomainBase_subordinateHosts" (
domain_base_repo_id text not null,
subordinate_hosts text
);
create table "GracePeriod" (
id bigserial not null,
billing_event_one_time bytea,
@@ -177,15 +124,11 @@
should_publish boolean not null,
primary key (revision_id)
);
alter table if exists "Domain_DelegationSignerData"
add constraint UK_2yp55erx1i51pa7gnb8bu7tjn unique (ds_data_key_tag);
alter table if exists "Domain_DesignatedContact"
add constraint UK_4ys6wdxcmndimlr6af3tsl0ow unique (all_contacts_contact);
alter table if exists "Domain_GracePeriod"
add constraint UK_4ps2u4y8i5r91wu2n1x2xea28 unique (grace_periods_id);
create index IDX8nr0ke9mrrx4ewj6pd2ag4rmr on "Domain" (creation_time);
create index IDX8ffrqm27qtj20jac056j7yq07 on "Domain" (current_sponsor_client_id);
create index IDX5mnf0wn20tno4b9do88j61klr on "Domain" (deletion_time);
create index IDX1rcgkdd777bpvj0r94sltwd5y on "Domain" (fully_qualified_domain_name);
create index IDXrwl38wwkli1j7gkvtywi9jokq on "Domain" (tld);
create index premiumlist_name_idx on "PremiumList" (name);
create index idx_registry_lock_verification_code on "RegistryLock" (verification_code);
create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id);
@@ -199,51 +142,6 @@ create index reservedlist_name_idx on "ReservedList" (name);
foreign key (revision_id)
references "ClaimsList";
alter table if exists "Domain_DelegationSignerData"
add constraint FKho8wxowo3f4e688ehdl4wpni5
foreign key (ds_data_key_tag)
references "DelegationSignerData";
alter table if exists "Domain_DelegationSignerData"
add constraint FK2nvqbovvy5wasa8arhyhy8mge
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "Domain_DesignatedContact"
add constraint FKqnnsrj0vi9eqhoth305cd4bi7
foreign key (all_contacts_contact)
references "DesignatedContact";
alter table if exists "Domain_DesignatedContact"
add constraint FK169lte99hlt3otom9di13ubfn
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "Domain_GracePeriod"
add constraint FKny62h7k1nd3910rp56gdo5pfi
foreign key (grace_periods_id)
references "GracePeriod";
alter table if exists "Domain_GracePeriod"
add constraint FKkpor7amcdp7gwe0hp3obng6do
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "DomainBase_nsHosts"
add constraint FKow28763fcl1ilx8unxrfjtbja
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "DomainBase_serverApproveEntities"
add constraint FK7vuyqcsmcfvpv5648femoxien
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "DomainBase_subordinateHosts"
add constraint FKkva2lb57ri8qf39hthcej538k
foreign key (domain_base_repo_id)
references "Domain";
alter table if exists "PremiumEntry"
add constraint FKo0gw90lpo1tuee56l0nb6y6g5
foreign key (revision_id)
@@ -87,6 +87,35 @@ CREATE TABLE public."Cursor" (
);
--
-- Name: Domain; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."Domain" (
repo_id text NOT NULL,
creation_client_id text,
creation_time timestamp with time zone,
current_sponsor_client_id text,
deletion_time timestamp with time zone,
last_epp_update_client_id text,
last_epp_update_time timestamp with time zone,
statuses text[],
auth_info_repo_id text,
auth_info_value text,
fully_qualified_domain_name text,
idn_table_name text,
last_transfer_time timestamp with time zone,
launch_notice_accepted_time timestamp with time zone,
launch_notice_expiration_time timestamp with time zone,
launch_notice_tcn_id text,
launch_notice_validator_id text,
registration_expiration_time timestamp with time zone,
smd_id text,
subordinate_hosts text[],
tld text
);
--
-- Name: PremiumEntry; Type: TABLE; Schema: public; Owner: -
--
@@ -264,6 +293,14 @@ ALTER TABLE ONLY public."Cursor"
ADD CONSTRAINT "Cursor_pkey" PRIMARY KEY (scope, type);
--
-- Name: Domain Domain_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT "Domain_pkey" PRIMARY KEY (repo_id);
--
-- Name: PremiumEntry PremiumEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -312,6 +349,34 @@ ALTER TABLE ONLY public."RegistryLock"
ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id);
--
-- Name: idx1rcgkdd777bpvj0r94sltwd5y; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx1rcgkdd777bpvj0r94sltwd5y ON public."Domain" USING btree (fully_qualified_domain_name);
--
-- Name: idx5mnf0wn20tno4b9do88j61klr; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx5mnf0wn20tno4b9do88j61klr ON public."Domain" USING btree (deletion_time);
--
-- Name: idx8ffrqm27qtj20jac056j7yq07; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx8ffrqm27qtj20jac056j7yq07 ON public."Domain" USING btree (current_sponsor_client_id);
--
-- Name: idx8nr0ke9mrrx4ewj6pd2ag4rmr; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idx8nr0ke9mrrx4ewj6pd2ag4rmr ON public."Domain" USING btree (creation_time);
--
-- Name: idx_registry_lock_registrar_id; Type: INDEX; Schema: public; Owner: -
--
@@ -326,6 +391,13 @@ CREATE INDEX idx_registry_lock_registrar_id ON public."RegistryLock" USING btree
CREATE INDEX idx_registry_lock_verification_code ON public."RegistryLock" USING btree (verification_code);
--
-- Name: idxrwl38wwkli1j7gkvtywi9jokq; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX idxrwl38wwkli1j7gkvtywi9jokq ON public."Domain" USING btree (tld);
--
-- Name: premiumlist_name_idx; Type: INDEX; Schema: public; Owner: -
--