From 86bdd154bc5566c5a9e31a62ad4a631261f536c8 Mon Sep 17 00:00:00 2001 From: Michael Muller Date: Mon, 26 Oct 2020 13:38:14 -0400 Subject: [PATCH] Restore ofy keys in GracePeriod objects (#846) * Restore ofy keys in GracePeriod objects Restore the ofy keys when loading GracePeriod object from SQL. There's no clear way to do this using the normal approach (fix-up during a PostLoad method) because fixups to these violate immutability after hibernate has already obtained their hash values. Instead, we force reconstitution of the ofy keys in all public methods that access them (including equals() and hashCode()) so that they can be generated before an invalid hash is generated. As part of this change, convert the GracePeriod id from an autogenerated sequence to a UUID allocated from ObjectifyService and enhance ImmutableObject to allow it to exclude certain fields from hash/equals and print. The ImmutableObject enhancements are necessary because we compare grace periods against locally created test objects in a number of unit tests and there's no way this can work with GracePeriods loaded from SQL currently, as they will have an identifier field generated from the database and the test objects will have an identifier field of null (or a new unique value, after this change). Removing autogeneration from GracePeriod ids ended up being likely not strictly necessary for this change (it was a consequence of an earlier iteration). However, it does alleviate the problem of mutation of an immutable object after creation and is more in line with how we've decided to allocate other identifiers. * Changed needed after rebase. --- .../registry/model/ImmutableObject.java | 20 +- .../google/registry/model/ModelUtils.java | 2 +- .../registry/model/domain/DomainContent.java | 9 +- .../registry/model/domain/GracePeriod.java | 12 +- .../model/domain/GracePeriodBase.java | 78 +- .../flows/EppLifecycleDomainTest.java | 17 +- .../model/domain/DomainBaseSqlTest.java | 34 + .../registry/model/domain/DomainBaseTest.java | 60 + .../model/domain/GracePeriodTest.java | 34 +- .../registry/tools/EppLifecycleToolsTest.java | 4 +- .../poll_response_domain_transfer_request.xml | 2 +- ...e_domain_transfer_server_approve_loser.xml | 2 +- ..._domain_transfer_server_approve_winner.xml | 2 +- .../registry/flows/poll_response_unrenew.xml | 2 +- .../sql/er_diagram/brief_er_diagram.html | 628 +++-- .../sql/er_diagram/full_er_diagram.html | 2048 +++++++++-------- db/src/main/resources/sql/flyway.txt | 1 + .../flyway/V67__grace_period_history_ids.sql | 26 + .../sql/schema/db-schema.sql.generated | 4 +- .../resources/sql/schema/nomulus.golden.sql | 46 +- 20 files changed, 1625 insertions(+), 1406 deletions(-) create mode 100644 db/src/main/resources/sql/flyway/V67__grace_period_history_ids.sql diff --git a/core/src/main/java/google/registry/model/ImmutableObject.java b/core/src/main/java/google/registry/model/ImmutableObject.java index b17af27ea..31407dec7 100644 --- a/core/src/main/java/google/registry/model/ImmutableObject.java +++ b/core/src/main/java/google/registry/model/ImmutableObject.java @@ -61,7 +61,17 @@ public abstract class ImmutableObject implements Cloneable { private boolean equalsImmutableObject(ImmutableObject other) { return getClass().equals(other.getClass()) && hashCode() == other.hashCode() - && ModelUtils.getFieldValues(this).equals(ModelUtils.getFieldValues(other)); + && getSignificantFields().equals(other.getSignificantFields()); + } + + /** + * Returns the map of significant fields (fields that we care about for purposes of comparison and + * display). + * + *

Isolated into a method so that derived classes can override it. + */ + protected Map getSignificantFields() { + return ModelUtils.getFieldValues(this); } @Override @@ -72,7 +82,7 @@ public abstract class ImmutableObject implements Cloneable { @Override public int hashCode() { if (hashCode == null) { - hashCode = Arrays.hashCode(ModelUtils.getFieldValues(this).values().toArray()); + hashCode = Arrays.hashCode(getSignificantFields().values().toArray()); } return hashCode; } @@ -111,7 +121,7 @@ public abstract class ImmutableObject implements Cloneable { @Override public String toString() { NavigableMap sortedFields = new TreeMap<>(); - for (Entry entry : ModelUtils.getFieldValues(this).entrySet()) { + for (Entry entry : getSignificantFields().entrySet()) { sortedFields.put(entry.getKey().getName(), entry.getValue()); } return toStringHelper(sortedFields); @@ -121,7 +131,7 @@ public abstract class ImmutableObject implements Cloneable { public String toHydratedString() { // We can't use ImmutableSortedMap because we need to allow null values. NavigableMap sortedFields = new TreeMap<>(); - for (Entry entry : ModelUtils.getFieldValues(this).entrySet()) { + for (Entry entry : getSignificantFields().entrySet()) { Field field = entry.getKey(); Object value = entry.getValue(); sortedFields.put( @@ -161,7 +171,7 @@ public abstract class ImmutableObject implements Cloneable { // LinkedHashMap to preserve field ordering and because ImmutableMap forbids null // values. Map result = new LinkedHashMap<>(); - for (Entry entry : ModelUtils.getFieldValues(o).entrySet()) { + for (Entry entry : ((ImmutableObject) o).getSignificantFields().entrySet()) { Field field = entry.getKey(); if (!field.isAnnotationPresent(IgnoredInDiffableMap.class)) { result.put(field.getName(), toMapRecursive(entry.getValue())); diff --git a/core/src/main/java/google/registry/model/ModelUtils.java b/core/src/main/java/google/registry/model/ModelUtils.java index a69ceb577..14e62c0dc 100644 --- a/core/src/main/java/google/registry/model/ModelUtils.java +++ b/core/src/main/java/google/registry/model/ModelUtils.java @@ -194,7 +194,7 @@ public class ModelUtils { * returned map in its implementation of {@link ImmutableObject#toString} and {@link * ImmutableObject#equals}, which work by comparing and printing these maps. */ - static Map getFieldValues(Object instance) { + public static Map getFieldValues(Object instance) { // Don't make this ImmutableMap because field values can be null. Map values = new LinkedHashMap<>(); for (Field field : getAllFields(instance.getClass()).values()) { diff --git a/core/src/main/java/google/registry/model/domain/DomainContent.java b/core/src/main/java/google/registry/model/domain/DomainContent.java index db381600a..6fbb2d626 100644 --- a/core/src/main/java/google/registry/model/domain/DomainContent.java +++ b/core/src/main/java/google/registry/model/domain/DomainContent.java @@ -303,14 +303,13 @@ public class DomainContent extends EppResource allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet()); setContactFields(allContacts, true); - // We have to return the cloned object here because the original object's - // hashcode is not correct due to the change to its domainRepoId. The cloned - // object will have a null hashcode so that it can get a recalculated hashcode - // when its hashCode() is invoked. + // We have to return the cloned object here because the original object's hashcode is not + // correct due to the change to its domainRepoId and history ids. The cloned object will have a + // null hashcode so that it can get a recalculated hashcode when its hashCode() is invoked. // TODO(b/162739503): Remove this after fully migrating to Cloud SQL. gracePeriods = nullToEmptyImmutableCopy(gracePeriods).stream() - .map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId())) + .map(gracePeriod -> gracePeriod.cloneAfterOfyLoad(getRepoId())) .collect(toImmutableSet()); // Restore history record ids. diff --git a/core/src/main/java/google/registry/model/domain/GracePeriod.java b/core/src/main/java/google/registry/model/domain/GracePeriod.java index 9070aa157..dcc52e641 100644 --- a/core/src/main/java/google/registry/model/domain/GracePeriod.java +++ b/core/src/main/java/google/registry/model/domain/GracePeriod.java @@ -21,6 +21,7 @@ import com.googlecode.objectify.annotation.Embed; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.domain.rgp.GracePeriodStatus; +import google.registry.model.ofy.ObjectifyService; import google.registry.persistence.VKey; import google.registry.schema.replay.DatastoreAndSqlEntity; import javax.annotation.Nullable; @@ -53,12 +54,15 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit (billingEventRecurring != null) == GracePeriodStatus.AUTO_RENEW.equals(type), "Recurring billing events must be present on (and only on) autorenew grace periods"); GracePeriod instance = new GracePeriod(); + instance.id = ObjectifyService.allocateId(); instance.type = checkArgumentNotNull(type); instance.domainRepoId = checkArgumentNotNull(domainRepoId); instance.expirationTime = checkArgumentNotNull(expirationTime); instance.clientId = checkArgumentNotNull(clientId); instance.billingEventOneTime = billingEventOneTime; + instance.billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime); instance.billingEventRecurring = billingEventRecurring; + instance.billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring); return instance; } @@ -108,14 +112,16 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit } /** - * Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value. + * Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value + * and reconstructed history ids. * *

TODO(b/162739503): Remove this function after fully migrating to Cloud SQL. */ - public GracePeriod cloneWithDomainRepoId(String domainRepoId) { + public GracePeriod cloneAfterOfyLoad(String domainRepoId) { GracePeriod clone = clone(this); + clone.id = ObjectifyService.allocateId(); clone.domainRepoId = checkArgumentNotNull(domainRepoId); + clone.restoreHistoryIds(); return clone; } - } diff --git a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java index cca2db53b..64f1ede7f 100644 --- a/core/src/main/java/google/registry/model/domain/GracePeriodBase.java +++ b/core/src/main/java/google/registry/model/domain/GracePeriodBase.java @@ -14,18 +14,21 @@ package google.registry.model.domain; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Ignore; import google.registry.model.ImmutableObject; +import google.registry.model.ModelUtils; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.OneTime; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.persistence.VKey; +import java.lang.reflect.Field; +import java.util.LinkedHashMap; +import java.util.Map; import javax.persistence.Column; import javax.persistence.EnumType; import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; import javax.persistence.MappedSuperclass; import org.joda.time.DateTime; @@ -36,7 +39,6 @@ public class GracePeriodBase extends ImmutableObject { /** Unique id required for hibernate representation. */ @javax.persistence.Id - @GeneratedValue(strategy = GenerationType.IDENTITY) @Ignore Long id; @@ -67,6 +69,10 @@ public class GracePeriodBase extends ImmutableObject { @Column(name = "billing_event_id") VKey billingEventOneTime = null; + @Ignore + @Column(name = "billing_event_history_id") + Long billingEventOneTimeHistoryId; + /** * The recurring billing event corresponding to the action that triggered this grace period, if * applicable - i.e. if the action was an autorenew - or null in all other cases. @@ -75,6 +81,14 @@ public class GracePeriodBase extends ImmutableObject { @Column(name = "billing_recurrence_id") VKey billingEventRecurring = null; + @Ignore + @Column(name = "billing_recurrence_history_id") + Long billingEventRecurringHistoryId; + + public long getId() { + return id; + } + public GracePeriodStatus getType() { return type; } @@ -101,6 +115,7 @@ public class GracePeriodBase extends ImmutableObject { * period is not AUTO_RENEW. */ public VKey getOneTimeBillingEvent() { + restoreOfyKeys(); return billingEventOneTime; } @@ -109,6 +124,63 @@ public class GracePeriodBase extends ImmutableObject { * period is AUTO_RENEW. */ public VKey getRecurringBillingEvent() { + restoreOfyKeys(); return billingEventRecurring; } + + /** + * Restores history ids for composite VKeys after a load from datastore. + * + *

For use by DomainContent.load() ONLY. + */ + protected void restoreHistoryIds() { + billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime); + billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring); + } + + /** + * Override {@link ImmutableObject#getSignificantFields()} to exclude "id", which breaks equality + * testing in the unit tests. + */ + @Override + protected Map getSignificantFields() { + restoreOfyKeys(); + // Can't use streams or ImmutableMap because we can have null values. + Map result = new LinkedHashMap(); + for (Map.Entry entry : ModelUtils.getFieldValues(this).entrySet()) { + if (!entry.getKey().getName().equals("id")) { + result.put(entry.getKey(), entry.getValue()); + } + } + return result; + } + + /** + * Restores Ofy keys in the billing events. + * + *

This must be called by all methods that access the one time or recurring billing event keys. + * When the billing event keys are loaded from SQL, they are loaded as asymmetric keys because the + * database columns that we load them from do not contain all of the information necessary to + * reconsitute the Ofy side of the key. In other cases, we restore the Ofy key during the + * hibernate {@link javax.persistence.PostLoad} method from the other fields of the object, but we + * have been unable to make this work with hibernate's internal persistence model in this case + * because the {@link GracePeriod}'s hash code is evaluated prior to these calls, and would be + * invalidated by changing the fields. + */ + private final synchronized void restoreOfyKeys() { + if (billingEventOneTime != null && !billingEventOneTime.maybeGetOfyKey().isPresent()) { + billingEventOneTime = + DomainBase.restoreOfyFrom( + Key.create(DomainBase.class, domainRepoId), + billingEventOneTime, + billingEventOneTimeHistoryId); + } + if (billingEventRecurring != null && !billingEventRecurring.maybeGetOfyKey().isPresent()) { + billingEventRecurring = + DomainBase.restoreOfyFrom( + Key.create(DomainBase.class, domainRepoId), + billingEventRecurring, + billingEventRecurringHistoryId); + } + } } diff --git a/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java b/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java index 5641cdfdf..756833072 100644 --- a/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java +++ b/core/src/test/java/google/registry/flows/EppLifecycleDomainTest.java @@ -805,30 +805,35 @@ class EppLifecycleDomainTest extends EppTestCase { // As the losing registrar, read the request poll message, and then ack it. assertThatLoginSucceeds("NewRegistrar", "foo-BAR2"); + String messageId = "1-C-EXAMPLE-20-26-2001"; assertThatCommand("poll.xml") .atTime("2001-01-01T00:01:00Z") - .hasResponse("poll_response_domain_transfer_request.xml"); - assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-23-2001")) + .hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId)); + assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId)) .atTime("2001-01-01T00:01:00Z") .hasResponse("poll_ack_response_empty.xml"); // Five days in the future, expect a server approval poll message to the loser, and ack it. + messageId = "1-C-EXAMPLE-20-25-2001"; assertThatCommand("poll.xml") .atTime("2001-01-06T00:01:00Z") - .hasResponse("poll_response_domain_transfer_server_approve_loser.xml"); - assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-22-2001")) + .hasResponse( + "poll_response_domain_transfer_server_approve_loser.xml", + ImmutableMap.of("ID", messageId)); + assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId)) .atTime("2001-01-06T00:01:00Z") .hasResponse("poll_ack_response_empty.xml"); assertThatLogoutSucceeds(); // Also expect a server approval poll message to the winner, with the transfer request trid. + messageId = "1-C-EXAMPLE-20-24-2001"; assertThatLoginSucceeds("TheRegistrar", "password2"); assertThatCommand("poll.xml") .atTime("2001-01-06T00:02:00Z") .hasResponse( "poll_response_domain_transfer_server_approve_winner.xml", - ImmutableMap.of("SERVER_TRID", transferRequestTrid)); - assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-21-2001")) + ImmutableMap.of("SERVER_TRID", transferRequestTrid, "ID", messageId)); + assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId)) .atTime("2001-01-06T00:02:00Z") .hasResponse("poll_ack_response_empty.xml"); assertThatLogoutSucceeds(); diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java index 2eaa3248c..0ed048390 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java @@ -78,6 +78,7 @@ public class DomainBaseSqlTest { private HostResource host; private ContactResource contact; private ContactResource contact2; + private ImmutableSet gracePeriods; @BeforeEach void setUp() { @@ -506,6 +507,20 @@ public class DomainBaseSqlTest { .setServerApproveAutorenewEvent(billEvent.createVKey()) .setServerApproveAutorenewPollMessage(autorenewPollMessage.createVKey()) .build(); + gracePeriods = + ImmutableSet.of( + GracePeriod.create( + GracePeriodStatus.ADD, + "4-COM", + END_OF_TIME, + "registrar1", + oneTimeBillingEvent.createVKey()), + GracePeriod.createForRecurring( + GracePeriodStatus.AUTO_RENEW, + "4-COM", + END_OF_TIME, + "registrar1", + billEvent.createVKey())); jpaTm().insert(contact); jpaTm().insert(contact2); @@ -517,6 +532,7 @@ public class DomainBaseSqlTest { .setAutorenewPollMessage(autorenewPollMessage.createVKey()) .setDeletePollMessage(deletePollMessage.createVKey()) .setTransferData(transferData) + .setGracePeriods(gracePeriods) .build(); historyEntry = historyEntry.asBuilder().setDomainContent(domain).build(); jpaTm().insert(historyEntry); @@ -553,6 +569,7 @@ public class DomainBaseSqlTest { .isEqualTo(originalTransferData.getServerApproveAutorenewEvent()); assertThat(persistedTransferData.getServerApproveAutorenewPollMessage()) .isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage()); + assertThat(persisted.getGracePeriods()).isEqualTo(gracePeriods); } @Test @@ -624,6 +641,21 @@ public class DomainBaseSqlTest { createLegacyVKey( PollMessage.Autorenew.class, autorenewPollMessage.getId())) .build(); + gracePeriods = + ImmutableSet.of( + GracePeriod.create( + GracePeriodStatus.ADD, + "4-COM", + END_OF_TIME, + "registrar1", + createLegacyVKey( + BillingEvent.OneTime.class, oneTimeBillingEvent.getId())), + GracePeriod.createForRecurring( + GracePeriodStatus.AUTO_RENEW, + "4-COM", + END_OF_TIME, + "registrar1", + createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId()))); jpaTm().insert(contact); jpaTm().insert(contact2); @@ -639,6 +671,7 @@ public class DomainBaseSqlTest { .setDeletePollMessage( createLegacyVKey(PollMessage.OneTime.class, deletePollMessage.getId())) .setTransferData(transferData) + .setGracePeriods(gracePeriods) .build(); historyEntry = historyEntry.asBuilder().setDomainContent(domain).build(); jpaTm().insert(historyEntry); @@ -675,6 +708,7 @@ public class DomainBaseSqlTest { .isEqualTo(originalTransferData.getServerApproveAutorenewEvent()); assertThat(persistedTransferData.getServerApproveAutorenewPollMessage()) .isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage()); + assertThat(domain.getGracePeriods()).isEqualTo(gracePeriods); } private VKey createLegacyVKey(Class clazz, long id) { diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java index 03b473218..f93b720fc 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java @@ -38,6 +38,7 @@ import com.google.common.collect.Ordering; import com.google.common.collect.Streams; import com.googlecode.objectify.Key; import google.registry.model.EntityTestCase; +import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.contact.ContactResource; @@ -830,4 +831,63 @@ public class DomainBaseTest extends EntityTestCase { assertThat(getOnlyElement(clone.getGracePeriods()).getType()) .isEqualTo(GracePeriodStatus.TRANSFER); } + + @Test + void testHistoryIdRestoration() { + // Verify that history ids for billing events are restored during load from datastore. History + // ids are not used by business code or persisted in datastore, but only to reconstruct + // objectify keys when loading from SQL. + DateTime now = fakeClock.nowUtc(); + domain = + persistResource( + domain + .asBuilder() + .setRegistrationExpirationTime(now.plusYears(1)) + .setGracePeriods( + ImmutableSet.of( + GracePeriod.createForRecurring( + GracePeriodStatus.AUTO_RENEW, + domain.getRepoId(), + now.plusDays(1), + "NewRegistrar", + recurringBillKey), + GracePeriod.create( + GracePeriodStatus.RENEW, + domain.getRepoId(), + now.plusDays(1), + "NewRegistrar", + oneTimeBillKey))) + .build()); + ImmutableSet historyIds = + domain.getGracePeriods().stream() + .map( + gp -> + new BillEventInfo( + gp.getRecurringBillingEvent(), gp.billingEventRecurringHistoryId, + gp.getOneTimeBillingEvent(), gp.billingEventOneTimeHistoryId)) + .collect(toImmutableSet()); + assertThat(historyIds) + .isEqualTo( + ImmutableSet.of( + new BillEventInfo(null, null, oneTimeBillKey, historyEntryKey.getId()), + new BillEventInfo(recurringBillKey, historyEntryKey.getId(), null, null))); + } + + static class BillEventInfo extends ImmutableObject { + VKey billingEventRecurring; + Long billingEventRecurringHistoryId; + VKey billingEventOneTime; + Long billingEventOneTimeHistoryId; + + BillEventInfo( + VKey billingEventRecurring, + Long billingEventRecurringHistoryId, + VKey billingEventOneTime, + Long billingEventOneTimeHistoryId) { + this.billingEventRecurring = billingEventRecurring; + this.billingEventRecurringHistoryId = billingEventRecurringHistoryId; + this.billingEventOneTime = billingEventOneTime; + this.billingEventOneTimeHistoryId = billingEventOneTimeHistoryId; + } + } } diff --git a/core/src/test/java/google/registry/model/domain/GracePeriodTest.java b/core/src/test/java/google/registry/model/domain/GracePeriodTest.java index 3ae94f976..13dffc2be 100644 --- a/core/src/test/java/google/registry/model/domain/GracePeriodTest.java +++ b/core/src/test/java/google/registry/model/domain/GracePeriodTest.java @@ -44,6 +44,7 @@ public class GracePeriodTest { private final DateTime now = DateTime.now(UTC); private BillingEvent.OneTime onetime; + private VKey recurringKey; @BeforeEach void before() { @@ -59,6 +60,14 @@ public class GracePeriodTest { .setPeriodYears(1) .setTargetId("foo.google") .build(); + recurringKey = + VKey.create( + Recurring.class, + 12345, + Key.create( + Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L), + Recurring.class, + 12345)); } @Test @@ -71,6 +80,24 @@ public class GracePeriodTest { assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar"); assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1)); assertThat(gracePeriod.hasBillingEvent()).isTrue(); + assertThat(gracePeriod.billingEventOneTimeHistoryId).isEqualTo(12345L); + assertThat(gracePeriod.billingEventRecurringHistoryId).isNull(); + } + + @Test + void testSuccess_forRecurringEvent() { + GracePeriod gracePeriod = + GracePeriod.createForRecurring( + GracePeriodStatus.AUTO_RENEW, "1-TEST", now.plusDays(1), "TheRegistrar", recurringKey); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.AUTO_RENEW); + assertThat(gracePeriod.getDomainRepoId()).isEqualTo("1-TEST"); + assertThat(gracePeriod.getOneTimeBillingEvent()).isNull(); + assertThat(gracePeriod.getRecurringBillingEvent()).isEqualTo(recurringKey); + assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar"); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1)); + assertThat(gracePeriod.hasBillingEvent()).isTrue(); + assertThat(gracePeriod.billingEventOneTimeHistoryId).isNull(); + assertThat(gracePeriod.billingEventRecurringHistoryId).isEqualTo(343L); } @Test @@ -98,11 +125,6 @@ public class GracePeriodTest { @Test void testFailure_createForRecurring_notAutoRenew() { - Key recurringKey = - Key.create( - Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L), - Recurring.class, - 12345); IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, @@ -112,7 +134,7 @@ public class GracePeriodTest { "1-TEST", now.plusDays(1), "TheRegistrar", - VKey.create(Recurring.class, 12345, recurringKey))); + recurringKey)); assertThat(thrown).hasMessageThat().contains("autorenew"); } } diff --git a/core/src/test/java/google/registry/tools/EppLifecycleToolsTest.java b/core/src/test/java/google/registry/tools/EppLifecycleToolsTest.java index 3fa7b0cb4..ddfc50d2d 100644 --- a/core/src/test/java/google/registry/tools/EppLifecycleToolsTest.java +++ b/core/src/test/java/google/registry/tools/EppLifecycleToolsTest.java @@ -108,7 +108,7 @@ class EppLifecycleToolsTest extends EppTestCase { .atTime("2001-06-08T00:00:00Z") .hasResponse("poll_response_unrenew.xml"); - assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-8-TLD-17-18-2001")) + assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-8-TLD-23-24-2001")) .atTime("2001-06-08T00:00:01Z") .hasResponse("poll_ack_response_empty.xml"); @@ -129,7 +129,7 @@ class EppLifecycleToolsTest extends EppTestCase { .hasResponse( "poll_response_autorenew.xml", ImmutableMap.of( - "ID", "1-8-TLD-17-20-2003", + "ID", "1-8-TLD-23-26-2003", "QDATE", "2003-06-01T00:02:00Z", "DOMAIN", "example.tld", "EXDATE", "2004-06-01T00:02:00Z")); diff --git a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_request.xml b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_request.xml index 0f19e13b6..35ae1cf8c 100644 --- a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_request.xml +++ b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_request.xml @@ -3,7 +3,7 @@ Command completed successfully; ack to dequeue - + 2001-01-01T00:00:00Z Transfer requested. diff --git a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_loser.xml b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_loser.xml index a12a093f3..f85fbc526 100644 --- a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_loser.xml +++ b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_loser.xml @@ -3,7 +3,7 @@ Command completed successfully; ack to dequeue - + 2001-01-06T00:00:00Z Transfer approved. diff --git a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_winner.xml b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_winner.xml index bf4411bff..d9a089d88 100644 --- a/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_winner.xml +++ b/core/src/test/resources/google/registry/flows/poll_response_domain_transfer_server_approve_winner.xml @@ -4,7 +4,7 @@ Command completed successfully; ack to dequeue - + 2001-01-06T00:00:00Z Transfer approved. diff --git a/core/src/test/resources/google/registry/flows/poll_response_unrenew.xml b/core/src/test/resources/google/registry/flows/poll_response_unrenew.xml index 704fcc389..112faf222 100644 --- a/core/src/test/resources/google/registry/flows/poll_response_unrenew.xml +++ b/core/src/test/resources/google/registry/flows/poll_response_unrenew.xml @@ -3,7 +3,7 @@ Command completed successfully; ack to dequeue - + 2001-06-07T00:00:00Z Domain example.tld was unrenewed by 3 years; now expires at 2003-06-01T00:02:00.000Z. diff --git a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html index d26bf52e5..2e4b1fe7f 100644 --- a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html @@ -261,59 +261,59 @@ td.section { generated on - 2020-10-21 14:40:52.221127 + 2020-10-26 15:49:09.291097 last flyway file - V66__create_rde_revision.sql + V67__grace_period_history_ids.sql

 

 

- + SchemaCrawler_Diagram - - + + generated by - + SchemaCrawler 16.10.1 - + generated on - - 2020-10-21 14:40:52.221127 + + 2020-10-26 15:49:09.291097 - + allocationtoken_a08ccbef - - + + public.AllocationToken - - + + [table] - + token - + - + text not null - + domain_name - + - + text - + billingcancellation_6eedf614 @@ -1165,89 +1165,84 @@ td.section { graceperiod_cd3b2e8f - - + + public.GracePeriod - - + + [table] - + id - + - - bigserial not null + + int8 not null - - - - auto-incremented - - + billing_event_id - + - + int8 - + billing_recurrence_id - + - + int8 - + domain_repo_id - + - + text not null - + graceperiod_cd3b2e8f:w->domain_6c51cffa:e - - - - + + + + - - + + - fk2mys4hojm6ev2g9tmy5aq6m7g + fk_grace_period_domain_repo_id graceperiod_cd3b2e8f:w->billingevent_a57d1815:e - - - + + + - + fk_grace_period_billing_event_id graceperiod_cd3b2e8f:w->billingrecurrence_5fa2cb01:e - - - + + + - + fk_grace_period_billing_recurrence_id @@ -1266,67 +1261,67 @@ td.section { claimsentry_105da9f1 - - + + public.ClaimsEntry - - + + [table] - + revision_id - + - + int8 not null - + domain_label - + - + text not null - + claimslist_3d49bc2b - - + + public.ClaimsList - - + + [table] - + revision_id - - - - bigserial not null - + bigserial not null + + + + auto-incremented - + claimsentry_105da9f1:w->claimslist_3d49bc2b:e - - - - - - - - + + + + + + + + fk6sc6at5hedffc0nhdcab6ivuq @@ -1795,70 +1790,70 @@ td.section { cursor_6af40e8c - - + + public."Cursor" - - + + [table] - + "scope" - + - + text not null - + type - + - + text not null - + delegationsignerdata_e542a872 - - + + public.DelegationSignerData - - + + [table] - + domain_repo_id - + - + text not null - + key_tag - + - + int4 not null - + delegationsignerdata_e542a872:w->domain_6c51cffa:e - - - + + + - + fktr24j9v14ph2mfuw2gsmt12kq @@ -1890,50 +1885,50 @@ td.section { domainhost_1ea127c2 - - + + public.DomainHost - - + + [table] - + domain_repo_id - + - + text not null - + host_repo_id - + - + text - + domainhost_1ea127c2:w->domain_6c51cffa:e - - - - - - - - + + + + + + + + fkfmi7bdink53swivs390m2btxg domainhost_1ea127c2:w->host_f21b78de:e - - - + + + @@ -2104,415 +2099,415 @@ td.section { lock_f21d4861 - - + + public.Lock - - + + [table] - + resource_name - + - + text not null - + tld - + - + text not null - + premiumentry_b0060b91 - - + + public.PremiumEntry - - + + [table] - + revision_id - + - + int8 not null - + domain_label - + - + text not null - + premiumlist_7c3ea68b - - + + public.PremiumList - - + + [table] - + revision_id - - - - bigserial not null - - auto-incremented - - - name + bigserial not null + auto-incremented + + + name + + + + text not null - + premiumentry_b0060b91:w->premiumlist_7c3ea68b:e - - - - - - - - + + + + + + + + fko0gw90lpo1tuee56l0nb6y6g5 rderevision_83396864 - - + + public.RdeRevision - - + + [table] - + tld - + - + text not null - + mode - + - + text not null - + "date" - + - + date not null - + registrarpoc_ab47054d - - + + public.RegistrarPoc - - + + [table] - + email_address - + - + text not null - + gae_user_id - + - + text - + registrar_id - + - + text not null - + registrylock_ac88663e - - + + public.RegistryLock - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + registrar_id - + - + text not null - + repo_id - + - + text not null - + verification_code - + - + text not null - + relock_revision_id - + - + int8 - + registrylock_ac88663e:w->registrylock_ac88663e:e - - - - - - - - + + + + + + + + fk2lhcwpxlnqijr96irylrh1707 reservedentry_1a7b8520 - - + + public.ReservedEntry - - + + [table] - + revision_id - + - + int8 not null - + domain_label - + - + text not null - + reservedlist_b97c3f1c - - + + public.ReservedList - - + + [table] - + revision_id - - - - bigserial not null - - auto-incremented - - - name + bigserial not null + auto-incremented + + + name + + + + text not null - + reservedentry_1a7b8520:w->reservedlist_b97c3f1c:e - - - - - - - - + + + + + + + + fkgq03rk0bt1hb915dnyvd3vnfc spec11threatmatch_a61228a6 - - + + public.Spec11ThreatMatch - - + + [table] - + id - + - + bigserial not null - + - + auto-incremented - + check_date - + - + date not null - + registrar_id - + - + text not null - + tld - + - + text not null - + tld_f1fa57e2 - - + + public.Tld - - + + [table] - + tld_name - + - + text not null - + transaction_d50389d4 - - + + public.Transaction - - + + [table] - + id - + - + bigserial not null - + - + auto-incremented - + @@ -3713,7 +3708,7 @@ td.section { - fk2mys4hojm6ev2g9tmy5aq6m7g + fk_grace_period_domain_repo_id [foreign key, with no action] @@ -4135,12 +4130,7 @@ td.section { id - bigserial not null - - - - - auto-incremented + int8 not null @@ -4209,7 +4199,7 @@ td.section { - fk2mys4hojm6ev2g9tmy5aq6m7g + fk_grace_period_domain_repo_id [foreign key, with no action] diff --git a/db/src/main/resources/sql/er_diagram/full_er_diagram.html b/db/src/main/resources/sql/er_diagram/full_er_diagram.html index 29d12af1a..12a697db3 100644 --- a/db/src/main/resources/sql/er_diagram/full_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/full_er_diagram.html @@ -261,139 +261,139 @@ td.section { generated on - 2020-10-21 14:40:50.371645 + 2020-10-26 15:49:07.386754 last flyway file - V66__create_rde_revision.sql + V67__grace_period_history_ids.sql

 

 

- + SchemaCrawler_Diagram - - + + generated by - + SchemaCrawler 16.10.1 - + generated on - - 2020-10-21 14:40:50.371645 + + 2020-10-26 15:49:07.386754 - + allocationtoken_a08ccbef - - + + public.AllocationToken - - + + [table] - + token - + - + text not null - + update_timestamp - + - + timestamptz - + allowed_registrar_ids - + - + _text - + allowed_tlds - + - + _text - + creation_time - + - + timestamptz not null - + discount_fraction - + - + float8(17, 17) not null - + discount_premiums - + - + bool not null - + discount_years - + - + int4 not null - + domain_name - + - + text - + redemption_history_entry - + - + text - + token_status_transitions - + - + "hstore" - + token_type - + - + text - + billingcancellation_6eedf614 @@ -642,64 +642,72 @@ td.section { billingrecurrence_5fa2cb01 - - + + public.BillingRecurrence - - + + [table] - + billing_recurrence_id + + + + int8 not null + + + registrar_id + - int8 not null + text not null - registrar_id + domain_history_revision_id - text not null + int8 not null - domain_history_revision_id + domain_repo_id - int8 not null + text not null - domain_repo_id + event_time - text not null + timestamptz not null - event_time + flags - timestamptz not null + _text - flags + reason - _text + text not null - reason + domain_name @@ -707,96 +715,96 @@ td.section { text not null - domain_name + recurrence_end_time - text not null + timestamptz - recurrence_end_time + recurrence_time_of_year - timestamptz - - - recurrence_time_of_year - - - - text - + billingcancellation_6eedf614:w->billingrecurrence_5fa2cb01:e - + - - - - + + + + fk_billing_cancellation_billing_recurrence_id registrar_6e1503e3 - - + + public.Registrar - - + + [table] - + registrar_id + + + + text not null + + + allowed_tlds + - text not null + _text - allowed_tlds + billing_account_map - _text + "hstore" - billing_account_map + billing_identifier - "hstore" + int8 - billing_identifier + block_premium_names - int8 + bool not null - block_premium_names + client_certificate - bool not null + text - client_certificate + client_certificate_hash @@ -804,31 +812,31 @@ td.section { text - client_certificate_hash + contacts_require_syncing - text + bool not null - contacts_require_syncing + creation_time - bool not null + timestamptz - creation_time + drive_folder_id - timestamptz + text - drive_folder_id + email_address @@ -836,7 +844,7 @@ td.section { text - email_address + failover_client_certificate @@ -844,7 +852,7 @@ td.section { text - failover_client_certificate + failover_client_certificate_hash @@ -852,7 +860,7 @@ td.section { text - failover_client_certificate_hash + fax_number @@ -860,23 +868,23 @@ td.section { text - fax_number + iana_identifier - text + int8 - iana_identifier + icann_referral_email - int8 + text - icann_referral_email + i18n_address_city @@ -884,7 +892,7 @@ td.section { text - i18n_address_city + i18n_address_country_code @@ -892,7 +900,7 @@ td.section { text - i18n_address_country_code + i18n_address_state @@ -900,7 +908,7 @@ td.section { text - i18n_address_state + i18n_address_street_line1 @@ -908,7 +916,7 @@ td.section { text - i18n_address_street_line1 + i18n_address_street_line2 @@ -916,7 +924,7 @@ td.section { text - i18n_address_street_line2 + i18n_address_street_line3 @@ -924,7 +932,7 @@ td.section { text - i18n_address_street_line3 + i18n_address_zip @@ -932,23 +940,23 @@ td.section { text - i18n_address_zip + ip_address_allow_list - text + _text - ip_address_allow_list + last_certificate_update_time - _text + timestamptz - last_certificate_update_time + last_update_time @@ -956,15 +964,15 @@ td.section { timestamptz - last_update_time + localized_address_city - timestamptz + text - localized_address_city + localized_address_country_code @@ -972,7 +980,7 @@ td.section { text - localized_address_country_code + localized_address_state @@ -980,7 +988,7 @@ td.section { text - localized_address_state + localized_address_street_line1 @@ -988,7 +996,7 @@ td.section { text - localized_address_street_line1 + localized_address_street_line2 @@ -996,7 +1004,7 @@ td.section { text - localized_address_street_line2 + localized_address_street_line3 @@ -1004,7 +1012,7 @@ td.section { text - localized_address_street_line3 + localized_address_zip @@ -1012,7 +1020,7 @@ td.section { text - localized_address_zip + password_hash @@ -1020,7 +1028,7 @@ td.section { text - password_hash + phone_number @@ -1028,7 +1036,7 @@ td.section { text - phone_number + phone_passcode @@ -1036,7 +1044,7 @@ td.section { text - phone_passcode + po_number @@ -1044,39 +1052,39 @@ td.section { text - po_number + rdap_base_urls - text + _text - rdap_base_urls + registrar_name - _text + text not null - registrar_name + registry_lock_allowed - text not null + bool not null - registry_lock_allowed + password_salt - bool not null + text - password_salt + state @@ -1084,48 +1092,40 @@ td.section { text - state + type - text + text not null - type + url - text not null + text - url + whois_server text - - whois_server - - - - - text - - + billingcancellation_6eedf614:w->registrar_6e1503e3:e - + - - - - + + + + fk_billing_cancellation_registrar_id @@ -1586,106 +1586,114 @@ td.section { domain_6c51cffa:w->billingrecurrence_5fa2cb01:e - + - - - - - - + + + + + + fk_domain_billing_recurrence_id domain_6c51cffa:w->billingrecurrence_5fa2cb01:e - + - - - - + + + + fk_domain_transfer_billing_recurrence_id contact_8de8cb16 - - + + public.Contact - - + + [table] - + repo_id + + + + text not null + + + creation_registrar_id + text not null - creation_registrar_id + creation_time - text not null + timestamptz not null - creation_time + current_sponsor_registrar_id - timestamptz not null + text not null - current_sponsor_registrar_id + deletion_time - text not null + timestamptz - deletion_time + last_epp_update_registrar_id - timestamptz + text - last_epp_update_registrar_id + last_epp_update_time - text + timestamptz - last_epp_update_time + statuses - timestamptz + _text - statuses + auth_info_repo_id - _text + text - auth_info_repo_id + auth_info_value @@ -1693,7 +1701,7 @@ td.section { text - auth_info_value + contact_id @@ -1701,23 +1709,23 @@ td.section { text - contact_id + disclose_types_addr - text + _text - disclose_types_addr + disclose_show_email - _text + bool - disclose_show_email + disclose_show_fax @@ -1725,7 +1733,7 @@ td.section { bool - disclose_show_fax + disclose_mode_flag @@ -1733,15 +1741,15 @@ td.section { bool - disclose_mode_flag + disclose_types_name - bool + _text - disclose_types_name + disclose_types_org @@ -1749,23 +1757,23 @@ td.section { _text - disclose_types_org + disclose_show_voice - _text + bool - disclose_show_voice + email - bool + text - email + fax_phone_extension @@ -1773,7 +1781,7 @@ td.section { text - fax_phone_extension + fax_phone_number @@ -1781,7 +1789,7 @@ td.section { text - fax_phone_number + addr_i18n_city @@ -1789,7 +1797,7 @@ td.section { text - addr_i18n_city + addr_i18n_country_code @@ -1797,7 +1805,7 @@ td.section { text - addr_i18n_country_code + addr_i18n_state @@ -1805,7 +1813,7 @@ td.section { text - addr_i18n_state + addr_i18n_street_line1 @@ -1813,7 +1821,7 @@ td.section { text - addr_i18n_street_line1 + addr_i18n_street_line2 @@ -1821,7 +1829,7 @@ td.section { text - addr_i18n_street_line2 + addr_i18n_street_line3 @@ -1829,7 +1837,7 @@ td.section { text - addr_i18n_street_line3 + addr_i18n_zip @@ -1837,7 +1845,7 @@ td.section { text - addr_i18n_zip + addr_i18n_name @@ -1845,7 +1853,7 @@ td.section { text - addr_i18n_name + addr_i18n_org @@ -1853,7 +1861,7 @@ td.section { text - addr_i18n_org + addr_i18n_type @@ -1861,23 +1869,23 @@ td.section { text - addr_i18n_type + last_transfer_time - text + timestamptz - last_transfer_time + addr_local_city - timestamptz + text - addr_local_city + addr_local_country_code @@ -1885,7 +1893,7 @@ td.section { text - addr_local_country_code + addr_local_state @@ -1893,7 +1901,7 @@ td.section { text - addr_local_state + addr_local_street_line1 @@ -1901,7 +1909,7 @@ td.section { text - addr_local_street_line1 + addr_local_street_line2 @@ -1909,7 +1917,7 @@ td.section { text - addr_local_street_line2 + addr_local_street_line3 @@ -1917,7 +1925,7 @@ td.section { text - addr_local_street_line3 + addr_local_zip @@ -1925,7 +1933,7 @@ td.section { text - addr_local_zip + addr_local_name @@ -1933,7 +1941,7 @@ td.section { text - addr_local_name + addr_local_org @@ -1941,7 +1949,7 @@ td.section { text - addr_local_org + addr_local_type @@ -1949,7 +1957,7 @@ td.section { text - addr_local_type + search_name @@ -1957,7 +1965,7 @@ td.section { text - search_name + voice_phone_extension @@ -1965,7 +1973,7 @@ td.section { text - voice_phone_extension + voice_phone_number @@ -1973,15 +1981,15 @@ td.section { text - voice_phone_number + transfer_gaining_poll_message_id - text + int8 - transfer_gaining_poll_message_id + transfer_losing_poll_message_id @@ -1989,15 +1997,15 @@ td.section { int8 - transfer_losing_poll_message_id + transfer_client_txn_id - int8 + text - transfer_client_txn_id + transfer_server_txn_id @@ -2005,7 +2013,7 @@ td.section { text - transfer_server_txn_id + transfer_gaining_registrar_id @@ -2013,7 +2021,7 @@ td.section { text - transfer_gaining_registrar_id + transfer_losing_registrar_id @@ -2021,15 +2029,15 @@ td.section { text - transfer_losing_registrar_id + transfer_pending_expiration_time - text + timestamptz - transfer_pending_expiration_time + transfer_request_time @@ -2037,80 +2045,72 @@ td.section { timestamptz - transfer_request_time + transfer_status - timestamptz + text - transfer_status + update_timestamp - text - - - update_timestamp - - - - timestamptz - + domain_6c51cffa:w->contact_8de8cb16:e - - - - - - - - + + + + + + + + fk_domain_admin_contact domain_6c51cffa:w->contact_8de8cb16:e - - - - - - - - + + + + + + + + fk_domain_billing_contact domain_6c51cffa:w->contact_8de8cb16:e - - - - - - - - + + + + + + + + fk_domain_registrant_contact domain_6c51cffa:w->contact_8de8cb16:e - - - - - - - - + + + + + + + + fk_domain_tech_contact @@ -2362,372 +2362,383 @@ td.section { domain_6c51cffa:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk2jc69qyg2tv9hhnmif6oa1cx1 domain_6c51cffa:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk2u3srsfbei272093m3b3xwj23 domain_6c51cffa:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fkjc0r9r5y1lfbt4gpbqw4wsuvq domain_6c51cffa:w->registrar_6e1503e3:e - + - - - - - + + + + + fk_domain_transfer_gaining_registrar_id domain_6c51cffa:w->registrar_6e1503e3:e - + - - - - + + + + fk_domain_transfer_losing_registrar_id billingevent_a57d1815:w->billingrecurrence_5fa2cb01:e - + - - - - + + + + fk_billing_event_cancellation_matching_billing_recurrence_id billingevent_a57d1815:w->registrar_6e1503e3:e - + - - - - - + + + + + fk_billing_event_registrar_id graceperiod_cd3b2e8f - - + + public.GracePeriod - - + + [table] - + id - + - - bigserial not null + + int8 not null - - - - auto-incremented - - + billing_event_id - + - + int8 - + billing_recurrence_id - + - + int8 - + registrar_id - + - + text not null - + domain_repo_id - + - + text not null - + expiration_time - + - + timestamptz not null - + type - + - + text not null - + + billing_event_history_id + + + + + int8 + + + billing_recurrence_history_id + + + + + int8 + + graceperiod_cd3b2e8f:w->domain_6c51cffa:e - - - - - - - - - fk2mys4hojm6ev2g9tmy5aq6m7g + + + + + + + + + fk_grace_period_domain_repo_id graceperiod_cd3b2e8f:w->billingevent_a57d1815:e - - - - - - - - + + + + + + + + fk_grace_period_billing_event_id graceperiod_cd3b2e8f:w->billingrecurrence_5fa2cb01:e - - - - - - - - + + + + + + + + fk_grace_period_billing_recurrence_id billingrecurrence_5fa2cb01:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk_billing_recurrence_registrar_id claimsentry_105da9f1 - - + + public.ClaimsEntry - - + + [table] - + revision_id - + - + int8 not null - + claim_key - + - + text not null - + domain_label - + - + text not null - + claimslist_3d49bc2b - - + + public.ClaimsList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz not null - + tmdb_generation_time - + - + timestamptz not null - + claimsentry_105da9f1:w->claimslist_3d49bc2b:e - - - - - - - - + + + + + + + + fk6sc6at5hedffc0nhdcab6ivuq contact_8de8cb16:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk1sfyj7o7954prbn1exk7lpnoe contact_8de8cb16:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk93c185fx7chn68uv7nl6uv2s0 contact_8de8cb16:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fkmb7tdiv85863134w1wogtxrb2 contact_8de8cb16:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk_contact_transfer_gaining_registrar_id contact_8de8cb16:w->registrar_6e1503e3:e - - - - - - - - + + + + + + + + fk_contact_transfer_losing_registrar_id @@ -3265,27 +3276,27 @@ td.section { contacthistory_d2964f8a:w->contact_8de8cb16:e - + - - - - + + + + fk_contact_history_contact_repo_id contacthistory_d2964f8a:w->registrar_6e1503e3:e - + - - - - - - + + + + + + fk_contact_history_registrar_id @@ -3304,14 +3315,14 @@ td.section { pollmessage_614a523e:w->contact_8de8cb16:e - - - - - - - - + + + + + + + + fk_poll_message_contact_repo_id @@ -4025,14 +4036,14 @@ td.section { pollmessage_614a523e:w->host_f21b78de:e - - - + + + - + fk_poll_message_host_repo_id @@ -4242,175 +4253,175 @@ td.section { pollmessage_614a523e:w->hosthistory_56210c2:e - - - - - - - - + + + + + + + + fk_poll_message_host_history pollmessage_614a523e:w->hosthistory_56210c2:e - - - - - - - - + + + + + + + + fk_poll_message_host_history pollmessage_614a523e:w->registrar_6e1503e3:e - + - - - - + + + + fk_poll_message_registrar_id pollmessage_614a523e:w->registrar_6e1503e3:e - - - - - - - + + + + + + + fk_poll_message_transfer_response_gaining_registrar_id pollmessage_614a523e:w->registrar_6e1503e3:e - - - - - - - + + + + + + + fk_poll_message_transfer_response_losing_registrar_id cursor_6af40e8c - - + + public."Cursor" - - + + [table] - + "scope" - + - + text not null - + type - + - + text not null - + cursor_time - + - + timestamptz not null - + last_update_time - + - + timestamptz not null - + delegationsignerdata_e542a872 - - + + public.DelegationSignerData - - + + [table] - + domain_repo_id - + - + text not null - + key_tag - + - + int4 not null - + algorithm - + - + int4 not null - + digest - + - + bytea not null - + digest_type - + - + int4 not null - + delegationsignerdata_e542a872:w->domain_6c51cffa:e - - - + + + - + fktr24j9v14ph2mfuw2gsmt12kq @@ -4429,63 +4440,63 @@ td.section { domainhistory_a54cc226:w->registrar_6e1503e3:e - - + + - - - - - + + + + + fk_domain_history_registrar_id domainhost_1ea127c2 - - + + public.DomainHost - - + + [table] - + domain_repo_id - + - + text not null - + host_repo_id - + - + text - + domainhost_1ea127c2:w->domain_6c51cffa:e - - - - + + + + - + - + fkfmi7bdink53swivs390m2btxg domainhost_1ea127c2:w->host_f21b78de:e - - - + + + @@ -4683,1020 +4694,1020 @@ td.section { hosthistory_56210c2:w->registrar_6e1503e3:e - + - - - - + + + + fk3d09knnmxrt6iniwnp8j2ykga lock_f21d4861 - - + + public.Lock - - + + [table] - + resource_name - + - + text not null - + tld - + - + text not null - + acquired_time - + - + timestamptz not null - + expiration_time - + - + timestamptz not null - + request_log_id - + - + text not null - + premiumentry_b0060b91 - - + + public.PremiumEntry - - + + [table] - + revision_id - + - + int8 not null - + price - + - + numeric(19, 2) not null - + domain_label - + - + text not null - + premiumlist_7c3ea68b - - + + public.PremiumList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz not null - + name - + - + text not null - + bloom_filter - + - + bytea not null - + currency - + - + text not null - + premiumentry_b0060b91:w->premiumlist_7c3ea68b:e - - - - - - - - + + + + + + + + fko0gw90lpo1tuee56l0nb6y6g5 rderevision_83396864 - - + + public.RdeRevision - - + + [table] - + tld - + - + text not null - + mode - + - + text not null - + "date" - + - + date not null - + update_timestamp - + - + timestamptz - + revision - + - + int4 not null - + registrarpoc_ab47054d - - + + public.RegistrarPoc - - + + [table] - + email_address - + - + text not null - + allowed_to_set_registry_lock_password - + - + bool not null - + fax_number - + - + text - + gae_user_id - + - + text - + name - + - + text - + phone_number - + - + text - + registry_lock_password_hash - + - + text - + registry_lock_password_salt - + - + text - + types - + - + _text - + visible_in_domain_whois_as_abuse - + - + bool not null - + visible_in_whois_as_admin - + - + bool not null - + visible_in_whois_as_tech - + - + bool not null - + registry_lock_email_address - + - + text - + registrar_id - + - + text not null - + registrylock_ac88663e - - + + public.RegistryLock - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + lock_completion_timestamp - + - + timestamptz - + lock_request_timestamp - + - + timestamptz not null - + domain_name - + - + text not null - + is_superuser - + - + bool not null - + registrar_id - + - + text not null - + registrar_poc_id - + - + text - + repo_id - + - + text not null - + verification_code - + - + text not null - + unlock_request_timestamp - + - + timestamptz - + unlock_completion_timestamp - + - + timestamptz - + last_update_timestamp - + - + timestamptz - + relock_revision_id - + - + int8 - + relock_duration - + - + interval - + registrylock_ac88663e:w->registrylock_ac88663e:e - - - - - - - - + + + + + + + + fk2lhcwpxlnqijr96irylrh1707 reservedentry_1a7b8520 - - + + public.ReservedEntry - - + + [table] - + revision_id - + - + int8 not null - + comment - + - + text - + reservation_type - + - + int4 not null - + domain_label - + - + text not null - + reservedlist_b97c3f1c - - + + public.ReservedList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz not null - + name - + - + text not null - + should_publish - + - + bool not null - + reservedentry_1a7b8520:w->reservedlist_b97c3f1c:e - - - - - - - - + + + + + + + + fkgq03rk0bt1hb915dnyvd3vnfc spec11threatmatch_a61228a6 - - + + public.Spec11ThreatMatch - - + + [table] - + id - + - + bigserial not null - + - + auto-incremented - + check_date - + - + date not null - + domain_name - + - + text not null - + domain_repo_id - + - + text not null - + registrar_id - + - + text not null - + threat_types - + - + _text not null - + tld - + - + text not null - + tld_f1fa57e2 - - + + public.Tld - - + + [table] - + tld_name - + - + text not null - + add_grace_period_length - + - + interval not null - + allowed_fully_qualified_host_names - + - + _text - + allowed_registrant_contact_ids - + - + _text - + anchor_tenant_add_grace_period_length - + - + interval not null - + auto_renew_grace_period_length - + - + interval not null - + automatic_transfer_length - + - + interval not null - + claims_period_end - + - + timestamptz not null - + create_billing_cost_amount - + - + numeric(19, 2) - + create_billing_cost_currency - + - + text - + creation_time - + - + timestamptz not null - + currency - + - + text not null - + dns_paused - + - + bool not null - + dns_writers - + - + _text not null - + drive_folder_id - + - + text - + eap_fee_schedule - + - + "hstore" not null - + escrow_enabled - + - + bool not null - + invoicing_enabled - + - + bool not null - + lordn_username - + - + text - + num_dns_publish_locks - + - + int4 not null - + pending_delete_length - + - + interval not null - + premium_list_name - + - + text - + pricing_engine_class_name - + - + text - + redemption_grace_period_length - + - + interval not null - + registry_lock_or_unlock_cost_amount - + - + numeric(19, 2) - + registry_lock_or_unlock_cost_currency - + - + text - + renew_billing_cost_transitions - + - + "hstore" not null - + renew_grace_period_length - + - + interval not null - + reserved_list_names - + - + _text not null - + restore_billing_cost_amount - + - + numeric(19, 2) - + restore_billing_cost_currency - + - + text - + roid_suffix - + - + text - + server_status_change_billing_cost_amount - + - + numeric(19, 2) - + server_status_change_billing_cost_currency - + - + text - + tld_state_transitions - + - + "hstore" not null - + tld_type - + - + text not null - + tld_unicode - + - + text not null - + transfer_grace_period_length - + - + interval not null - + transaction_d50389d4 - - + + public.Transaction - - + + [table] - + id - + - + bigserial not null - + - + auto-incremented - + contents - + - + bytea - + @@ -8228,7 +8239,7 @@ td.section { - fk2mys4hojm6ev2g9tmy5aq6m7g + fk_grace_period_domain_repo_id [foreign key, with no action] @@ -9149,12 +9160,7 @@ td.section { id - bigserial not null - - - - - auto-incremented + int8 not null @@ -9186,6 +9192,16 @@ td.section { type text not null + + + billing_event_history_id + int8 + + + + billing_recurrence_history_id + int8 + @@ -9238,7 +9254,7 @@ td.section { - fk2mys4hojm6ev2g9tmy5aq6m7g + fk_grace_period_domain_repo_id [foreign key, with no action] diff --git a/db/src/main/resources/sql/flyway.txt b/db/src/main/resources/sql/flyway.txt index db47392ef..290df0ff6 100644 --- a/db/src/main/resources/sql/flyway.txt +++ b/db/src/main/resources/sql/flyway.txt @@ -64,3 +64,4 @@ V63__add_schema_for_ds_data.sql V64__transfer_history_columns.sql V65__local_date_date_type.sql V66__create_rde_revision.sql +V67__grace_period_history_ids.sql diff --git a/db/src/main/resources/sql/flyway/V67__grace_period_history_ids.sql b/db/src/main/resources/sql/flyway/V67__grace_period_history_ids.sql new file mode 100644 index 000000000..4f0a583ca --- /dev/null +++ b/db/src/main/resources/sql/flyway/V67__grace_period_history_ids.sql @@ -0,0 +1,26 @@ +-- 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. + +ALTER TABLE "GracePeriod" ADD COLUMN billing_event_history_id int8; +ALTER TABLE "GracePeriod" ADD COLUMN billing_recurrence_history_id int8; + +ALTER TABLE ONLY public."GracePeriod" + DROP CONSTRAINT fk2mys4hojm6ev2g9tmy5aq6m7g; +ALTER TABLE ONLY public."GracePeriod" + ADD CONSTRAINT fk_grace_period_domain_repo_id + FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) + DEFERRABLE INITIALLY DEFERRED; + +ALTER TABLE "GracePeriod" ALTER COLUMN id drop default; +DROP SEQUENCE "GracePeriod_id_seq"; diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 6e7a72f18..d46cd81f5 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -383,9 +383,11 @@ ); create table "GracePeriod" ( - id bigserial not null, + id int8 not null, billing_event_id int8, + billing_event_history_id int8, billing_recurrence_id int8, + billing_recurrence_history_id int8, registrar_id text not null, domain_repo_id text not null, expiration_time timestamptz not null, diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql index 0298bc968..aa4def939 100644 --- a/db/src/main/resources/sql/schema/nomulus.golden.sql +++ b/db/src/main/resources/sql/schema/nomulus.golden.sql @@ -515,29 +515,12 @@ CREATE TABLE public."GracePeriod" ( registrar_id text NOT NULL, domain_repo_id text NOT NULL, expiration_time timestamp with time zone NOT NULL, - type text NOT NULL + type text NOT NULL, + billing_event_history_id bigint, + billing_recurrence_history_id bigint ); --- --- Name: GracePeriod_id_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public."GracePeriod_id_seq" - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- Name: GracePeriod_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - --- - -ALTER SEQUENCE public."GracePeriod_id_seq" OWNED BY public."GracePeriod".id; - - -- -- Name: Host; Type: TABLE; Schema: public; Owner: - -- @@ -977,13 +960,6 @@ ALTER TABLE ONLY public."ClaimsList" ALTER COLUMN revision_id SET DEFAULT nextva ALTER TABLE ONLY public."DomainTransactionRecord" ALTER COLUMN id SET DEFAULT nextval('public."DomainTransactionRecord_id_seq"'::regclass); --- --- Name: GracePeriod id; Type: DEFAULT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public."GracePeriod" ALTER COLUMN id SET DEFAULT nextval('public."GracePeriod_id_seq"'::regclass); - - -- -- Name: PremiumList revision_id; Type: DEFAULT; Schema: public; Owner: - -- @@ -1640,14 +1616,6 @@ ALTER TABLE ONLY public."RegistryLock" ADD CONSTRAINT fk2lhcwpxlnqijr96irylrh1707 FOREIGN KEY (relock_revision_id) REFERENCES public."RegistryLock"(revision_id); --- --- Name: GracePeriod fk2mys4hojm6ev2g9tmy5aq6m7g; Type: FK CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public."GracePeriod" - ADD CONSTRAINT fk2mys4hojm6ev2g9tmy5aq6m7g FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id); - - -- -- Name: Domain fk2u3srsfbei272093m3b3xwj23; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -1896,6 +1864,14 @@ ALTER TABLE ONLY public."GracePeriod" ADD CONSTRAINT fk_grace_period_billing_recurrence_id FOREIGN KEY (billing_recurrence_id) REFERENCES public."BillingRecurrence"(billing_recurrence_id); +-- +-- Name: GracePeriod fk_grace_period_domain_repo_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."GracePeriod" + ADD CONSTRAINT fk_grace_period_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED; + + -- -- Name: Host fk_host_superordinate_domain; Type: FK CONSTRAINT; Schema: public; Owner: - --