diff --git a/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java b/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java index 08163eefb..75db980c9 100644 --- a/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java +++ b/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java @@ -370,11 +370,10 @@ public class DeleteContactsAndHostsAction implements Runnable { : "it was transferred prior to deletion"); HistoryEntry historyEntry = - new HistoryEntry.Builder() + HistoryEntry.createBuilderForResource(resource) .setClientId(deletionRequest.requestingClientId()) .setModificationTime(now) .setType(getHistoryEntryType(resource, deleteAllowed)) - .setParent(deletionRequest.key()) .build(); PollMessage.OneTime pollMessage = @@ -409,7 +408,9 @@ public class DeleteContactsAndHostsAction implements Runnable { } else { resourceToSave = resource.asBuilder().removeStatusValue(PENDING_DELETE).build(); } - auditedOfy().save().entities(resourceToSave, historyEntry, pollMessage); + auditedOfy() + .save() + .entities(resourceToSave, historyEntry.asHistoryEntry(), pollMessage); return DeletionResult.create( deleteAllowed ? Type.DELETED : Type.NOT_DELETED, pollMessageText); } diff --git a/core/src/main/java/google/registry/batch/DeleteProberDataAction.java b/core/src/main/java/google/registry/batch/DeleteProberDataAction.java index 3eb9aba5b..79a859cc2 100644 --- a/core/src/main/java/google/registry/batch/DeleteProberDataAction.java +++ b/core/src/main/java/google/registry/batch/DeleteProberDataAction.java @@ -44,11 +44,11 @@ import google.registry.mapreduce.MapreduceRunner; import google.registry.mapreduce.inputs.EppResourceInputs; import google.registry.model.EppResourceUtils; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldType; -import google.registry.model.reporting.HistoryEntry; import google.registry.request.Action; import google.registry.request.Parameter; import google.registry.request.Response; @@ -253,9 +253,9 @@ public class DeleteProberDataAction implements Runnable { .setDeletionTime(tm().getTransactionTime()) .setStatusValues(null) .build(); - HistoryEntry historyEntry = - new HistoryEntry.Builder() - .setParent(domain) + DomainHistory historyEntry = + new DomainHistory.Builder() + .setDomain(domain) .setType(DOMAIN_DELETE) .setModificationTime(tm().getTransactionTime()) .setBySuperuser(true) @@ -263,11 +263,9 @@ public class DeleteProberDataAction implements Runnable { .setClientId(registryAdminClientId) .build(); // Note that we don't bother handling grace periods, billing events, pending - // transfers, - // poll messages, or auto-renews because these will all be hard-deleted the next - // time the - // mapreduce runs anyway. - auditedOfy().save().entities(deletedDomain, historyEntry); + // transfers, poll messages, or auto-renews because these will all be hard-deleted + // the next time the mapreduce runs anyway. + tm().putAll(deletedDomain, historyEntry); updateForeignKeyIndexDeletionTime(deletedDomain); dnsQueue.addDomainRefreshTask(deletedDomain.getDomainName()); }); diff --git a/core/src/main/java/google/registry/batch/ExpandRecurringBillingEventsAction.java b/core/src/main/java/google/registry/batch/ExpandRecurringBillingEventsAction.java index 33b942349..f1d2d632b 100644 --- a/core/src/main/java/google/registry/batch/ExpandRecurringBillingEventsAction.java +++ b/core/src/main/java/google/registry/batch/ExpandRecurringBillingEventsAction.java @@ -49,6 +49,7 @@ import google.registry.model.billing.BillingEvent.OneTime; import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.common.Cursor; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.domain.Period; import google.registry.model.registry.Registry; import google.registry.model.reporting.DomainTransactionRecord; @@ -188,12 +189,14 @@ public class ExpandRecurringBillingEventsAction implements Runnable { // an event persisted. for (DateTime billingTime : difference(billingTimes, existingBillingTimes)) { // Construct a new HistoryEntry that parents over the OneTime - HistoryEntry historyEntry = - new HistoryEntry.Builder() + DomainHistory historyEntry = + new DomainHistory.Builder() .setBySuperuser(false) .setClientId(recurring.getClientId()) .setModificationTime(tm().getTransactionTime()) - .setParent(domainKey) + // TODO (jianglai): modify this to use setDomain instead when + // converting this action to be SQL-aware. + .setDomainRepoId(domainKey.getName()) .setPeriod(Period.create(1, YEARS)) .setReason( "Domain autorenewal by ExpandRecurringBillingEventsAction") diff --git a/core/src/main/java/google/registry/flows/FlowModule.java b/core/src/main/java/google/registry/flows/FlowModule.java index 2905d26a5..bbabbfb80 100644 --- a/core/src/main/java/google/registry/flows/FlowModule.java +++ b/core/src/main/java/google/registry/flows/FlowModule.java @@ -213,50 +213,78 @@ public class FlowModule { return Strings.nullToEmpty(((Poll) eppInput.getCommandWrapper().getCommand()).getMessageId()); } + private static > + B makeHistoryEntryBuilder( + B builder, + Trid trid, + byte[] inputXmlBytes, + boolean isSuperuser, + String clientId, + EppInput eppInput) { + builder + .setTrid(trid) + .setXmlBytes(inputXmlBytes) + .setBySuperuser(isSuperuser) + .setClientId(clientId); + Optional metadataExtension = + eppInput.getSingleExtension(MetadataExtension.class); + metadataExtension.ifPresent( + extension -> + builder + .setReason(extension.getReason()) + .setRequestedByRegistrar(extension.getRequestedByRegistrar())); + return builder; + } + /** - * Provides a partially filled in {@link HistoryEntry} builder. + * Provides a partially filled in {@link ContactHistory.Builder} * *

This is not marked with {@link FlowScope} so that each retry gets a fresh one. Otherwise, * the fact that the builder is one-use would cause NPEs. */ @Provides - static HistoryEntry.Builder provideHistoryEntryBuilder( + static ContactHistory.Builder provideContactHistoryBuilder( Trid trid, @InputXml byte[] inputXmlBytes, @Superuser boolean isSuperuser, @ClientId String clientId, EppInput eppInput) { - HistoryEntry.Builder historyBuilder = - new HistoryEntry.Builder() - .setTrid(trid) - .setXmlBytes(inputXmlBytes) - .setBySuperuser(isSuperuser) - .setClientId(clientId); - Optional metadataExtension = - eppInput.getSingleExtension(MetadataExtension.class); - metadataExtension.ifPresent( - extension -> - historyBuilder - .setReason(extension.getReason()) - .setRequestedByRegistrar(extension.getRequestedByRegistrar())); - return historyBuilder; + return makeHistoryEntryBuilder( + new ContactHistory.Builder(), trid, inputXmlBytes, isSuperuser, clientId, eppInput); } + /** + * Provides a partially filled in {@link HostHistory.Builder} + * + *

This is not marked with {@link FlowScope} so that each retry gets a fresh one. Otherwise, + * the fact that the builder is one-use would cause NPEs. + */ @Provides - static ContactHistory.Builder provideContactHistoryBuilder( - HistoryEntry.Builder historyEntryBuilder) { - return new ContactHistory.Builder().copyFrom(historyEntryBuilder); + static HostHistory.Builder provideHostHistoryBuilder( + Trid trid, + @InputXml byte[] inputXmlBytes, + @Superuser boolean isSuperuser, + @ClientId String clientId, + EppInput eppInput) { + return makeHistoryEntryBuilder( + new HostHistory.Builder(), trid, inputXmlBytes, isSuperuser, clientId, eppInput); } + /** + * Provides a partially filled in {@link DomainHistory.Builder} + * + *

This is not marked with {@link FlowScope} so that each retry gets a fresh one. Otherwise, + * the fact that the builder is one-use would cause NPEs. + */ @Provides static DomainHistory.Builder provideDomainHistoryBuilder( - HistoryEntry.Builder historyEntryBuilder) { - return new DomainHistory.Builder().copyFrom(historyEntryBuilder); - } - - @Provides - static HostHistory.Builder provideHostHistoryBuilder(HistoryEntry.Builder historyEntryBuilder) { - return new HostHistory.Builder().copyFrom(historyEntryBuilder); + Trid trid, + @InputXml byte[] inputXmlBytes, + @Superuser boolean isSuperuser, + @ClientId String clientId, + EppInput eppInput) { + return makeHistoryEntryBuilder( + new DomainHistory.Builder(), trid, inputXmlBytes, isSuperuser, clientId, eppInput); } /** diff --git a/core/src/main/java/google/registry/model/contact/ContactHistory.java b/core/src/main/java/google/registry/model/contact/ContactHistory.java index 564681759..033a51329 100644 --- a/core/src/main/java/google/registry/model/contact/ContactHistory.java +++ b/core/src/main/java/google/registry/model/contact/ContactHistory.java @@ -41,6 +41,11 @@ import javax.persistence.PostLoad; *

In addition to the general history fields (e.g. action time, registrar ID) we also persist a * copy of the contact entity at this point in time. We persist a raw {@link ContactBase} so that * the foreign-keyed fields in that class can refer to this object. + * + *

This class is only marked as a Datastore entity subclass and registered with Objectify so that + * when building it its ID can be auto-populated by Objectify. It is converted to its superclass + * {@link HistoryEntry} when persisted to Datastore using {@link + * google.registry.persistence.transaction.TransactionManager}. */ @Entity @javax.persistence.Table( diff --git a/core/src/main/java/google/registry/model/domain/DomainHistory.java b/core/src/main/java/google/registry/model/domain/DomainHistory.java index 9bd3780a0..052a86669 100644 --- a/core/src/main/java/google/registry/model/domain/DomainHistory.java +++ b/core/src/main/java/google/registry/model/domain/DomainHistory.java @@ -21,7 +21,6 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.EntitySubclass; -import com.googlecode.objectify.annotation.Ignore; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainHistory.DomainHistoryId; import google.registry.model.domain.GracePeriod.GracePeriodHistory; @@ -62,6 +61,11 @@ import javax.persistence.Table; *

In addition to the general history fields (e.g. action time, registrar ID) we also persist a * copy of the domain entity at this point in time. We persist a raw {@link DomainContent} so that * the foreign-keyed fields in that class can refer to this object. + * + *

This class is only marked as a Datastore entity subclass and registered with Objectify so that + * when building it its ID can be auto-populated by Objectify. It is converted to its superclass + * {@link HistoryEntry} when persisted to Datastore using {@link + * google.registry.persistence.transaction.TransactionManager}. */ @Entity @Table( @@ -97,7 +101,6 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { // We could have reused domainContent.nsHosts here, but Hibernate throws a weird exception after // we change to use a composite primary key. // TODO(b/166776754): Investigate if we can reuse domainContent.nsHosts for storing host keys. - @Ignore @ElementCollection @JoinTable( name = "DomainHistoryHost", @@ -111,7 +114,6 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { @Column(name = "host_repo_id") Set> nsHosts; - @Ignore @OneToMany( cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, @@ -131,7 +133,6 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { // HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request Set dsDataHistories = new HashSet<>(); - @Ignore @OneToMany( cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, diff --git a/core/src/main/java/google/registry/model/host/HostHistory.java b/core/src/main/java/google/registry/model/host/HostHistory.java index fa71064b8..2526073b8 100644 --- a/core/src/main/java/google/registry/model/host/HostHistory.java +++ b/core/src/main/java/google/registry/model/host/HostHistory.java @@ -41,6 +41,11 @@ import javax.persistence.PostLoad; *

In addition to the general history fields (e.g. action time, registrar ID) we also persist a * copy of the host entity at this point in time. We persist a raw {@link HostBase} so that the * foreign-keyed fields in that class can refer to this object. + * + *

This class is only marked as a Datastore entity subclass and registered with Objectify so that + * when building it its ID can be auto-populated by Objectify. It is converted to its superclass + * {@link HistoryEntry} when persisted to Datastore using {@link + * google.registry.persistence.transaction.TransactionManager}. */ @Entity @javax.persistence.Table( diff --git a/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java b/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java index 861ffe3dd..11b935158 100644 --- a/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java +++ b/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java @@ -173,6 +173,11 @@ public class DatastoreTransactionManager implements TransactionManager { putAll(entities); } + @Override + public void updateAll(Object... entities) { + updateAll(ImmutableList.of(entities)); + } + @Override public void updateWithoutBackup(Object entity) { putWithoutBackup(entity); diff --git a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java index 080e5c90f..8d088912b 100644 --- a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java +++ b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java @@ -32,14 +32,17 @@ import google.registry.model.Buildable; import google.registry.model.EppResource; import google.registry.model.ImmutableObject; import google.registry.model.annotations.ReportedOn; +import google.registry.model.contact.ContactBase; import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactHistory.ContactHistoryId; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainContent; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory.DomainHistoryId; import google.registry.model.domain.Period; import google.registry.model.eppcommon.Trid; +import google.registry.model.host.HostBase; import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory.HostHistoryId; import google.registry.model.host.HostResource; @@ -60,7 +63,21 @@ import javax.persistence.MappedSuperclass; import javax.persistence.Transient; import org.joda.time.DateTime; -/** A record of an EPP command that mutated a resource. */ +/** + * A record of an EPP command that mutated a resource. + * + *

Due to historical reasons this class is persisted only to Datastore. It has three subclasses + * that include the parent resource itself which are persisted to Cloud SQL. During migration this + * class cannot be made abstract in order for the class to be persisted and loaded to and from + * Datastore. However it should never be used directly in the Java code itself. When it is loaded + * from Datastore it should be converted to a subclass for handling and when a new history entry is + * built it should always be a subclass, which is automatically converted to HistoryEntry when + * persisting to Datastore. + * + *

Some care has been taken to make it close to impossible to use this class directly, but the + * user should still exercise caution. After the migration is complete this class will be made + * abstract. + */ @ReportedOn @Entity @MappedSuperclass @@ -203,6 +220,12 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor @ImmutableObject.EmptySetToNull protected Set domainTransactionRecords; + // Make it impossible to instantiate a HistoryEntry explicitly. One should only instantiate a + // subtype of HistoryEntry. + protected HistoryEntry() { + super(); + } + public long getId() { // For some reason, Hibernate throws NPE during some initialization phase if we don't deal with // the null case. Setting the id to 0L when it is null should be fine because 0L for primitive @@ -285,13 +308,50 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords); } + /** + * Throws an error when trying to get a builder from a bare {@link HistoryEntry}. + * + *

This method only exists to satisfy the requirement that the {@link HistoryEntry} is NOT + * abstract, it should never be called directly and all three of the subclass of {@link + * HistoryEntry} implements it. + */ @Override - public Builder asBuilder() { - return new Builder(clone(this)); + public Builder asBuilder() { + throw new UnsupportedOperationException( + "You should never attempt to build a HistoryEntry from a raw HistoryEntry. A raw " + + "HistoryEntry should only exist internally when persisting to datastore. If you need " + + "to build from a raw HistoryEntry, use " + + "{Contact,Host,Domain}History.Builder.copyFrom(HistoryEntry) instead."); } + /** + * Clones and returns a {@code HistoryEntry} objec + * + *

This is useful when converting a subclass to the base class to persist to Datastore. + */ public HistoryEntry asHistoryEntry() { - return new Builder().copyFrom(this).build(); + HistoryEntry historyEntry = new HistoryEntry(); + copy(this, historyEntry); + return historyEntry; + } + + protected static void copy(HistoryEntry src, HistoryEntry dst) { + dst.id = src.id; + dst.parent = src.parent; + dst.type = src.type; + dst.period = src.period; + dst.xmlBytes = src.xmlBytes; + dst.modificationTime = src.modificationTime; + dst.clientId = src.clientId; + dst.otherClientId = src.otherClientId; + dst.trid = src.trid; + dst.bySuperuser = src.bySuperuser; + dst.reason = src.reason; + dst.requestedByRegistrar = src.requestedByRegistrar; + dst.domainTransactionRecords = + src.domainTransactionRecords == null + ? null + : ImmutableSet.copyOf(src.domainTransactionRecords); } @SuppressWarnings("unchecked") @@ -349,33 +409,18 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor } /** A builder for {@link HistoryEntry} since it is immutable */ - public static class Builder> + public abstract static class Builder> extends GenericBuilder { - public Builder() {} + protected Builder() {} - public Builder(T instance) { + protected Builder(T instance) { super(instance); } // Used to fill out the fields in this object from an object which may not be exactly the same // as the class T, where both classes still subclass HistoryEntry public B copyFrom(HistoryEntry historyEntry) { - setId(historyEntry.id); - setParent(historyEntry.parent); - setType(historyEntry.type); - setPeriod(historyEntry.period); - setXmlBytes(historyEntry.xmlBytes); - setModificationTime(historyEntry.modificationTime); - setClientId(historyEntry.clientId); - setOtherClientId(historyEntry.otherClientId); - setTrid(historyEntry.trid); - setBySuperuser(historyEntry.bySuperuser); - setReason(historyEntry.reason); - setRequestedByRegistrar(historyEntry.requestedByRegistrar); - setDomainTransactionRecords( - historyEntry.domainTransactionRecords == null - ? null - : ImmutableSet.copyOf(historyEntry.domainTransactionRecords)); + copy(historyEntry, getInstance()); return thisCastToDerived(); } @@ -400,13 +445,13 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor return thisCastToDerived(); } - public B setParent(EppResource parent) { + protected B setParent(EppResource parent) { getInstance().parent = Key.create(parent); return thisCastToDerived(); } // Until we move completely to SQL, override this in subclasses (e.g. HostHistory) to set VKeys - public B setParent(Key parent) { + protected B setParent(Key parent) { getInstance().parent = parent; return thisCastToDerived(); } @@ -467,4 +512,19 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor return thisCastToDerived(); } } + + public static + HistoryEntry.Builder createBuilderForResource(E parent) { + if (parent instanceof DomainContent) { + return new DomainHistory.Builder().setDomain((DomainContent) parent); + } else if (parent instanceof ContactBase) { + return new ContactHistory.Builder().setContact((ContactBase) parent); + } else if (parent instanceof HostBase) { + return new HostHistory.Builder().setHost((HostBase) parent); + } else { + throw new IllegalStateException( + String.format( + "Class %s does not have an associated HistoryEntry", parent.getClass().getName())); + } + } } diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java index 822c8b358..30b96fce0 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java @@ -350,6 +350,11 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager { entities.forEach(this::update); } + @Override + public void updateAll(Object... entities) { + updateAll(ImmutableList.of(entities)); + } + @Override public void updateWithoutBackup(Object entity) { update(entity); diff --git a/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java b/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java index 8eb97f73f..8a6f76cee 100644 --- a/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java +++ b/core/src/main/java/google/registry/persistence/transaction/TransactionManager.java @@ -159,6 +159,9 @@ public interface TransactionManager { /** Updates all entities in the database, throws exception if any entity does not exist. */ void updateAll(ImmutableCollection entities); + /** Updates all entities in the database, throws exception if any entity does not exist. */ + void updateAll(Object... entities); + /** * Updates an entity in the database without writing commit logs if the underlying database is * Datastore. diff --git a/core/src/main/java/google/registry/tools/DomainLockUtils.java b/core/src/main/java/google/registry/tools/DomainLockUtils.java index 81d289f84..5037b7396 100644 --- a/core/src/main/java/google/registry/tools/DomainLockUtils.java +++ b/core/src/main/java/google/registry/tools/DomainLockUtils.java @@ -22,7 +22,6 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import com.googlecode.objectify.Key; import google.registry.batch.AsyncTaskEnqueuer; import google.registry.config.RegistryConfig.Config; import google.registry.model.billing.BillingEvent; @@ -370,7 +369,7 @@ public final class DomainLockUtils { .setRequestedByRegistrar(!lock.isSuperuser()) .setType(HistoryEntry.Type.DOMAIN_UPDATE) .setModificationTime(now) - .setParent(Key.create(domain)) + .setDomain(domain) .setReason(reason) .build(); tm().update(domain); diff --git a/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java b/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java index 43497aaa5..207f32e98 100644 --- a/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UnrenewDomainCommand.java @@ -185,7 +185,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot leapSafeSubtractYears(domain.getRegistrationExpirationTime(), period); DomainHistory domainHistory = new DomainHistory.Builder() - .setParent(domain) + .setDomain(domain) .setModificationTime(now) .setBySuperuser(true) .setType(Type.SYNTHETIC) diff --git a/core/src/main/java/google/registry/tools/javascrap/CreateSyntheticHistoryEntriesAction.java b/core/src/main/java/google/registry/tools/javascrap/CreateSyntheticHistoryEntriesAction.java index be764669b..18a75225a 100644 --- a/core/src/main/java/google/registry/tools/javascrap/CreateSyntheticHistoryEntriesAction.java +++ b/core/src/main/java/google/registry/tools/javascrap/CreateSyntheticHistoryEntriesAction.java @@ -113,12 +113,11 @@ public class CreateSyntheticHistoryEntriesAction implements Runnable { () -> { EppResource eppResource = ofy().load().key(resourceKey).now(); tm().put( - new HistoryEntry.Builder<>() + HistoryEntry.createBuilderForResource(eppResource) .setClientId(registryAdminRegistrarId) .setBySuperuser(true) .setRequestedByRegistrar(false) .setModificationTime(tm().getTransactionTime()) - .setParent(eppResource) .setReason( "Backfill EppResource history objects during Cloud SQL migration") .setType(HistoryEntry.Type.SYNTHETIC) diff --git a/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java b/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java index e5e135561..8fdf6fcb3 100644 --- a/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteExpiredDomainsActionTest.java @@ -33,6 +33,7 @@ import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.ofy.Ofy; import google.registry.model.poll.PollMessage; import google.registry.model.reporting.HistoryEntry; @@ -166,9 +167,9 @@ class DeleteExpiredDomainsActionTest { DomainBase pendingExpirationDomain = persistActiveDomain(domainName); HistoryEntry createHistoryEntry = persistResource( - new HistoryEntry.Builder() + new DomainHistory.Builder() .setType(DOMAIN_CREATE) - .setParent(pendingExpirationDomain) + .setDomain(pendingExpirationDomain) .setModificationTime(clock.nowUtc().minusMonths(9)) .setClientId(pendingExpirationDomain.getCreationClientId()) .build()); diff --git a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java index 8c8207a88..0656f8116 100644 --- a/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteProberDataActionTest.java @@ -39,6 +39,7 @@ import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.index.EppResourceIndex; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.poll.PollMessage; @@ -281,8 +282,8 @@ class DeleteProberDataActionTest extends MapreduceTestCase historyEntryKey = Key.create( persistResource( - new HistoryEntry.Builder() - .setParent(domainKey) + new DomainHistory.Builder() + .setDomainRepoId(domainKey.getName()) .setType(HistoryEntry.Type.DOMAIN_CREATE) .setClientId("TheRegistrar") .setModificationTime(fakeClock.nowUtc().minusYears(1)) diff --git a/core/src/test/java/google/registry/beam/initsql/InitSqlPipelineTest.java b/core/src/test/java/google/registry/beam/initsql/InitSqlPipelineTest.java index 294638b20..7d578376f 100644 --- a/core/src/test/java/google/registry/beam/initsql/InitSqlPipelineTest.java +++ b/core/src/test/java/google/registry/beam/initsql/InitSqlPipelineTest.java @@ -38,6 +38,7 @@ import google.registry.model.contact.ContactResource; import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DomainAuthInfo; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.domain.GracePeriod; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.rgp.GracePeriodStatus; @@ -178,8 +179,8 @@ class InitSqlPipelineTest { .build()); historyEntry = persistResource( - new HistoryEntry.Builder() - .setParent(domainKey) + new DomainHistory.Builder() + .setDomainRepoId(domainKey.getName()) .setModificationTime(fakeClock.nowUtc()) .setClientId(registrar1.getClientId()) .setType(HistoryEntry.Type.DOMAIN_CREATE) diff --git a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java index a86ccda8d..e631b75ca 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java @@ -84,6 +84,7 @@ import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.domain.GracePeriod; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; @@ -121,7 +122,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase { // response data block is produced in the poll message. HistoryEntry historyEntry = persistResource( - new HistoryEntry.Builder() + new ContactHistory.Builder() .setClientId("NewRegistrar") .setModificationTime(clock.nowUtc().minusDays(1)) .setType(HistoryEntry.Type.CONTACT_DELETE) - .setParent(contact) + .setContact(contact) .build()); persistResource( new PollMessage.OneTime.Builder() @@ -233,11 +235,11 @@ class PollRequestFlowTest extends FlowTestCase { // response data block is produced in the poll message. HistoryEntry historyEntry = persistResource( - new HistoryEntry.Builder() + new HostHistory.Builder() .setClientId("NewRegistrar") .setModificationTime(clock.nowUtc().minusDays(1)) .setType(HistoryEntry.Type.HOST_DELETE) - .setParent(host) + .setHost(host) .build()); persistResource( new PollMessage.OneTime.Builder() 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 3c3dc160c..0e1957818 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java @@ -441,7 +441,6 @@ public class DomainBaseSqlTest { .setType(HistoryEntry.Type.DOMAIN_CREATE) .setPeriod(Period.create(1, Period.Unit.YEARS)) .setModificationTime(DateTime.now(UTC)) - .setParent(Key.create(DomainBase.class, "4-COM")) .setDomainRepoId("4-COM") .setClientId("registrar1") // These are non-null, but I don't think some tests set them. @@ -572,7 +571,6 @@ public class DomainBaseSqlTest { .setType(HistoryEntry.Type.DOMAIN_CREATE) .setPeriod(Period.create(1, Period.Unit.YEARS)) .setModificationTime(DateTime.now(UTC)) - .setParent(Key.create(DomainBase.class, "4-COM")) .setDomainRepoId("4-COM") .setClientId("registrar1") // These are non-null, but I don't think some tests set them. 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 0c8d934c2..681ca7152 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java @@ -100,8 +100,8 @@ public class DomainBaseTest extends EntityTestCase { historyEntryKey = Key.create( persistResource( - new HistoryEntry.Builder() - .setParent(domainKey.getOfyKey()) + new DomainHistory.Builder() + .setDomainRepoId(domainKey.getOfyKey().getName()) .setModificationTime(fakeClock.nowUtc()) .setType(HistoryEntry.Type.DOMAIN_CREATE) .setClientId("aregistrar") @@ -364,8 +364,8 @@ public class DomainBaseTest extends EntityTestCase { private void doExpiredTransferTest(DateTime oldExpirationTime) { HistoryEntry historyEntry = - new HistoryEntry.Builder() - .setParent(domain) + new DomainHistory.Builder() + .setDomain(domain) .setModificationTime(fakeClock.nowUtc()) .setClientId(domain.getCurrentSponsorClientId()) .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST) diff --git a/core/src/test/java/google/registry/model/history/LegacyHistoryObjectTest.java b/core/src/test/java/google/registry/model/history/LegacyHistoryObjectTest.java index 599856c67..3bc35b5d9 100644 --- a/core/src/test/java/google/registry/model/history/LegacyHistoryObjectTest.java +++ b/core/src/test/java/google/registry/model/history/LegacyHistoryObjectTest.java @@ -207,9 +207,8 @@ public class LegacyHistoryObjectTest extends EntityTestCase { .build(); } - private HistoryEntry.Builder historyEntryBuilderFor(EppResource parent) { - return new HistoryEntry.Builder() - .setParent(parent) + private HistoryEntry.Builder historyEntryBuilderFor(EppResource parent) { + return HistoryEntry.createBuilderForResource(parent) .setType(HistoryEntry.Type.DOMAIN_CREATE) .setXmlBytes("".getBytes(UTF_8)) .setModificationTime(fakeClock.nowUtc()) diff --git a/core/src/test/java/google/registry/model/ofy/OfyTest.java b/core/src/test/java/google/registry/model/ofy/OfyTest.java index d5d8e29a8..96fa1167f 100644 --- a/core/src/test/java/google/registry/model/ofy/OfyTest.java +++ b/core/src/test/java/google/registry/model/ofy/OfyTest.java @@ -17,6 +17,7 @@ package google.registry.model.ofy; import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService; import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; +import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects; import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.ofy.ObjectifyService.auditedOfy; import static google.registry.model.ofy.Ofy.getBaseEntityClassFromEntityOrKey; @@ -40,6 +41,7 @@ import com.googlecode.objectify.annotation.OnLoad; import com.googlecode.objectify.annotation.OnSave; import com.googlecode.objectify.annotation.Parent; import google.registry.model.ImmutableObject; +import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; import google.registry.model.eppcommon.Trid; @@ -70,11 +72,11 @@ public class OfyTest { void beforeEach() { createTld("tld"); someObject = - new HistoryEntry.Builder() + new ContactHistory.Builder() .setClientId("clientid") .setModificationTime(START_OF_TIME) .setType(HistoryEntry.Type.CONTACT_CREATE) - .setParent(persistActiveContact("parentContact")) + .setContact(persistActiveContact("parentContact")) .setTrid(Trid.create("client", "server")) .setXmlBytes("".getBytes(UTF_8)) .build(); @@ -99,7 +101,8 @@ public class OfyTest { @Test void testBackupGroupRootTimestampsMustIncreaseOnSave() { - doBackupGroupRootTimestampInversionTest(() -> auditedOfy().save().entity(someObject)); + doBackupGroupRootTimestampInversionTest( + () -> auditedOfy().save().entity(someObject.asHistoryEntry())); } @Test @@ -115,8 +118,8 @@ public class OfyTest { () -> tm().transact( () -> { - auditedOfy().save().entity(someObject); - auditedOfy().save().entity(someObject); + auditedOfy().save().entity(someObject.asHistoryEntry()); + auditedOfy().save().entity(someObject.asHistoryEntry()); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -143,7 +146,7 @@ public class OfyTest { () -> tm().transact( () -> { - auditedOfy().save().entity(someObject); + auditedOfy().save().entity(someObject.asHistoryEntry()); auditedOfy().delete().entity(someObject); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); @@ -158,7 +161,7 @@ public class OfyTest { tm().transact( () -> { auditedOfy().delete().entity(someObject); - auditedOfy().save().entity(someObject); + auditedOfy().save().entity(someObject.asHistoryEntry()); })); assertThat(thrown).hasMessageThat().contains("Multiple entries with same key"); } @@ -288,7 +291,7 @@ public class OfyTest { public Integer get() { // There will be something in the manifest now, but it won't be committed if // we throw. - auditedOfy().save().entity(someObject); + auditedOfy().save().entity(someObject.asHistoryEntry()); count++; if (count == 3) { return count; @@ -310,7 +313,7 @@ public class OfyTest { public Void get() { if (firstCallToVrun) { firstCallToVrun = false; - auditedOfy().save().entity(someObject); + auditedOfy().save().entity(someObject.asHistoryEntry()); return null; } fail("Shouldn't have retried."); @@ -409,24 +412,26 @@ public class OfyTest { @Test void test_doWithFreshSessionCache() { - auditedOfy().saveWithoutBackup().entity(someObject).now(); + auditedOfy().saveWithoutBackup().entity(someObject.asHistoryEntry()).now(); final HistoryEntry modifiedObject = someObject.asBuilder().setModificationTime(END_OF_TIME).build(); // Mutate the saved objected, bypassing the Objectify session cache. - getDatastoreService().put(auditedOfy().saveWithoutBackup().toEntity(modifiedObject)); + getDatastoreService() + .put(auditedOfy().saveWithoutBackup().toEntity(modifiedObject.asHistoryEntry())); // Normal loading should come from the session cache and shouldn't reflect the mutation. - assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject); + assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry()); // Loading inside doWithFreshSessionCache() should reflect the mutation. boolean ran = auditedOfy() .doWithFreshSessionCache( () -> { - assertThat(auditedOfy().load().entity(someObject).now()) - .isEqualTo(modifiedObject); + assertAboutImmutableObjects() + .that(auditedOfy().load().entity(someObject).now()) + .isEqualExceptFields(modifiedObject, "contactBase"); return true; }); assertThat(ran).isTrue(); // Test the normal loading again to verify that we've restored the original session unchanged. - assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject); + assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry()); } } diff --git a/core/src/test/java/google/registry/model/poll/PollMessageTest.java b/core/src/test/java/google/registry/model/poll/PollMessageTest.java index c86281bff..58662948c 100644 --- a/core/src/test/java/google/registry/model/poll/PollMessageTest.java +++ b/core/src/test/java/google/registry/model/poll/PollMessageTest.java @@ -26,6 +26,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import google.registry.model.EntityTestCase; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.domain.Period; import google.registry.model.eppcommon.Trid; import google.registry.model.reporting.HistoryEntry; @@ -56,8 +57,8 @@ public class PollMessageTest extends EntityTestCase { domain = persistResource(newDomainBase("foo.foobar", contact)); historyEntry = persistResource( - new HistoryEntry.Builder() - .setParent(domain) + new DomainHistory.Builder() + .setDomain(domain) .setType(HistoryEntry.Type.DOMAIN_CREATE) .setPeriod(Period.create(1, Period.Unit.YEARS)) .setXmlBytes("".getBytes(UTF_8)) diff --git a/core/src/test/java/google/registry/model/reporting/HistoryEntryTest.java b/core/src/test/java/google/registry/model/reporting/HistoryEntryTest.java index 39d1df8a7..b93e74d39 100644 --- a/core/src/test/java/google/registry/model/reporting/HistoryEntryTest.java +++ b/core/src/test/java/google/registry/model/reporting/HistoryEntryTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import google.registry.model.EntityTestCase; +import google.registry.model.contact.ContactHistory; import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.Period; @@ -96,7 +97,7 @@ class HistoryEntryTest extends EntityTestCase { assertThrows( IllegalArgumentException.class, () -> - new HistoryEntry.Builder() + new ContactHistory.Builder() .setId(5L) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) .setClientId("TheRegistrar") @@ -111,7 +112,7 @@ class HistoryEntryTest extends EntityTestCase { assertThrows( IllegalArgumentException.class, () -> - new HistoryEntry.Builder() + new ContactHistory.Builder() .setId(5L) .setType(HistoryEntry.Type.CONTACT_CREATE) .setClientId("TheRegistrar") @@ -126,7 +127,7 @@ class HistoryEntryTest extends EntityTestCase { assertThrows( IllegalArgumentException.class, () -> - new HistoryEntry.Builder() + new ContactHistory.Builder() .setId(5L) .setType(HistoryEntry.Type.CONTACT_CREATE) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) @@ -141,7 +142,7 @@ class HistoryEntryTest extends EntityTestCase { assertThrows( IllegalArgumentException.class, () -> - new HistoryEntry.Builder() + new ContactHistory.Builder() .setId(5L) .setType(HistoryEntry.Type.SYNTHETIC) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) diff --git a/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java b/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java index b06f5ed87..1f5adc222 100644 --- a/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java +++ b/core/src/test/java/google/registry/rdap/RdapDomainSearchActionTest.java @@ -1147,9 +1147,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase { tm().put(resource); tm().put( - new HistoryEntry.Builder() - .setParent(resource) + HistoryEntry.createBuilderForResource(resource) .setClientId(resource.getCreationClientId()) .setType(getHistoryEntryType(resource)) .setModificationTime(tm().getTransactionTime()) @@ -1158,10 +1158,9 @@ public class DatabaseHelper { public static HistoryEntry createHistoryEntryForEppResource( T parentResource) { return persistResource( - new HistoryEntry.Builder() + HistoryEntry.createBuilderForResource(parentResource) .setType(getHistoryEntryType(parentResource)) .setModificationTime(DateTime.now(DateTimeZone.UTC)) - .setParent(parentResource) .setClientId(parentResource.getPersistedCurrentSponsorClientId()) .build()); } diff --git a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java index 0588cd6e8..13f7edc80 100644 --- a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java +++ b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java @@ -385,18 +385,16 @@ public final class FullFieldsTestEntityHelper { Period period, String reason, DateTime modificationTime) { - HistoryEntry.Builder builder = - new HistoryEntry.Builder() - .setParent(resource) - .setType(type) - .setPeriod(period) - .setXmlBytes("".getBytes(UTF_8)) - .setModificationTime(modificationTime) - .setClientId(resource.getPersistedCurrentSponsorClientId()) - .setTrid(Trid.create("ABC-123", "server-trid")) - .setBySuperuser(false) - .setReason(reason) - .setRequestedByRegistrar(false); - return builder.build(); + return HistoryEntry.createBuilderForResource(resource) + .setType(type) + .setPeriod(period) + .setXmlBytes("".getBytes(UTF_8)) + .setModificationTime(modificationTime) + .setClientId(resource.getPersistedCurrentSponsorClientId()) + .setTrid(Trid.create("ABC-123", "server-trid")) + .setBySuperuser(false) + .setReason(reason) + .setRequestedByRegistrar(false) + .build(); } } diff --git a/core/src/test/java/google/registry/tools/DedupeOneTimeBillingEventIdsCommandTest.java b/core/src/test/java/google/registry/tools/DedupeOneTimeBillingEventIdsCommandTest.java index 440936ccb..5e8ea92ca 100644 --- a/core/src/test/java/google/registry/tools/DedupeOneTimeBillingEventIdsCommandTest.java +++ b/core/src/test/java/google/registry/tools/DedupeOneTimeBillingEventIdsCommandTest.java @@ -128,8 +128,7 @@ class DedupeOneTimeBillingEventIdsCommandTest private HistoryEntry persistHistoryEntry(EppResource parent) { return persistResource( - new HistoryEntry.Builder() - .setParent(parent) + HistoryEntry.createBuilderForResource(parent) .setType(HistoryEntry.Type.DOMAIN_CREATE) .setPeriod(Period.create(1, Period.Unit.YEARS)) .setXmlBytes("".getBytes(UTF_8)) diff --git a/core/src/test/java/google/registry/tools/javascrap/BackfillRegistryLocksCommandTest.java b/core/src/test/java/google/registry/tools/javascrap/BackfillRegistryLocksCommandTest.java index a22db4747..816094f34 100644 --- a/core/src/test/java/google/registry/tools/javascrap/BackfillRegistryLocksCommandTest.java +++ b/core/src/test/java/google/registry/tools/javascrap/BackfillRegistryLocksCommandTest.java @@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.truth.Truth8; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory; import google.registry.model.registrar.Registrar; import google.registry.model.reporting.HistoryEntry; import google.registry.schema.domain.RegistryLock; @@ -127,11 +128,11 @@ class BackfillRegistryLocksCommandTest extends CommandTestCase gracePeriodHistories; + java.util.Set dsDataHistories; java.util.Set domainTransactionRecords; + java.util.Set> nsHosts; org.joda.time.DateTime modificationTime; } class google.registry.model.domain.GracePeriod { @@ -298,6 +301,16 @@ class google.registry.model.domain.GracePeriod { long gracePeriodId; org.joda.time.DateTime expirationTime; } +class google.registry.model.domain.GracePeriod$GracePeriodHistory { + google.registry.model.domain.rgp.GracePeriodStatus type; + google.registry.persistence.BillingVKey$BillingEventVKey billingEventOneTime; + google.registry.persistence.BillingVKey$BillingRecurrenceVKey billingEventRecurring; + java.lang.Long domainHistoryRevisionId; + java.lang.Long gracePeriodHistoryRevisionId; + java.lang.String clientId; + long gracePeriodId; + org.joda.time.DateTime expirationTime; +} class google.registry.model.domain.Period { google.registry.model.domain.Period$Unit unit; java.lang.Integer value; @@ -330,6 +343,14 @@ class google.registry.model.domain.secdns.DelegationSignerData { int digestType; int keyTag; } +class google.registry.model.domain.secdns.DomainDsDataHistory { + byte[] digest; + int algorithm; + int digestType; + int keyTag; + java.lang.Long domainHistoryRevisionId; + java.lang.Long dsDataHistoryRevisionId; +} class google.registry.model.domain.token.AllocationToken { @Id java.lang.String token; boolean discountPremiums;