Begin saving the EppResource parent in *History objects (#1090)

* Begin saving the EppResource parent in *History objects

We use DomainCreateFlow as an example here of how this will work. There
were a few changes necessary:

- various changes around GracePeriod / GracePeriodHistory so that we can
actually store them without throwing NPEs
- Creating one injectable *History.Builder field and using in place of
the HistoryEntry.Builder injected field in DomainCreateFlow
- Saving the EppResource as the parent in the *History.Builder setParent
calls
- Converting to/from HistoryEntry/*History classes in
DatastoreTransactionManager. Basically, we'll want to return the
*History subclasses (and similar in the ofy portions of HistoryEntryDao)
- Converting a few HistoryEntry.Builder usages to DomainHistory.Builder
usages. Eventually we should convert all of them.
This commit is contained in:
gbrodman 2021-04-22 15:03:37 -04:00 committed by GitHub
parent 40db04db8d
commit 9f69a0bf2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 202 additions and 135 deletions

View file

@ -20,6 +20,7 @@ import com.google.common.base.Strings;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.flows.picker.FlowPicker; import google.registry.flows.picker.FlowPicker;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
@ -239,6 +240,12 @@ public class FlowModule {
return historyBuilder; return historyBuilder;
} }
@Provides
static DomainHistory.Builder provideDomainHistoryBuilder(
HistoryEntry.Builder historyEntryBuilder) {
return new DomainHistory.Builder().copyFrom(historyEntryBuilder.build());
}
/** /**
* Provides a partially filled in {@link EppResponse} builder. * Provides a partially filled in {@link EppResponse} builder.
* *

View file

@ -80,6 +80,7 @@ import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.DomainCommand.Create; import google.registry.model.domain.DomainCommand.Create;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeCreateCommandExtension;
@ -206,7 +207,7 @@ public class DomainCreateFlow implements TransactionalFlow {
@Inject @ClientId String clientId; @Inject @ClientId String clientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject HistoryEntry.Builder historyBuilder; @Inject DomainHistory.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject AllocationTokenFlowUtils allocationTokenFlowUtils; @Inject AllocationTokenFlowUtils allocationTokenFlowUtils;
@Inject DomainCreateFlowCustomLogic flowCustomLogic; @Inject DomainCreateFlowCustomLogic flowCustomLogic;
@ -303,8 +304,8 @@ public class DomainCreateFlow implements TransactionalFlow {
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr()); String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr());
DateTime registrationExpirationTime = leapSafeAddYears(now, years); DateTime registrationExpirationTime = leapSafeAddYears(now, years);
HistoryEntry historyEntry = buildHistoryEntry( DomainHistory domainHistory =
repoId, registry, now, period, registry.getAddGracePeriodLength()); buildHistoryEntry(repoId, registry, now, period, registry.getAddGracePeriodLength());
// Bill for the create. // Bill for the create.
BillingEvent.OneTime createBillingEvent = BillingEvent.OneTime createBillingEvent =
createOneTimeBillingEvent( createOneTimeBillingEvent(
@ -314,20 +315,16 @@ public class DomainCreateFlow implements TransactionalFlow {
isReserved(domainName, isSunriseCreate), isReserved(domainName, isSunriseCreate),
years, years,
feesAndCredits, feesAndCredits,
historyEntry, domainHistory,
allocationToken, allocationToken,
now); now);
// Create a new autorenew billing event and poll message starting at the expiration time. // Create a new autorenew billing event and poll message starting at the expiration time.
BillingEvent.Recurring autorenewBillingEvent = BillingEvent.Recurring autorenewBillingEvent =
createAutorenewBillingEvent(historyEntry, registrationExpirationTime); createAutorenewBillingEvent(domainHistory, registrationExpirationTime);
PollMessage.Autorenew autorenewPollMessage = PollMessage.Autorenew autorenewPollMessage =
createAutorenewPollMessage(historyEntry, registrationExpirationTime); createAutorenewPollMessage(domainHistory, registrationExpirationTime);
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
entitiesToSave.add( entitiesToSave.add(createBillingEvent, autorenewBillingEvent, autorenewPollMessage);
historyEntry,
createBillingEvent,
autorenewBillingEvent,
autorenewPollMessage);
// Bill for EAP cost, if any. // Bill for EAP cost, if any.
if (!feesAndCredits.getEapCost().isZero()) { if (!feesAndCredits.getEapCost().isZero()) {
entitiesToSave.add(createEapBillingEvent(feesAndCredits, createBillingEvent)); entitiesToSave.add(createEapBillingEvent(feesAndCredits, createBillingEvent));
@ -337,7 +334,7 @@ public class DomainCreateFlow implements TransactionalFlow {
if (getReservationTypes(domainName).contains(NAME_COLLISION)) { if (getReservationTypes(domainName).contains(NAME_COLLISION)) {
statuses.add(SERVER_HOLD); statuses.add(SERVER_HOLD);
entitiesToSave.add( entitiesToSave.add(
createNameCollisionOneTimePollMessage(targetId, historyEntry, clientId, now)); createNameCollisionOneTimePollMessage(targetId, domainHistory, clientId, now));
} }
DomainBase newDomain = DomainBase newDomain =
@ -365,13 +362,13 @@ public class DomainCreateFlow implements TransactionalFlow {
.build(); .build();
entitiesToSave.add( entitiesToSave.add(
newDomain, newDomain,
domainHistory.asBuilder().setDomainContent(newDomain).build(),
ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()), ForeignKeyIndex.create(newDomain, newDomain.getDeletionTime()),
EppResourceIndex.create(Key.create(newDomain))); EppResourceIndex.create(Key.create(newDomain)));
if (allocationToken.isPresent() if (allocationToken.isPresent()
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) { && TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
entitiesToSave.add( entitiesToSave.add(
allocationTokenFlowUtils.redeemToken( allocationTokenFlowUtils.redeemToken(allocationToken.get(), domainHistory.createVKey()));
allocationToken.get(), HistoryEntry.createVKey(Key.create(historyEntry))));
} }
enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice); enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice);
@ -379,7 +376,7 @@ public class DomainCreateFlow implements TransactionalFlow {
flowCustomLogic.beforeSave( flowCustomLogic.beforeSave(
DomainCreateFlowCustomLogic.BeforeSaveParameters.newBuilder() DomainCreateFlowCustomLogic.BeforeSaveParameters.newBuilder()
.setNewDomain(newDomain) .setNewDomain(newDomain)
.setHistoryEntry(historyEntry) .setHistoryEntry(domainHistory)
.setEntityChanges( .setEntityChanges(
EntityChanges.newBuilder().setSaves(entitiesToSave.build()).build()) EntityChanges.newBuilder().setSaves(entitiesToSave.build()).build())
.setYears(years) .setYears(years)
@ -483,7 +480,7 @@ public class DomainCreateFlow implements TransactionalFlow {
: null); : null);
} }
private HistoryEntry buildHistoryEntry( private DomainHistory buildHistoryEntry(
String repoId, Registry registry, DateTime now, Period period, Duration addGracePeriod) { String repoId, Registry registry, DateTime now, Period period, Duration addGracePeriod) {
// We ignore prober transactions // We ignore prober transactions
if (registry.getTldType() == TldType.REAL) { if (registry.getTldType() == TldType.REAL) {
@ -511,7 +508,7 @@ public class DomainCreateFlow implements TransactionalFlow {
boolean isReserved, boolean isReserved,
int years, int years,
FeesAndCredits feesAndCredits, FeesAndCredits feesAndCredits,
HistoryEntry historyEntry, DomainHistory domainHistory,
Optional<AllocationToken> allocationToken, Optional<AllocationToken> allocationToken,
DateTime now) { DateTime now) {
ImmutableSet.Builder<Flag> flagsBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder<Flag> flagsBuilder = new ImmutableSet.Builder<>();
@ -540,12 +537,12 @@ public class DomainCreateFlow implements TransactionalFlow {
? registry.getAnchorTenantAddGracePeriodLength() ? registry.getAnchorTenantAddGracePeriodLength()
: registry.getAddGracePeriodLength())) : registry.getAddGracePeriodLength()))
.setFlags(flagsBuilder.build()) .setFlags(flagsBuilder.build())
.setParent(historyEntry) .setParent(domainHistory)
.build(); .build();
} }
private Recurring createAutorenewBillingEvent( private Recurring createAutorenewBillingEvent(
HistoryEntry historyEntry, DateTime registrationExpirationTime) { DomainHistory domainHistory, DateTime registrationExpirationTime) {
return new BillingEvent.Recurring.Builder() return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW) .setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) .setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
@ -553,7 +550,7 @@ public class DomainCreateFlow implements TransactionalFlow {
.setClientId(clientId) .setClientId(clientId)
.setEventTime(registrationExpirationTime) .setEventTime(registrationExpirationTime)
.setRecurrenceEndTime(END_OF_TIME) .setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry) .setParent(domainHistory)
.build(); .build();
} }

View file

@ -56,6 +56,7 @@ import javax.persistence.PostLoad;
public class ContactHistory extends HistoryEntry implements SqlEntity { public class ContactHistory extends HistoryEntry implements SqlEntity {
// Store ContactBase instead of ContactResource so we don't pick up its @Id // Store ContactBase instead of ContactResource so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
@Nullable ContactBase contactBase; @Nullable ContactBase contactBase;
@Id @Id
@ -193,9 +194,13 @@ public class ContactHistory extends HistoryEntry implements SqlEntity {
super(instance); super(instance);
} }
public Builder setContactBase(ContactBase contactBase) { public Builder setContactBase(@Nullable ContactBase contactBase) {
// Nullable for the sake of pre-Registry-3.0 history objects
if (contactBase == null) {
return this;
}
getInstance().contactBase = contactBase; getInstance().contactBase = contactBase;
return this; return super.setParent(contactBase);
} }
public Builder setContactRepoId(String contactRepoId) { public Builder setContactRepoId(String contactRepoId) {

View file

@ -33,6 +33,7 @@ import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity; import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity; import google.registry.schema.replay.SqlEntity;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -76,6 +77,7 @@ import javax.persistence.Table;
public class DomainHistory extends HistoryEntry implements SqlEntity { public class DomainHistory extends HistoryEntry implements SqlEntity {
// Store DomainContent instead of DomainBase so we don't pick up its @Id // Store DomainContent instead of DomainBase so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
@Nullable DomainContent domainContent; @Nullable DomainContent domainContent;
@Id @Id
@ -126,7 +128,8 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
insertable = false, insertable = false,
updatable = false) updatable = false)
}) })
Set<DomainDsDataHistory> dsDataHistories = ImmutableSet.of(); // HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
Set<DomainDsDataHistory> dsDataHistories = new HashSet<>();
@Ignore @Ignore
@OneToMany( @OneToMany(
@ -145,7 +148,8 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
insertable = false, insertable = false,
updatable = false) updatable = false)
}) })
Set<GracePeriodHistory> gracePeriodHistories = ImmutableSet.of(); // HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
Set<GracePeriodHistory> gracePeriodHistories = new HashSet<>();
@Override @Override
@Nullable @Nullable
@ -341,9 +345,13 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
super(instance); super(instance);
} }
public Builder setDomainContent(DomainContent domainContent) { public Builder setDomainContent(@Nullable DomainContent domainContent) {
// Nullable for the sake of pre-Registry-3.0 history objects
if (domainContent == null) {
return this;
}
getInstance().domainContent = domainContent; getInstance().domainContent = domainContent;
return this; return super.setParent(domainContent);
} }
public Builder setDomainRepoId(String domainRepoId) { public Builder setDomainRepoId(String domainRepoId) {

View file

@ -38,7 +38,7 @@ import org.joda.time.DateTime;
public class GracePeriodBase extends ImmutableObject { public class GracePeriodBase extends ImmutableObject {
/** Unique id required for hibernate representation. */ /** Unique id required for hibernate representation. */
@Transient Long gracePeriodId; @Transient long gracePeriodId;
/** Repository id for the domain which this grace period belongs to. */ /** Repository id for the domain which this grace period belongs to. */
@Ignore @Ignore

View file

@ -57,6 +57,7 @@ import javax.persistence.PostLoad;
public class HostHistory extends HistoryEntry implements SqlEntity { public class HostHistory extends HistoryEntry implements SqlEntity {
// Store HostBase instead of HostResource so we don't pick up its @Id // Store HostBase instead of HostResource so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects
@Nullable HostBase hostBase; @Nullable HostBase hostBase;
@Id @Id
@ -194,9 +195,13 @@ public class HostHistory extends HistoryEntry implements SqlEntity {
super(instance); super(instance);
} }
public Builder setHostBase(HostBase hostBase) { public Builder setHostBase(@Nullable HostBase hostBase) {
// Nullable for the sake of pre-Registry-3.0 history objects
if (hostBase == null) {
return this;
}
getInstance().hostBase = hostBase; getInstance().hostBase = hostBase;
return this; return super.setParent(hostBase);
} }
public Builder setHostRepoId(String hostRepoId) { public Builder setHostRepoId(String hostRepoId) {

View file

@ -39,6 +39,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.transaction.QueryComposer; import google.registry.persistence.transaction.QueryComposer;
import google.registry.persistence.transaction.TransactionManager; import google.registry.persistence.transaction.TransactionManager;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Optional; import java.util.Optional;
@ -141,22 +143,23 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override @Override
public void putAll(Object... entities) { public void putAll(Object... entities) {
syncIfTransactionless(getOfy().save().entities(entities)); syncIfTransactionless(
getOfy().save().entities(toDatastoreEntities(ImmutableList.copyOf(entities))));
} }
@Override @Override
public void putAll(ImmutableCollection<?> entities) { public void putAll(ImmutableCollection<?> entities) {
syncIfTransactionless(getOfy().save().entities(entities)); syncIfTransactionless(getOfy().save().entities(toDatastoreEntities(entities)));
} }
@Override @Override
public void putWithoutBackup(Object entity) { public void putWithoutBackup(Object entity) {
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entity)); syncIfTransactionless(getOfy().saveWithoutBackup().entities(toDatastoreEntity(entity)));
} }
@Override @Override
public void putAllWithoutBackup(ImmutableCollection<?> entities) { public void putAllWithoutBackup(ImmutableCollection<?> entities) {
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entities)); syncIfTransactionless(getOfy().saveWithoutBackup().entities(toDatastoreEntities(entities)));
} }
@Override @Override
@ -181,7 +184,7 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override @Override
public boolean exists(Object entity) { public boolean exists(Object entity) {
return getOfy().load().key(Key.create(entity)).now() != null; return getOfy().load().key(Key.create(toDatastoreEntity(entity))).now() != null;
} }
@Override @Override
@ -210,8 +213,7 @@ public class DatastoreTransactionManager implements TransactionManager {
return getOfy().load().keys(keyMap.keySet()).entrySet().stream() return getOfy().load().keys(keyMap.keySet()).entrySet().stream()
.collect( .collect(
toImmutableMap( toImmutableMap(
entry -> keyMap.get(entry.getKey()), entry -> keyMap.get(entry.getKey()), entry -> toSqlEntity(entry.getValue())));
entry -> toChildHistoryEntryIfPossible(entry.getValue())));
} }
@Override @Override
@ -244,7 +246,7 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override @Override
public <T> T loadByEntity(T entity) { public <T> T loadByEntity(T entity) {
return ofy().load().entity(entity).now(); return (T) toSqlEntity(ofy().load().entity(toDatastoreEntity(entity)).now());
} }
@Override @Override
@ -286,7 +288,7 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override @Override
public void delete(Object entity) { public void delete(Object entity) {
syncIfTransactionless(getOfy().delete().entity(entity)); syncIfTransactionless(getOfy().delete().entity(toDatastoreEntity(entity)));
} }
@Override @Override
@ -304,7 +306,7 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override @Override
public void deleteWithoutBackup(Object entity) { public void deleteWithoutBackup(Object entity) {
syncIfTransactionless(getOfy().deleteWithoutBackup().entity(entity)); syncIfTransactionless(getOfy().deleteWithoutBackup().entity(toDatastoreEntity(entity)));
} }
@Override @Override
@ -348,28 +350,52 @@ public class DatastoreTransactionManager implements TransactionManager {
*/ */
private void saveEntity(Object entity) { private void saveEntity(Object entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
if (entity instanceof HistoryEntry) { syncIfTransactionless(getOfy().save().entity(toDatastoreEntity(entity)));
entity = ((HistoryEntry) entity).asHistoryEntry();
}
syncIfTransactionless(getOfy().save().entity(entity));
} }
@Nullable @Nullable
private <T> T loadNullable(VKey<T> key) { private <T> T loadNullable(VKey<T> key) {
return toChildHistoryEntryIfPossible(getOfy().load().key(key.getOfyKey()).now()); return toSqlEntity(getOfy().load().key(key.getOfyKey()).now());
} }
/** Converts a nonnull {@link HistoryEntry} to the child format, e.g. {@link DomainHistory} */ /**
* Converts a possible {@link SqlEntity} to a {@link DatastoreEntity}.
*
* <p>One example is that this would convert a {@link DomainHistory} to a {@link HistoryEntry}.
*/
private static Object toDatastoreEntity(@Nullable Object obj) {
if (obj instanceof SqlEntity) {
Optional<DatastoreEntity> possibleDatastoreEntity = ((SqlEntity) obj).toDatastoreEntity();
if (possibleDatastoreEntity.isPresent()) {
return possibleDatastoreEntity.get();
}
}
return obj;
}
/** Converts many possible {@link SqlEntity} objects to {@link DatastoreEntity} objects. */
private static ImmutableList<Object> toDatastoreEntities(ImmutableCollection<?> collection) {
return collection.stream()
.map(DatastoreTransactionManager::toDatastoreEntity)
.collect(toImmutableList());
}
/**
* Converts an object to the corresponding {@link SqlEntity} if necessary and possible.
*
* <p>This should be used when returning objects from Datastore to make sure they reflect the most
* recent type of the object in question.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T toChildHistoryEntryIfPossible(@Nullable T obj) { public static <T> T toSqlEntity(@Nullable T obj) {
// NB: The Key of the object in question may not necessarily be the resulting class that we // NB: The Key of the object in question may not necessarily be the resulting class that we
// wish to have. Because all *History classes are @EntitySubclasses, their Keys will have type // wish to have. For example, because all *History classes are @EntitySubclasses, their Keys
// HistoryEntry -- even if you create them based off the *History class. // will have type HistoryEntry -- even if you create them based off the *History class.
if (obj instanceof HistoryEntry if (obj instanceof DatastoreEntity && !(obj instanceof SqlEntity)) {
&& !(obj instanceof ContactHistory) Optional<SqlEntity> possibleSqlEntity = ((DatastoreEntity) obj).toSqlEntity();
&& !(obj instanceof DomainHistory) if (possibleSqlEntity.isPresent()) {
&& !(obj instanceof HostHistory)) { return (T) possibleSqlEntity.get();
return (T) ((HistoryEntry) obj).toChildHistoryEntity(); }
} }
return obj; return obj;
} }

View file

@ -51,12 +51,15 @@ public class HistoryEntryDao {
public static Iterable<? extends HistoryEntry> loadAllHistoryObjects( public static Iterable<? extends HistoryEntry> loadAllHistoryObjects(
DateTime afterTime, DateTime beforeTime) { DateTime afterTime, DateTime beforeTime) {
if (tm().isOfy()) { if (tm().isOfy()) {
return ofy() return Streams.stream(
.load() ofy()
.type(HistoryEntry.class) .load()
.order("modificationTime") .type(HistoryEntry.class)
.filter("modificationTime >=", afterTime) .order("modificationTime")
.filter("modificationTime <=", beforeTime); .filter("modificationTime >=", afterTime)
.filter("modificationTime <=", beforeTime))
.map(HistoryEntry::toChildHistoryEntity)
.collect(toImmutableList());
} else { } else {
return jpaTm() return jpaTm()
.transact( .transact(
@ -78,13 +81,16 @@ public class HistoryEntryDao {
public static Iterable<? extends HistoryEntry> loadHistoryObjectsForResource( public static Iterable<? extends HistoryEntry> loadHistoryObjectsForResource(
VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) { VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) {
if (tm().isOfy()) { if (tm().isOfy()) {
return ofy() return Streams.stream(
.load() ofy()
.type(HistoryEntry.class) .load()
.ancestor(parentKey.getOfyKey()) .type(HistoryEntry.class)
.order("modificationTime") .ancestor(parentKey.getOfyKey())
.filter("modificationTime >=", afterTime) .order("modificationTime")
.filter("modificationTime <=", beforeTime); .filter("modificationTime >=", afterTime)
.filter("modificationTime <=", beforeTime))
.map(HistoryEntry::toChildHistoryEntity)
.collect(toImmutableList());
} else { } else {
return jpaTm() return jpaTm()
.transact(() -> loadHistoryObjectsForResourceFromSql(parentKey, afterTime, beforeTime)); .transact(() -> loadHistoryObjectsForResourceFromSql(parentKey, afterTime, beforeTime));

View file

@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.ofy.DatastoreTransactionManager.toChildHistoryEntryIfPossible; import static google.registry.model.ofy.DatastoreTransactionManager.toSqlEntity;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static java.util.AbstractMap.SimpleEntry; import static java.util.AbstractMap.SimpleEntry;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
@ -268,7 +268,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
} }
assertInTransaction(); assertInTransaction();
// Necessary due to the changes in HistoryEntry representation during the migration to SQL // Necessary due to the changes in HistoryEntry representation during the migration to SQL
Object toPersist = toChildHistoryEntryIfPossible(entity); Object toPersist = toSqlEntity(entity);
getEntityManager().persist(toPersist); getEntityManager().persist(toPersist);
transactionInfo.get().addUpdate(toPersist); transactionInfo.get().addUpdate(toPersist);
} }
@ -298,7 +298,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
} }
assertInTransaction(); assertInTransaction();
// Necessary due to the changes in HistoryEntry representation during the migration to SQL // Necessary due to the changes in HistoryEntry representation during the migration to SQL
Object toPersist = toChildHistoryEntryIfPossible(entity); Object toPersist = toSqlEntity(entity);
getEntityManager().merge(toPersist); getEntityManager().merge(toPersist);
transactionInfo.get().addUpdate(toPersist); transactionInfo.get().addUpdate(toPersist);
} }
@ -338,7 +338,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
assertInTransaction(); assertInTransaction();
checkArgument(exists(entity), "Given entity does not exist"); checkArgument(exists(entity), "Given entity does not exist");
// Necessary due to the changes in HistoryEntry representation during the migration to SQL // Necessary due to the changes in HistoryEntry representation during the migration to SQL
Object toPersist = toChildHistoryEntryIfPossible(entity); Object toPersist = toSqlEntity(entity);
getEntityManager().merge(toPersist); getEntityManager().merge(toPersist);
transactionInfo.get().addUpdate(toPersist); transactionInfo.get().addUpdate(toPersist);
} }
@ -371,7 +371,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public boolean exists(Object entity) { public boolean exists(Object entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
entity = toChildHistoryEntryIfPossible(entity); entity = toSqlEntity(entity);
EntityType<?> entityType = getEntityType(entity.getClass()); EntityType<?> entityType = getEntityType(entity.getClass());
ImmutableSet<EntityId> entityIds = getEntityIdsFromEntity(entityType, entity); ImmutableSet<EntityId> entityIds = getEntityIdsFromEntity(entityType, entity);
return exists(entityType.getName(), entityIds); return exists(entityType.getName(), entityIds);
@ -414,7 +414,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override @Override
public <T> ImmutableList<T> loadByEntitiesIfPresent(Iterable<T> entities) { public <T> ImmutableList<T> loadByEntitiesIfPresent(Iterable<T> entities) {
return Streams.stream(entities) return Streams.stream(entities)
.map(DatastoreTransactionManager::toChildHistoryEntryIfPossible) .map(DatastoreTransactionManager::toSqlEntity)
.filter(this::exists) .filter(this::exists)
.map(this::loadByEntity) .map(this::loadByEntity)
.collect(toImmutableList()); .collect(toImmutableList());
@ -449,9 +449,8 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
public <T> T loadByEntity(T entity) { public <T> T loadByEntity(T entity) {
checkArgumentNotNull(entity, "entity must be specified"); checkArgumentNotNull(entity, "entity must be specified");
assertInTransaction(); assertInTransaction();
entity = toChildHistoryEntryIfPossible(entity);
// If the caller requested a HistoryEntry, load the corresponding *History class // If the caller requested a HistoryEntry, load the corresponding *History class
T possibleChild = toChildHistoryEntryIfPossible(entity); T possibleChild = toSqlEntity(entity);
return (T) return (T)
loadByKey( loadByKey(
VKey.createSql( VKey.createSql(
@ -511,7 +510,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
return; return;
} }
assertInTransaction(); assertInTransaction();
entity = toChildHistoryEntryIfPossible(entity); entity = toSqlEntity(entity);
Object managedEntity = entity; Object managedEntity = entity;
if (!getEntityManager().contains(entity)) { if (!getEntityManager().contains(entity)) {
managedEntity = getEntityManager().merge(entity); managedEntity = getEntityManager().merge(entity);

View file

@ -751,11 +751,11 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse( .hasResponse(
"poll_response_autorenew.xml", "poll_response_autorenew.xml",
ImmutableMap.of( ImmutableMap.of(
"ID", "1-C-EXAMPLE-13-16-2002", "ID", "1-D-EXAMPLE-11-16-2002",
"QDATE", "2002-06-01T00:04:00Z", "QDATE", "2002-06-01T00:04:00Z",
"DOMAIN", "fakesite.example", "DOMAIN", "fakesite.example",
"EXDATE", "2003-06-01T00:04:00Z")); "EXDATE", "2003-06-01T00:04:00Z"));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-13-16-2002")) assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-D-EXAMPLE-11-16-2002"))
.atTime("2002-07-01T00:02:00Z") .atTime("2002-07-01T00:02:00Z")
.hasResponse("poll_ack_response_empty.xml"); .hasResponse("poll_ack_response_empty.xml");
@ -769,13 +769,13 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse( .hasResponse(
"poll_response_autorenew.xml", "poll_response_autorenew.xml",
ImmutableMap.of( ImmutableMap.of(
"ID", "1-C-EXAMPLE-13-16-2003", // Note -- Year is different from previous ID. "ID", "1-D-EXAMPLE-11-16-2003", // Note -- Year is different from previous ID.
"QDATE", "2003-06-01T00:04:00Z", "QDATE", "2003-06-01T00:04:00Z",
"DOMAIN", "fakesite.example", "DOMAIN", "fakesite.example",
"EXDATE", "2004-06-01T00:04:00Z")); "EXDATE", "2004-06-01T00:04:00Z"));
// Ack the second poll message and verify that none remain. // Ack the second poll message and verify that none remain.
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-13-16-2003")) assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-D-EXAMPLE-11-16-2003"))
.atTime("2003-07-01T00:05:05Z") .atTime("2003-07-01T00:05:05Z")
.hasResponse("poll_ack_response_empty.xml"); .hasResponse("poll_ack_response_empty.xml");
assertThatCommand("poll.xml") assertThatCommand("poll.xml")
@ -805,7 +805,7 @@ class EppLifecycleDomainTest extends EppTestCase {
// As the losing registrar, read the request poll message, and then ack it. // As the losing registrar, read the request poll message, and then ack it.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2"); assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
String messageId = "1-C-EXAMPLE-18-24-2001"; String messageId = "1-D-EXAMPLE-19-25-2001";
assertThatCommand("poll.xml") assertThatCommand("poll.xml")
.atTime("2001-01-01T00:01:00Z") .atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId)); .hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId));
@ -814,7 +814,7 @@ class EppLifecycleDomainTest extends EppTestCase {
.hasResponse("poll_ack_response_empty.xml"); .hasResponse("poll_ack_response_empty.xml");
// Five days in the future, expect a server approval poll message to the loser, and ack it. // Five days in the future, expect a server approval poll message to the loser, and ack it.
messageId = "1-C-EXAMPLE-18-23-2001"; messageId = "1-D-EXAMPLE-19-24-2001";
assertThatCommand("poll.xml") assertThatCommand("poll.xml")
.atTime("2001-01-06T00:01:00Z") .atTime("2001-01-06T00:01:00Z")
.hasResponse( .hasResponse(
@ -826,7 +826,7 @@ class EppLifecycleDomainTest extends EppTestCase {
assertThatLogoutSucceeds(); assertThatLogoutSucceeds();
// Also expect a server approval poll message to the winner, with the transfer request trid. // Also expect a server approval poll message to the winner, with the transfer request trid.
messageId = "1-C-EXAMPLE-18-22-2001"; messageId = "1-D-EXAMPLE-19-23-2001";
assertThatLoginSucceeds("TheRegistrar", "password2"); assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("poll.xml") assertThatCommand("poll.xml")
.atTime("2001-01-06T00:02:00Z") .atTime("2001-01-06T00:02:00Z")

View file

@ -403,7 +403,7 @@ class DomainTransferCancelFlowTest
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setType(DOMAIN_TRANSFER_REQUEST) .setType(DOMAIN_TRANSFER_REQUEST)
.setParent(domain) .setDomainContent(domain)
.setModificationTime(clock.nowUtc().minusDays(4)) .setModificationTime(clock.nowUtc().minusDays(4))
.setDomainTransactionRecords( .setDomainTransactionRecords(
ImmutableSet.of(previousSuccessRecord, notCancellableRecord)) ImmutableSet.of(previousSuccessRecord, notCancellableRecord))

View file

@ -35,8 +35,10 @@ import google.registry.model.EppResource;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.index.EppResourceIndex; import google.registry.model.index.EppResourceIndex;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
@ -64,9 +66,9 @@ class ChildEntityInputTest {
private DomainBase domainA; private DomainBase domainA;
private DomainBase domainB; private DomainBase domainB;
private HistoryEntry domainHistoryEntryA; private DomainHistory domainHistoryEntryA;
private HistoryEntry domainHistoryEntryB; private DomainHistory domainHistoryEntryB;
private HistoryEntry contactHistoryEntry; private ContactHistory contactHistoryEntry;
private BillingEvent.OneTime oneTimeA; private BillingEvent.OneTime oneTimeA;
private BillingEvent.OneTime oneTimeB; private BillingEvent.OneTime oneTimeB;
private BillingEvent.Recurring recurringA; private BillingEvent.Recurring recurringA;
@ -78,10 +80,10 @@ class ChildEntityInputTest {
domainA = persistEppResourceInFirstBucket(newDomainBase("a.tld", contact)); domainA = persistEppResourceInFirstBucket(newDomainBase("a.tld", contact));
domainHistoryEntryA = domainHistoryEntryA =
persistResource( persistResource(
new HistoryEntry.Builder().setParent(domainA).setModificationTime(now).build()); new DomainHistory.Builder().setDomainContent(domainA).setModificationTime(now).build());
contactHistoryEntry = contactHistoryEntry =
persistResource( persistResource(
new HistoryEntry.Builder().setParent(contact).setModificationTime(now).build()); new ContactHistory.Builder().setContactBase(contact).setModificationTime(now).build());
oneTimeA = oneTimeA =
persistResource( persistResource(
new BillingEvent.OneTime.Builder() new BillingEvent.OneTime.Builder()
@ -111,7 +113,7 @@ class ChildEntityInputTest {
domainB = persistEppResourceInFirstBucket(newDomainBase("b.tld")); domainB = persistEppResourceInFirstBucket(newDomainBase("b.tld"));
domainHistoryEntryB = domainHistoryEntryB =
persistResource( persistResource(
new HistoryEntry.Builder().setParent(domainB).setModificationTime(now).build()); new DomainHistory.Builder().setDomainContent(domainB).setModificationTime(now).build());
oneTimeB = oneTimeB =
persistResource( persistResource(
new BillingEvent.OneTime.Builder() new BillingEvent.OneTime.Builder()
@ -177,9 +179,9 @@ class ChildEntityInputTest {
} }
assertThat(seen) assertThat(seen)
.containsExactly( .containsExactly(
domainHistoryEntryA, domainHistoryEntryA.asHistoryEntry(),
domainHistoryEntryB, domainHistoryEntryB.asHistoryEntry(),
contactHistoryEntry, contactHistoryEntry.asHistoryEntry(),
oneTimeA, oneTimeA,
recurringA, recurringA,
oneTimeB, oneTimeB,
@ -202,7 +204,11 @@ class ChildEntityInputTest {
.createReaders() .createReaders()
.get(0); .get(0);
assertThat(getAllFromReader(reader)) assertThat(getAllFromReader(reader))
.containsExactly(domainHistoryEntryA, contactHistoryEntry, oneTimeA, recurringA); .containsExactly(
domainHistoryEntryA.asHistoryEntry(),
contactHistoryEntry.asHistoryEntry(),
oneTimeA,
recurringA);
} }
private static Set<ImmutableObject> getAllFromReader(InputReader<ImmutableObject> reader) private static Set<ImmutableObject> getAllFromReader(InputReader<ImmutableObject> reader)
@ -230,7 +236,7 @@ class ChildEntityInputTest {
HistoryEntry.class, BillingEvent.OneTime.class, BillingEvent.Recurring.class)) HistoryEntry.class, BillingEvent.OneTime.class, BillingEvent.Recurring.class))
.createReaders() .createReaders()
.get(0); .get(0);
assertThat(getAllFromReader(reader)).containsExactly(contactHistoryEntry); assertThat(getAllFromReader(reader)).containsExactly(contactHistoryEntry.asHistoryEntry());
} }
@Test @Test
@ -287,11 +293,12 @@ class ChildEntityInputTest {
DomainBase domain = persistSimpleResource(newDomainBase(i + ".tld")); DomainBase domain = persistSimpleResource(newDomainBase(i + ".tld"));
historyEntries.add( historyEntries.add(
persistResource( persistResource(
new HistoryEntry.Builder() new DomainHistory.Builder()
.setParent(domain) .setDomainContent(domain)
.setModificationTime(now) .setModificationTime(now)
.setClientId(i + ".tld") .setClientId(i + ".tld")
.build())); .build())
.asHistoryEntry());
persistResource(EppResourceIndex.create(getBucketKey(i), Key.create(domain))); persistResource(EppResourceIndex.create(getBucketKey(i), Key.create(domain)));
} }
Set<ImmutableObject> seen = new HashSet<>(); Set<ImmutableObject> seen = new HashSet<>();
@ -333,7 +340,11 @@ class ChildEntityInputTest {
seen.add(deserializedReader.next()); seen.add(deserializedReader.next());
seen.add(deserializedReader.next()); seen.add(deserializedReader.next());
assertThat(seen) assertThat(seen)
.containsExactly(domainHistoryEntryA, contactHistoryEntry, oneTimeA, recurringA); .containsExactly(
domainHistoryEntryA.asHistoryEntry(),
contactHistoryEntry.asHistoryEntry(),
oneTimeA,
recurringA);
assertThrows(NoSuchElementException.class, deserializedReader::next); assertThrows(NoSuchElementException.class, deserializedReader::next);
} }
} }

View file

@ -62,7 +62,7 @@ public class PollMessageExternalKeyConverterTest {
historyEntry = historyEntry =
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setParent(persistActiveDomain("foo.foobar")) .setDomainContent(persistActiveDomain("foo.foobar"))
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))
.setXmlBytes("<xml></xml>".getBytes(UTF_8)) .setXmlBytes("<xml></xml>".getBytes(UTF_8))

View file

@ -41,7 +41,7 @@ import org.junit.jupiter.api.BeforeEach;
class HistoryEntryDaoTest extends EntityTestCase { class HistoryEntryDaoTest extends EntityTestCase {
private DomainBase domain; private DomainBase domain;
private HistoryEntry historyEntry; private HistoryEntry domainHistory;
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {
@ -55,10 +55,10 @@ class HistoryEntryDaoTest extends EntityTestCase {
.setReportField(TransactionReportField.NET_ADDS_1_YR) .setReportField(TransactionReportField.NET_ADDS_1_YR)
.setReportAmount(1) .setReportAmount(1)
.build(); .build();
// Set up a new persisted HistoryEntry entity. // Set up a new persisted DomainHistory entity.
historyEntry = domainHistory =
new DomainHistory.Builder() new DomainHistory.Builder()
.setParent(domain) .setDomainContent(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))
.setXmlBytes("<xml></xml>".getBytes(UTF_8)) .setXmlBytes("<xml></xml>".getBytes(UTF_8))
@ -71,14 +71,14 @@ class HistoryEntryDaoTest extends EntityTestCase {
.setRequestedByRegistrar(false) .setRequestedByRegistrar(false)
.setDomainTransactionRecords(ImmutableSet.of(transactionRecord)) .setDomainTransactionRecords(ImmutableSet.of(transactionRecord))
.build(); .build();
persistResource(historyEntry); persistResource(domainHistory);
} }
@TestOfyAndSql @TestOfyAndSql
void testSimpleLoadAll() { void testSimpleLoadAll() {
assertThat(HistoryEntryDao.loadAllHistoryObjects(START_OF_TIME, END_OF_TIME)) assertThat(HistoryEntryDao.loadAllHistoryObjects(START_OF_TIME, END_OF_TIME))
.comparingElementsUsing(immutableObjectCorrespondence("nsHosts")) .comparingElementsUsing(immutableObjectCorrespondence("nsHosts", "domainContent"))
.containsExactly(historyEntry); .containsExactly(domainHistory);
} }
@TestOfyAndSql @TestOfyAndSql
@ -99,8 +99,8 @@ class HistoryEntryDaoTest extends EntityTestCase {
transactIfJpaTm( transactIfJpaTm(
() -> () ->
assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey())) assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey()))
.comparingElementsUsing(immutableObjectCorrespondence("nsHosts")) .comparingElementsUsing(immutableObjectCorrespondence("nsHosts", "domainContent"))
.containsExactly(historyEntry)); .containsExactly(domainHistory));
} }
@TestOfyAndSql @TestOfyAndSql

View file

@ -39,7 +39,7 @@ import org.junit.jupiter.api.BeforeEach;
@DualDatabaseTest @DualDatabaseTest
class HistoryEntryTest extends EntityTestCase { class HistoryEntryTest extends EntityTestCase {
private HistoryEntry historyEntry; private DomainHistory domainHistory;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
@ -53,9 +53,9 @@ class HistoryEntryTest extends EntityTestCase {
.setReportAmount(1) .setReportAmount(1)
.build(); .build();
// Set up a new persisted HistoryEntry entity. // Set up a new persisted HistoryEntry entity.
historyEntry = domainHistory =
new DomainHistory.Builder() new DomainHistory.Builder()
.setParent(domain) .setDomainContent(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))
.setXmlBytes("<xml></xml>".getBytes(UTF_8)) .setXmlBytes("<xml></xml>".getBytes(UTF_8))
@ -68,26 +68,27 @@ class HistoryEntryTest extends EntityTestCase {
.setRequestedByRegistrar(false) .setRequestedByRegistrar(false)
.setDomainTransactionRecords(ImmutableSet.of(transactionRecord)) .setDomainTransactionRecords(ImmutableSet.of(transactionRecord))
.build(); .build();
persistResource(historyEntry); persistResource(domainHistory);
} }
@TestOfyAndSql @TestOfyAndSql
void testPersistence() { void testPersistence() {
transactIfJpaTm( transactIfJpaTm(
() -> { () -> {
HistoryEntry fromDatabase = tm().loadByEntity(historyEntry); HistoryEntry fromDatabase = tm().loadByEntity(domainHistory);
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(fromDatabase) .that(fromDatabase)
.isEqualExceptFields(historyEntry, "nsHosts", "domainTransactionRecords"); .isEqualExceptFields(
domainHistory, "nsHosts", "domainTransactionRecords", "domainContent");
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(Iterables.getOnlyElement(fromDatabase.getDomainTransactionRecords())) .that(Iterables.getOnlyElement(fromDatabase.getDomainTransactionRecords()))
.isEqualExceptFields( .isEqualExceptFields(
Iterables.getOnlyElement(historyEntry.getDomainTransactionRecords()), "id"); Iterables.getOnlyElement(domainHistory.getDomainTransactionRecords()), "id");
}); });
} }
@TestOfyOnly @TestOfyOnly
void testIndexing() throws Exception { void testIndexing() throws Exception {
verifyIndexing(historyEntry.asHistoryEntry(), "modificationTime", "clientId"); verifyIndexing(domainHistory.asHistoryEntry(), "modificationTime", "clientId");
} }
} }

View file

@ -224,7 +224,7 @@ public class DomainBaseToXjcConverterTest {
new DomainHistory.Builder() new DomainHistory.Builder()
.setModificationTime(clock.nowUtc()) .setModificationTime(clock.nowUtc())
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setParent(domain) .setDomainContent(domain)
.build()); .build());
BillingEvent.OneTime billingEvent = BillingEvent.OneTime billingEvent =
persistResource( persistResource(

View file

@ -64,7 +64,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.Saver;
import google.registry.dns.writer.VoidDnsWriter; import google.registry.dns.writer.VoidDnsWriter;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.EppResource; import google.registry.model.EppResource;
@ -124,6 +123,7 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
@ -642,7 +642,7 @@ public class DatabaseHelper {
new DomainHistory.Builder() new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setModificationTime(now) .setModificationTime(now)
.setParent(domain) .setDomainContent(domain)
.build()); .build());
BillingEvent.Recurring autorenewEvent = BillingEvent.Recurring autorenewEvent =
persistResource( persistResource(
@ -683,7 +683,7 @@ public class DatabaseHelper {
new DomainHistory.Builder() new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST) .setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
.setModificationTime(tm().transact(() -> tm().getTransactionTime())) .setModificationTime(tm().transact(() -> tm().getTransactionTime()))
.setParent(domain) .setDomainContent(domain)
.build()); .build());
BillingEvent.OneTime transferBillingEvent = BillingEvent.OneTime transferBillingEvent =
persistResource( persistResource(
@ -1014,8 +1014,9 @@ public class DatabaseHelper {
private static <R> void saveResource(R resource, boolean wantBackup) { private static <R> void saveResource(R resource, boolean wantBackup) {
if (tm().isOfy()) { if (tm().isOfy()) {
Saver saver = wantBackup || alwaysSaveWithBackup ? ofy().save() : ofy().saveWithoutBackup(); Consumer<Object> saver =
saver.entity(resource); wantBackup || alwaysSaveWithBackup ? tm()::put : tm()::putWithoutBackup;
saver.accept(resource);
if (resource instanceof EppResource) { if (resource instanceof EppResource) {
EppResource eppResource = (EppResource) resource; EppResource eppResource = (EppResource) resource;
persistEppResourceExtras( persistEppResourceExtras(
@ -1027,13 +1028,13 @@ public class DatabaseHelper {
} }
private static <R extends EppResource> void persistEppResourceExtras( private static <R extends EppResource> void persistEppResourceExtras(
R resource, EppResourceIndex index, Saver saver) { R resource, EppResourceIndex index, Consumer<Object> saver) {
assertWithMessage("Cannot persist an EppResource with a missing repoId in tests") assertWithMessage("Cannot persist an EppResource with a missing repoId in tests")
.that(resource.getRepoId()) .that(resource.getRepoId())
.isNotEmpty(); .isNotEmpty();
saver.entity(index); saver.accept(index);
if (resource instanceof ForeignKeyedEppResource) { if (resource instanceof ForeignKeyedEppResource) {
saver.entity(ForeignKeyIndex.create(resource, resource.getDeletionTime())); saver.accept(ForeignKeyIndex.create(resource, resource.getDeletionTime()));
} }
} }
@ -1058,8 +1059,8 @@ public class DatabaseHelper {
tm().transact( tm().transact(
() -> { () -> {
if (tm().isOfy()) { if (tm().isOfy()) {
Saver saver = ofy().save(); Consumer<Object> saver = tm()::put;
saver.entity(resource); saver.accept(resource);
persistEppResourceExtras(resource, eppResourceIndex, saver); persistEppResourceExtras(resource, eppResourceIndex, saver);
} else { } else {
tm().put(resource); tm().put(resource);

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects; import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistActiveDomain; import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
@ -225,7 +226,7 @@ class DedupeRecurringBillingEventIdsCommandTest
private static void assertNotChangeExceptUpdateTime(ImmutableObject... entities) { private static void assertNotChangeExceptUpdateTime(ImmutableObject... entities) {
for (ImmutableObject entity : entities) { for (ImmutableObject entity : entities) {
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(ofy().load().entity(entity).now()) .that(loadByEntity(entity))
.isEqualExceptFields(entity, "updateTimestamp", "revisions"); .isEqualExceptFields(entity, "updateTimestamp", "revisions");
} }
} }

View file

@ -108,7 +108,7 @@ class EppLifecycleToolsTest extends EppTestCase {
.atTime("2001-06-08T00:00:00Z") .atTime("2001-06-08T00:00:00Z")
.hasResponse("poll_response_unrenew.xml"); .hasResponse("poll_response_unrenew.xml");
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-8-TLD-19-20-2001")) assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-9-TLD-20-21-2001"))
.atTime("2001-06-08T00:00:01Z") .atTime("2001-06-08T00:00:01Z")
.hasResponse("poll_ack_response_empty.xml"); .hasResponse("poll_ack_response_empty.xml");
@ -129,7 +129,7 @@ class EppLifecycleToolsTest extends EppTestCase {
.hasResponse( .hasResponse(
"poll_response_autorenew.xml", "poll_response_autorenew.xml",
ImmutableMap.of( ImmutableMap.of(
"ID", "1-8-TLD-19-22-2003", "ID", "1-9-TLD-20-23-2003",
"QDATE", "2003-06-01T00:02:00Z", "QDATE", "2003-06-01T00:02:00Z",
"DOMAIN", "example.tld", "DOMAIN", "example.tld",
"EXDATE", "2004-06-01T00:02:00Z")); "EXDATE", "2004-06-01T00:02:00Z"));

View file

@ -298,7 +298,7 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
new DomainHistory.Builder() new DomainHistory.Builder()
.setModificationTime(fakeClock.nowUtc()) .setModificationTime(fakeClock.nowUtc())
.setType(DOMAIN_CREATE) .setType(DOMAIN_CREATE)
.setParent(domain) .setDomainContent(domain)
.build()); .build());
BillingEvent.Recurring autorenewBillingEvent = BillingEvent.Recurring autorenewBillingEvent =
persistResource( persistResource(

View file

@ -3,7 +3,7 @@
<result code="1301"> <result code="1301">
<msg>Command completed successfully; ack to dequeue</msg> <msg>Command completed successfully; ack to dequeue</msg>
</result> </result>
<msgQ count="1" id="1-8-TLD-19-20-2001"> <msgQ count="1" id="1-9-TLD-20-21-2001">
<qDate>2001-06-07T00:00:00Z</qDate> <qDate>2001-06-07T00:00:00Z</qDate>
<msg>Domain example.tld was unrenewed by 3 years; now expires at 2003-06-01T00:02:00.000Z.</msg> <msg>Domain example.tld was unrenewed by 3 years; now expires at 2003-06-01T00:02:00.000Z.</msg>
</msgQ> </msgQ>

View file

@ -294,8 +294,8 @@ class google.registry.model.domain.GracePeriod {
google.registry.model.domain.rgp.GracePeriodStatus type; google.registry.model.domain.rgp.GracePeriodStatus type;
google.registry.persistence.BillingVKey$BillingEventVKey billingEventOneTime; google.registry.persistence.BillingVKey$BillingEventVKey billingEventOneTime;
google.registry.persistence.BillingVKey$BillingRecurrenceVKey billingEventRecurring; google.registry.persistence.BillingVKey$BillingRecurrenceVKey billingEventRecurring;
java.lang.Long gracePeriodId;
java.lang.String clientId; java.lang.String clientId;
long gracePeriodId;
org.joda.time.DateTime expirationTime; org.joda.time.DateTime expirationTime;
} }
class google.registry.model.domain.Period { class google.registry.model.domain.Period {