Remove ofy support from HistoryEntry (#1823)

This PR removes all Ofy related cruft around `HistoryEntry` and its three subclasses in order to support dual-write to datastore and SQL. The class structure was refactored to take advantage of inheritance to reduce code duplication and improve clarity.

Note that for the embedded EPP resources, either their columns are all empty (for pre-3.0 entities imported into SQL), including their unique foreign key (domain name, host name, contact id) and the update timestamp; or they are filled as expected (for entities that were written since dual writing was implemented).

Therefore the check for foreign key column nullness in the various `@PostLoad` methods in the original code is an no-op as the EPP resource would have been loaded as null. In another word, there is no case where the update timestamp is null but other columns are not.

See the following query for the most recent entries in each table where the foreign key column or the update timestamp are null -- they are the same.

```
[I]postgres=> select MAX(history_modification_time) from "DomainHistory" where update_timestamp is null;
            max
----------------------------
 2021-09-27 15:56:52.502+00
(1 row)

[I]postgres=> select MAX(history_modification_time) from "DomainHistory" where domain_name is null;
            max
----------------------------
 2021-09-27 15:56:52.502+00
(1 row)

[I]postgres=> select MAX(history_modification_time) from "ContactHistory" where update_timestamp is null;
            max
----------------------------
 2021-09-27 15:56:04.311+00
(1 row)

[I]postgres=> select MAX(history_modification_time) from "ContactHistory" where contact_id is null;
            max
----------------------------
 2021-09-27 15:56:04.311+00
(1 row)

[I]postgres=> select MAX(history_modification_time) from "HostHistory" where update_timestamp is null;
            max
----------------------------
 2021-09-27 15:52:16.517+00
(1 row)

[I]postgres=> select MAX(history_modification_time) from "HostHistory" where host_name is null;
            max
----------------------------
 2021-09-27 15:52:16.517+00
(1 row)
```
This commit is contained in:
Lai Jiang 2022-11-01 21:17:20 -04:00 committed by GitHub
parent 5df9a2e4aa
commit ad4aa73328
88 changed files with 845 additions and 2213 deletions

View file

@ -101,15 +101,20 @@ public class DeleteProberDataAction implements Runnable {
@Inject DnsQueue dnsQueue; @Inject DnsQueue dnsQueue;
@Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun; @Inject
@Parameter(PARAM_DRY_RUN)
boolean isDryRun;
/** List of TLDs to work on. If empty - will work on all TLDs that end with .test. */ /** List of TLDs to work on. If empty - will work on all TLDs that end with .test. */
@Inject @Parameter(PARAM_TLDS) ImmutableSet<String> tlds; @Inject
@Parameter(PARAM_TLDS)
ImmutableSet<String> tlds;
@Inject @Inject
@Config("registryAdminClientId") @Config("registryAdminClientId")
String registryAdminRegistrarId; String registryAdminRegistrarId;
@Inject DeleteProberDataAction() {} @Inject
DeleteProberDataAction() {}
@Override @Override
public void run() { public void run() {
@ -151,7 +156,7 @@ public class DeleteProberDataAction implements Runnable {
DateTime now = tm().getTransactionTime(); DateTime now = tm().getTransactionTime();
// Scroll through domains, soft-deleting as necessary (very few will be soft-deleted) and // Scroll through domains, soft-deleting as necessary (very few will be soft-deleted) and
// keeping track of which domains to hard-delete (there can be many, so we batch them up) // keeping track of which domains to hard-delete (there can be many, so we batch them up)
ScrollableResults scrollableResult = try (ScrollableResults scrollableResult =
jpaTm() jpaTm()
.query(DOMAIN_QUERY_STRING, Domain.class) .query(DOMAIN_QUERY_STRING, Domain.class)
.setParameter("tlds", deletableTlds) .setParameter("tlds", deletableTlds)
@ -161,28 +166,30 @@ public class DeleteProberDataAction implements Runnable {
.setParameter("nowAutoTimestamp", CreateAutoTimestamp.create(now)) .setParameter("nowAutoTimestamp", CreateAutoTimestamp.create(now))
.unwrap(Query.class) .unwrap(Query.class)
.setCacheMode(CacheMode.IGNORE) .setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY); .scroll(ScrollMode.FORWARD_ONLY)) {
ImmutableList.Builder<String> domainRepoIdsToHardDelete = new ImmutableList.Builder<>(); ImmutableList.Builder<String> domainRepoIdsToHardDelete = new ImmutableList.Builder<>();
ImmutableList.Builder<String> hostNamesToHardDelete = new ImmutableList.Builder<>(); ImmutableList.Builder<String> hostNamesToHardDelete = new ImmutableList.Builder<>();
for (int i = 1; scrollableResult.next(); i = (i + 1) % BATCH_SIZE) { for (int i = 1; scrollableResult.next(); i = (i + 1) % BATCH_SIZE) {
Domain domain = (Domain) scrollableResult.get(0); Domain domain = (Domain) scrollableResult.get(0);
processDomain( processDomain(
domain, domain,
domainRepoIdsToHardDelete, domainRepoIdsToHardDelete,
hostNamesToHardDelete, hostNamesToHardDelete,
softDeletedDomains, softDeletedDomains,
hardDeletedDomains); hardDeletedDomains);
// Batch the deletion and DB flush + session clearing so we don't OOM // Batch the deletion and DB flush + session clearing, so we don't OOM
if (i == 0) { if (i == 0) {
hardDeleteDomainsAndHosts(domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build()); hardDeleteDomainsAndHosts(
domainRepoIdsToHardDelete = new ImmutableList.Builder<>(); domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build());
hostNamesToHardDelete = new ImmutableList.Builder<>(); domainRepoIdsToHardDelete = new ImmutableList.Builder<>();
jpaTm().getEntityManager().flush(); hostNamesToHardDelete = new ImmutableList.Builder<>();
jpaTm().getEntityManager().clear(); jpaTm().getEntityManager().flush();
jpaTm().getEntityManager().clear();
}
} }
// process the remainder
hardDeleteDomainsAndHosts(domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build());
} }
// process the remainder
hardDeleteDomainsAndHosts(domainRepoIdsToHardDelete.build(), hostNamesToHardDelete.build());
} }
private void processDomain( private void processDomain(
@ -236,7 +243,7 @@ public class DeleteProberDataAction implements Runnable {
.setParameter("repoIds", domainRepoIds) .setParameter("repoIds", domainRepoIds)
.executeUpdate(); .executeUpdate();
jpaTm() jpaTm()
.query("DELETE FROM DomainHistory WHERE domainRepoId IN :repoIds") .query("DELETE FROM DomainHistory WHERE repoId IN :repoIds")
.setParameter("repoIds", domainRepoIds) .setParameter("repoIds", domainRepoIds)
.executeUpdate(); .executeUpdate();
jpaTm() jpaTm()

View file

@ -55,7 +55,7 @@ import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.Type; import google.registry.model.registrar.Registrar.Type;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel; import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.rde.DepositFragment; import google.registry.rde.DepositFragment;
@ -71,11 +71,9 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet; import java.util.HashSet;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.persistence.IdClass;
import org.apache.beam.sdk.Pipeline; import org.apache.beam.sdk.Pipeline;
import org.apache.beam.sdk.PipelineResult; import org.apache.beam.sdk.PipelineResult;
import org.apache.beam.sdk.coders.KvCoder; import org.apache.beam.sdk.coders.KvCoder;
@ -172,6 +170,7 @@ import org.joda.time.DateTime;
* @see <a href="https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates">Using * @see <a href="https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates">Using
* Flex Templates</a> * Flex Templates</a>
*/ */
@SuppressWarnings("ALL")
@Singleton @Singleton
public class RdePipeline implements Serializable { public class RdePipeline implements Serializable {
@ -191,16 +190,6 @@ public class RdePipeline implements Serializable {
private static final ImmutableSet<Type> IGNORED_REGISTRAR_TYPES = private static final ImmutableSet<Type> IGNORED_REGISTRAR_TYPES =
Sets.immutableEnumSet(Registrar.Type.MONITORING, Registrar.Type.TEST); Sets.immutableEnumSet(Registrar.Type.MONITORING, Registrar.Type.TEST);
// The field name of the EPP resource embedded in its corresponding history entry.
private static final ImmutableMap<Class<? extends HistoryEntry>, String> EPP_RESOURCE_FIELD_NAME =
ImmutableMap.of(
DomainHistory.class,
"domainBase",
ContactHistory.class,
"contactBase",
HostHistory.class,
"hostBase");
private static final FluentLogger logger = FluentLogger.forEnclosingClass(); private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject @Inject
@ -337,26 +326,20 @@ public class RdePipeline implements Serializable {
*/ */
private <T extends HistoryEntry> PCollection<KV<String, Long>> getMostRecentHistoryEntries( private <T extends HistoryEntry> PCollection<KV<String, Long>> getMostRecentHistoryEntries(
Pipeline pipeline, Class<T> historyClass) { Pipeline pipeline, Class<T> historyClass) {
String repoIdFieldName = HistoryEntryDao.REPO_ID_FIELD_NAMES.get(historyClass);
String resourceFieldName = EPP_RESOURCE_FIELD_NAME.get(historyClass);
return pipeline.apply( return pipeline.apply(
String.format("Load most recent %s", historyClass.getSimpleName()), String.format("Load most recent %s", historyClass.getSimpleName()),
RegistryJpaIO.read( RegistryJpaIO.read(
("SELECT %repoIdField%, id FROM %entity% WHERE (%repoIdField%, modificationTime)" ("SELECT repoId, revisionId FROM %entity% WHERE (repoId, modificationTime) IN"
+ " IN (SELECT %repoIdField%, MAX(modificationTime) FROM %entity% WHERE" + " (SELECT repoId, MAX(modificationTime) FROM %entity% WHERE"
+ " modificationTime <= :watermark GROUP BY %repoIdField%) AND" + " modificationTime <= :watermark GROUP BY repoId) AND resource.deletionTime"
+ " %resourceField%.deletionTime > :watermark AND" + " > :watermark AND COALESCE(resource.creationClientId, '') NOT LIKE"
+ " COALESCE(%resourceField%.creationClientId, '') NOT LIKE 'prober-%' AND" + " 'prober-%' AND COALESCE(resource.currentSponsorClientId, '') NOT LIKE"
+ " COALESCE(%resourceField%.currentSponsorClientId, '') NOT LIKE 'prober-%'" + " 'prober-%' AND COALESCE(resource.lastEppUpdateClientId, '') NOT LIKE"
+ " AND COALESCE(%resourceField%.lastEppUpdateClientId, '') NOT LIKE"
+ " 'prober-%' " + " 'prober-%' "
+ (historyClass == DomainHistory.class + (historyClass == DomainHistory.class
? "AND %resourceField%.tld IN " ? "AND resource.tld IN " + "(SELECT id FROM Tld WHERE tldType = 'REAL')"
+ "(SELECT id FROM Tld WHERE tldType = 'REAL')"
: "")) : ""))
.replace("%entity%", historyClass.getSimpleName()) .replace("%entity%", historyClass.getSimpleName()),
.replace("%repoIdField%", repoIdFieldName)
.replace("%resourceField%", resourceFieldName),
ImmutableMap.of("watermark", watermark), ImmutableMap.of("watermark", watermark),
Object[].class, Object[].class,
row -> KV.of((String) row[0], (long) row[1])) row -> KV.of((String) row[0], (long) row[1]))
@ -380,38 +363,28 @@ public class RdePipeline implements Serializable {
checkState( checkState(
dedupedIds.size() == 1, dedupedIds.size() == 1,
"Multiple unique revision IDs detected for %s repo ID %s: %s", "Multiple unique revision IDs detected for %s repo ID %s: %s",
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz), historyEntryClazz.getSimpleName(),
repoId, repoId,
ids); ids);
logger.atSevere().log( logger.atSevere().log(
"Duplicate revision IDs detected for %s repo ID %s: %s", "Duplicate revision IDs detected for %s repo ID %s: %s",
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz), repoId, ids); historyEntryClazz.getSimpleName(), repoId, ids);
} }
return loadResourceByHistoryEntryId(historyEntryClazz, repoId, ids.get(0)); return loadResourceByHistoryEntryId(historyEntryClazz, repoId, ids.get(0));
} }
private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId( private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId(
Class<T> historyEntryClazz, String repoId, long revisionId) { Class<T> historyEntryClazz, String repoId, long revisionId) {
try {
Class<?> idClazz = historyEntryClazz.getAnnotation(IdClass.class).value(); return jpaTm()
Serializable idObject = .transact(
(Serializable) () ->
idClazz.getConstructor(String.class, long.class).newInstance(repoId, revisionId); jpaTm()
return jpaTm() .loadByKey(
.transact(() -> jpaTm().loadByKey(VKey.createSql(historyEntryClazz, idObject))) VKey.createSql(historyEntryClazz, new HistoryEntryId(repoId, revisionId))))
.getResourceAtPointInTime() .getResourceAtPointInTime()
.map(resource -> resource.cloneProjectedAtTime(watermark)) .map(resource -> resource.cloneProjectedAtTime(watermark))
.get(); .get();
} catch (NoSuchMethodException
| InvocationTargetException
| InstantiationException
| IllegalAccessException e) {
throw new RuntimeException(
String.format(
"Cannot load resource from %s with repoId %s and revisionId %s",
historyEntryClazz.getSimpleName(), repoId, revisionId),
e);
}
} }
/** /**

View file

@ -23,7 +23,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.ParameterValueRangeErrorException;
import google.registry.flows.EppException.SyntaxErrorException; import google.registry.flows.EppException.SyntaxErrorException;
@ -34,7 +33,7 @@ import google.registry.model.eppcommon.EppXmlTransformer;
import google.registry.model.eppinput.EppInput.WrongProtocolVersionException; import google.registry.model.eppinput.EppInput.WrongProtocolVersionException;
import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppOutput;
import google.registry.model.host.InetAddressAdapter.IpVersionMismatchException; import google.registry.model.host.InetAddressAdapter.IpVersionMismatchException;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.translators.CurrencyUnitAdapter.UnknownCurrencyException; import google.registry.model.translators.CurrencyUnitAdapter.UnknownCurrencyException;
import google.registry.xml.XmlException; import google.registry.xml.XmlException;
import java.util.List; import java.util.List;
@ -103,9 +102,8 @@ public final class FlowUtils {
} }
} }
public static <H extends HistoryEntry> Key<H> createHistoryKey( public static HistoryEntryId createHistoryEntryId(EppResource parent) {
EppResource parent, Class<H> clazz) { return new HistoryEntryId(parent.getRepoId(), allocateId());
return Key.create(Key.create(parent), clazz, allocateId());
} }
/** Registrar is not logged in. */ /** Registrar is not logged in. */
@ -118,7 +116,7 @@ public final class FlowUtils {
/** IP address version mismatch. */ /** IP address version mismatch. */
public static class IpAddressVersionMismatchException extends ParameterValueRangeErrorException { public static class IpAddressVersionMismatchException extends ParameterValueRangeErrorException {
public IpAddressVersionMismatchException() { public IpAddressVersionMismatchException() {
super("IP adddress version mismatch"); super("IP address version mismatch");
} }
} }

View file

@ -20,17 +20,15 @@ import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.ParameterValuePolicyErrorException;
import google.registry.flows.EppException.ParameterValueSyntaxErrorException; import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.contact.PostalInfo; import google.registry.model.contact.PostalInfo;
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse.ContactTransferResponse; import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
import java.util.Set; import java.util.Set;
@ -69,10 +67,7 @@ public class ContactFlowUtils {
/** Create a poll message for the gaining client in a transfer. */ /** Create a poll message for the gaining client in a transfer. */
static PollMessage createGainingTransferPollMessage( static PollMessage createGainingTransferPollMessage(
String targetId, String targetId, TransferData transferData, DateTime now, HistoryEntryId contactHistoryId) {
TransferData transferData,
DateTime now,
Key<ContactHistory> contactHistoryKey) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setRegistrarId(transferData.getGainingRegistrarId()) .setRegistrarId(transferData.getGainingRegistrarId())
.setEventTime(transferData.getPendingTransferExpirationTime()) .setEventTime(transferData.getPendingTransferExpirationTime())
@ -85,23 +80,19 @@ public class ContactFlowUtils {
transferData.getTransferStatus().isApproved(), transferData.getTransferStatus().isApproved(),
transferData.getTransferRequestTrid(), transferData.getTransferRequestTrid(),
now))) now)))
.setContactHistoryId( .setContactHistoryId(contactHistoryId)
new ContactHistoryId(
contactHistoryKey.getParent().getName(), contactHistoryKey.getId()))
.build(); .build();
} }
/** Create a poll message for the losing client in a transfer. */ /** Create a poll message for the losing client in a transfer. */
static PollMessage createLosingTransferPollMessage( static PollMessage createLosingTransferPollMessage(
String targetId, TransferData transferData, Key<ContactHistory> contactHistoryKey) { String targetId, TransferData transferData, HistoryEntryId contactHistoryId) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setRegistrarId(transferData.getLosingRegistrarId()) .setRegistrarId(transferData.getLosingRegistrarId())
.setEventTime(transferData.getPendingTransferExpirationTime()) .setEventTime(transferData.getPendingTransferExpirationTime())
.setMsg(transferData.getTransferStatus().getMessage()) .setMsg(transferData.getTransferStatus().getMessage())
.setResponseData(ImmutableList.of(createTransferResponse(targetId, transferData))) .setResponseData(ImmutableList.of(createTransferResponse(targetId, transferData)))
.setContactHistoryId( .setContactHistoryId(contactHistoryId)
new ContactHistoryId(
contactHistoryKey.getParent().getName(), contactHistoryKey.getId()))
.build(); .build();
} }

View file

@ -26,7 +26,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_TRANSFER
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -93,7 +92,7 @@ public final class ContactTransferApproveFlow implements TransactionalFlow {
// Create a poll message for the gaining client. // Create a poll message for the gaining client.
PollMessage gainingPollMessage = PollMessage gainingPollMessage =
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, newContact.getTransferData(), now, Key.create(contactHistory)); targetId, newContact.getTransferData(), now, contactHistory.getHistoryEntryId());
tm().insertAll(ImmutableSet.of(contactHistory, gainingPollMessage)); tm().insertAll(ImmutableSet.of(contactHistory, gainingPollMessage));
tm().update(newContact); tm().update(newContact);
// Delete the billing event and poll messages that were written in case the transfer would have // Delete the billing event and poll messages that were written in case the transfer would have

View file

@ -26,7 +26,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_TRANSFER
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -73,7 +72,7 @@ public final class ContactTransferCancelFlow implements TransactionalFlow {
@Inject ContactTransferCancelFlow() {} @Inject ContactTransferCancelFlow() {}
@Override @Override
public final EppResponse run() throws EppException { public EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
validateRegistrarIsLoggedIn(registrarId); validateRegistrarIsLoggedIn(registrarId);
extensionManager.validate(); extensionManager.validate();
@ -89,7 +88,7 @@ public final class ContactTransferCancelFlow implements TransactionalFlow {
// Create a poll message for the losing client. // Create a poll message for the losing client.
PollMessage losingPollMessage = PollMessage losingPollMessage =
createLosingTransferPollMessage( createLosingTransferPollMessage(
targetId, newContact.getTransferData(), Key.create(contactHistory)); targetId, newContact.getTransferData(), contactHistory.getHistoryEntryId());
tm().insertAll(ImmutableSet.of(contactHistory, losingPollMessage)); tm().insertAll(ImmutableSet.of(contactHistory, losingPollMessage));
tm().update(newContact); tm().update(newContact);
// Delete the billing event and poll messages that were written in case the transfer would have // Delete the billing event and poll messages that were written in case the transfer would have

View file

@ -26,7 +26,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_TRANSFER
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -86,7 +85,7 @@ public final class ContactTransferRejectFlow implements TransactionalFlow {
historyBuilder.setType(CONTACT_TRANSFER_REJECT).setContact(newContact).build(); historyBuilder.setType(CONTACT_TRANSFER_REJECT).setContact(newContact).build();
PollMessage gainingPollMessage = PollMessage gainingPollMessage =
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, newContact.getTransferData(), now, Key.create(contactHistory)); targetId, newContact.getTransferData(), now, contactHistory.getHistoryEntryId());
tm().insertAll(ImmutableSet.of(contactHistory, gainingPollMessage)); tm().insertAll(ImmutableSet.of(contactHistory, gainingPollMessage));
tm().update(newContact); tm().update(newContact);
// Delete the billing event and poll messages that were written in case the transfer would have // Delete the billing event and poll messages that were written in case the transfer would have

View file

@ -14,7 +14,7 @@
package google.registry.flows.contact; package google.registry.flows.contact;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyAuthInfo; import static google.registry.flows.ResourceFlowUtils.verifyAuthInfo;
@ -28,7 +28,6 @@ import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_TRANSFER
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -46,6 +45,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.ContactTransferData; import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
@ -74,21 +74,27 @@ import org.joda.time.Duration;
@ReportingSpec(ActivityReportField.CONTACT_TRANSFER_REQUEST) @ReportingSpec(ActivityReportField.CONTACT_TRANSFER_REQUEST)
public final class ContactTransferRequestFlow implements TransactionalFlow { public final class ContactTransferRequestFlow implements TransactionalFlow {
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of( private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES =
StatusValue.CLIENT_TRANSFER_PROHIBITED, ImmutableSet.of(
StatusValue.PENDING_DELETE, StatusValue.CLIENT_TRANSFER_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED); StatusValue.PENDING_DELETE,
StatusValue.SERVER_TRANSFER_PROHIBITED);
@Inject ExtensionManager extensionManager; @Inject ExtensionManager extensionManager;
@Inject Optional<AuthInfo> authInfo; @Inject Optional<AuthInfo> authInfo;
@Inject @RegistrarId String gainingClientId; @Inject @RegistrarId String gainingClientId;
@Inject @TargetId String targetId; @Inject @TargetId String targetId;
@Inject @Config("contactAutomaticTransferLength") Duration automaticTransferLength;
@Inject
@Config("contactAutomaticTransferLength")
Duration automaticTransferLength;
@Inject ContactHistory.Builder historyBuilder; @Inject ContactHistory.Builder historyBuilder;
@Inject Trid trid; @Inject Trid trid;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject ContactTransferRequestFlow() {}
@Inject
ContactTransferRequestFlow() {}
@Override @Override
public EppResponse run() throws EppException { public EppResponse run() throws EppException {
@ -120,29 +126,31 @@ public final class ContactTransferRequestFlow implements TransactionalFlow {
.setPendingTransferExpirationTime(transferExpirationTime) .setPendingTransferExpirationTime(transferExpirationTime)
.setTransferStatus(TransferStatus.SERVER_APPROVED) .setTransferStatus(TransferStatus.SERVER_APPROVED)
.build(); .build();
Key<ContactHistory> contactHistoryKey = createHistoryKey(existingContact, ContactHistory.class); HistoryEntryId contactHistoryId = createHistoryEntryId(existingContact);
historyBuilder.setId(contactHistoryKey.getId()).setType(CONTACT_TRANSFER_REQUEST); historyBuilder
.setRevisionId(contactHistoryId.getRevisionId())
.setType(CONTACT_TRANSFER_REQUEST);
// If the transfer is server approved, this message will be sent to the losing registrar. */ // If the transfer is server approved, this message will be sent to the losing registrar. */
PollMessage serverApproveLosingPollMessage = PollMessage serverApproveLosingPollMessage =
createLosingTransferPollMessage(targetId, serverApproveTransferData, contactHistoryKey); createLosingTransferPollMessage(targetId, serverApproveTransferData, contactHistoryId);
// If the transfer is server approved, this message will be sent to the gaining registrar. */ // If the transfer is server approved, this message will be sent to the gaining registrar. */
PollMessage serverApproveGainingPollMessage = PollMessage serverApproveGainingPollMessage =
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, serverApproveTransferData, now, contactHistoryKey); targetId, serverApproveTransferData, now, contactHistoryId);
ContactTransferData pendingTransferData = ContactTransferData pendingTransferData =
serverApproveTransferData serverApproveTransferData
.asBuilder() .asBuilder()
.setTransferStatus(TransferStatus.PENDING) .setTransferStatus(TransferStatus.PENDING)
.setServerApproveEntities( .setServerApproveEntities(
serverApproveGainingPollMessage.getContactRepoId(), serverApproveGainingPollMessage.getContactRepoId(),
contactHistoryKey.getId(), contactHistoryId.getRevisionId(),
ImmutableSet.of( ImmutableSet.of(
serverApproveGainingPollMessage.createVKey(), serverApproveGainingPollMessage.createVKey(),
serverApproveLosingPollMessage.createVKey())) serverApproveLosingPollMessage.createVKey()))
.build(); .build();
// When a transfer is requested, a poll message is created to notify the losing registrar. // When a transfer is requested, a poll message is created to notify the losing registrar.
PollMessage requestPollMessage = PollMessage requestPollMessage =
createLosingTransferPollMessage(targetId, pendingTransferData, contactHistoryKey) createLosingTransferPollMessage(targetId, pendingTransferData, contactHistoryId)
.asBuilder() .asBuilder()
.setEventTime(now) // Unlike the serverApprove messages, this applies immediately. .setEventTime(now) // Unlike the serverApprove messages, this applies immediately.
.build(); .build();
@ -165,4 +173,3 @@ public final class ContactTransferRequestFlow implements TransactionalFlow {
.build(); .build();
} }
} }

View file

@ -86,7 +86,6 @@ import google.registry.model.domain.Domain;
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.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
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;
@ -110,6 +109,7 @@ import google.registry.model.poll.PollMessage.Autorenew;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState; import google.registry.model.tld.Registry.TldState;
@ -327,14 +327,14 @@ public final class DomainCreateFlow implements TransactionalFlow {
FeesAndCredits feesAndCredits = FeesAndCredits feesAndCredits =
pricingLogic.getCreatePrice( pricingLogic.getCreatePrice(
registry, targetId, now, years, isAnchorTenant, allocationToken); registry, targetId, now, years, isAnchorTenant, allocationToken);
validateFeeChallenge(targetId, now, feeCreate, feesAndCredits); validateFeeChallenge(feeCreate, feesAndCredits);
Optional<SecDnsCreateExtension> secDnsCreate = Optional<SecDnsCreateExtension> secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
DateTime registrationExpirationTime = leapSafeAddYears(now, years); DateTime registrationExpirationTime = leapSafeAddYears(now, years);
String repoId = createDomainRepoId(allocateId(), registry.getTldStr()); String repoId = createDomainRepoId(allocateId(), registry.getTldStr());
long historyRevisionId = allocateId(); long historyRevisionId = allocateId();
DomainHistoryId domainHistoryId = new DomainHistoryId(repoId, historyRevisionId); HistoryEntryId domainHistoryId = new HistoryEntryId(repoId, historyRevisionId);
historyBuilder.setId(historyRevisionId); historyBuilder.setRevisionId(historyRevisionId);
// Bill for the create. // Bill for the create.
BillingEvent.OneTime createBillingEvent = BillingEvent.OneTime createBillingEvent =
createOneTimeBillingEvent( createOneTimeBillingEvent(
@ -406,7 +406,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
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(allocationToken.get(), domainHistory.createVKey())); allocationTokenFlowUtils.redeemToken(
allocationToken.get(), domainHistory.getHistoryEntryId()));
} }
enqueueTasks(domain, hasSignedMarks, hasClaimsNotice); enqueueTasks(domain, hasSignedMarks, hasClaimsNotice);
@ -562,7 +563,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
boolean isReserved, boolean isReserved,
int years, int years,
FeesAndCredits feesAndCredits, FeesAndCredits feesAndCredits,
DomainHistoryId domainHistoryId, HistoryEntryId domainHistoryId,
Optional<AllocationToken> allocationToken, Optional<AllocationToken> allocationToken,
DateTime now) { DateTime now) {
ImmutableSet.Builder<Flag> flagsBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder<Flag> flagsBuilder = new ImmutableSet.Builder<>();
@ -596,7 +597,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
} }
private Recurring createAutorenewBillingEvent( private Recurring createAutorenewBillingEvent(
DomainHistoryId domainHistoryId, HistoryEntryId domainHistoryId,
DateTime registrationExpirationTime, DateTime registrationExpirationTime,
RenewalPriceInfo renewalpriceInfo) { RenewalPriceInfo renewalpriceInfo) {
return new BillingEvent.Recurring.Builder() return new BillingEvent.Recurring.Builder()
@ -613,7 +614,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
} }
private Autorenew createAutorenewPollMessage( private Autorenew createAutorenewPollMessage(
DomainHistoryId domainHistoryId, DateTime registrationExpirationTime) { HistoryEntryId domainHistoryId, DateTime registrationExpirationTime) {
return new PollMessage.Autorenew.Builder() return new PollMessage.Autorenew.Builder()
.setTargetId(targetId) .setTargetId(targetId)
.setRegistrarId(registrarId) .setRegistrarId(registrarId)
@ -634,7 +635,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
.setEventTime(createBillingEvent.getEventTime()) .setEventTime(createBillingEvent.getEventTime())
.setBillingTime(createBillingEvent.getBillingTime()) .setBillingTime(createBillingEvent.getBillingTime())
.setFlags(createBillingEvent.getFlags()) .setFlags(createBillingEvent.getFlags())
.setDomainHistoryId(createBillingEvent.getDomainHistoryId()) .setDomainHistoryId(createBillingEvent.getHistoryEntryId())
.build(); .build();
} }
@ -674,11 +675,11 @@ public final class DomainCreateFlow implements TransactionalFlow {
Optional<AllocationToken> allocationToken, Optional<AllocationToken> allocationToken,
FeesAndCredits feesAndCredits) { FeesAndCredits feesAndCredits) {
if (isAnchorTenant) { if (isAnchorTenant) {
if (allocationToken.isPresent()) { allocationToken.ifPresent(
checkArgument( token ->
allocationToken.get().getRenewalPriceBehavior() != RenewalPriceBehavior.SPECIFIED, checkArgument(
"Renewal price behavior cannot be SPECIFIED for anchor tenant"); token.getRenewalPriceBehavior() != RenewalPriceBehavior.SPECIFIED,
} "Renewal price behavior cannot be SPECIFIED for anchor tenant"));
return RenewalPriceInfo.create(RenewalPriceBehavior.NONPREMIUM, null); return RenewalPriceInfo.create(RenewalPriceBehavior.NONPREMIUM, null);
} else if (allocationToken.isPresent() } else if (allocationToken.isPresent()
&& allocationToken.get().getRenewalPriceBehavior() == RenewalPriceBehavior.SPECIFIED) { && allocationToken.get().getRenewalPriceBehavior() == RenewalPriceBehavior.SPECIFIED) {
@ -705,9 +706,12 @@ public final class DomainCreateFlow implements TransactionalFlow {
private static ImmutableList<FeeTransformResponseExtension> createResponseExtensions( private static ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
Optional<FeeCreateCommandExtension> feeCreate, FeesAndCredits feesAndCredits) { Optional<FeeCreateCommandExtension> feeCreate, FeesAndCredits feesAndCredits) {
return feeCreate.isPresent() return feeCreate
? ImmutableList.of(createFeeCreateResponse(feeCreate.get(), feesAndCredits)) .map(
: ImmutableList.of(); feeCreateCommandExtension ->
ImmutableList.of(
createFeeCreateResponse(feeCreateCommandExtension, feesAndCredits)))
.orElseGet(ImmutableList::of);
} }
/** Signed marks are only allowed during sunrise. */ /** Signed marks are only allowed during sunrise. */

View file

@ -16,7 +16,7 @@ package google.registry.flows.domain;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.persistEntityChanges; import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@ -43,7 +43,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.batch.AsyncTaskEnqueuer; import google.registry.batch.AsyncTaskEnqueuer;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -66,7 +65,6 @@ import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.Credit;
@ -88,6 +86,7 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldType; import google.registry.model.tld.Registry.TldType;
@ -181,8 +180,8 @@ public final class DomainDeleteFlow implements TransactionalFlow {
? Duration.ZERO ? Duration.ZERO
// By default, this should be 30 days of grace, and 5 days of pending delete. // By default, this should be 30 days of grace, and 5 days of pending delete.
: redemptionGracePeriodLength.plus(pendingDeleteLength); : redemptionGracePeriodLength.plus(pendingDeleteLength);
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder.setId(domainHistoryKey.getId()); historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
DateTime deletionTime = now.plus(durationUntilDelete); DateTime deletionTime = now.plus(durationUntilDelete);
if (durationUntilDelete.equals(Duration.ZERO)) { if (durationUntilDelete.equals(Duration.ZERO)) {
builder.setDeletionTime(now).setStatusValues(null); builder.setDeletionTime(now).setStatusValues(null);
@ -214,7 +213,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
// it is synchronous). // it is synchronous).
if (durationUntilDelete.isLongerThan(Duration.ZERO) || isSuperuser) { if (durationUntilDelete.isLongerThan(Duration.ZERO) || isSuperuser) {
PollMessage.OneTime deletePollMessage = PollMessage.OneTime deletePollMessage =
createDeletePollMessage(existingDomain, domainHistoryKey, deletionTime); createDeletePollMessage(existingDomain, domainHistoryId, deletionTime);
entitiesToSave.add(deletePollMessage); entitiesToSave.add(deletePollMessage);
builder.setDeletePollMessage(deletePollMessage.createVKey()); builder.setDeletePollMessage(deletePollMessage.createVKey());
} }
@ -224,7 +223,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
if (durationUntilDelete.isLongerThan(Duration.ZERO) if (durationUntilDelete.isLongerThan(Duration.ZERO)
&& !registrarId.equals(existingDomain.getPersistedCurrentSponsorRegistrarId())) { && !registrarId.equals(existingDomain.getPersistedCurrentSponsorRegistrarId())) {
entitiesToSave.add( entitiesToSave.add(
createImmediateDeletePollMessage(existingDomain, domainHistoryKey, now, deletionTime)); createImmediateDeletePollMessage(existingDomain, domainHistoryId, now, deletionTime));
} }
// Cancel any grace periods that were still active, and set the expiration time accordingly. // Cancel any grace periods that were still active, and set the expiration time accordingly.
@ -233,14 +232,9 @@ public final class DomainDeleteFlow implements TransactionalFlow {
// No cancellation is written if the grace period was not for a billable event. // No cancellation is written if the grace period was not for a billable event.
if (gracePeriod.hasBillingEvent()) { if (gracePeriod.hasBillingEvent()) {
entitiesToSave.add( entitiesToSave.add(
BillingEvent.Cancellation.forGracePeriod( BillingEvent.Cancellation.forGracePeriod(gracePeriod, now, domainHistoryId, targetId));
gracePeriod,
now,
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()),
targetId));
if (gracePeriod.getOneTimeBillingEvent() != null) { if (gracePeriod.getOneTimeBillingEvent() != null) {
// Take the amount of amount of registration time being refunded off the expiration time. // Take the amount of registration time being refunded off the expiration time.
// This can be either add grace periods or renew grace periods. // This can be either add grace periods or renew grace periods.
BillingEvent.OneTime oneTime = tm().loadByKey(gracePeriod.getOneTimeBillingEvent()); BillingEvent.OneTime oneTime = tm().loadByKey(gracePeriod.getOneTimeBillingEvent());
newExpirationTime = newExpirationTime.minusYears(oneTime.getPeriodYears()); newExpirationTime = newExpirationTime.minusYears(oneTime.getPeriodYears());
@ -262,7 +256,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
BillingEvent.Recurring recurringBillingEvent = BillingEvent.Recurring recurringBillingEvent =
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, now, domainHistory.getDomainHistoryId()); existingDomain, existingRecurring, now, domainHistory.getHistoryEntryId());
// If there's a pending transfer, the gaining client's autorenew billing // If there's a pending transfer, the gaining client's autorenew billing
// event and poll message will already have been deleted in // event and poll message will already have been deleted in
// ResourceDeleteFlow since it's listed in serverApproveEntities. // ResourceDeleteFlow since it's listed in serverApproveEntities.
@ -343,7 +337,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
} }
private PollMessage.OneTime createDeletePollMessage( private PollMessage.OneTime createDeletePollMessage(
Domain existingDomain, Key<DomainHistory> domainHistoryKey, DateTime deletionTime) { Domain existingDomain, HistoryEntryId domainHistoryId, DateTime deletionTime) {
Optional<MetadataExtension> metadataExtension = Optional<MetadataExtension> metadataExtension =
eppInput.getSingleExtension(MetadataExtension.class); eppInput.getSingleExtension(MetadataExtension.class);
boolean hasMetadataMessage = boolean hasMetadataMessage =
@ -362,21 +356,16 @@ public final class DomainDeleteFlow implements TransactionalFlow {
ImmutableList.of( ImmutableList.of(
DomainPendingActionNotificationResponse.create( DomainPendingActionNotificationResponse.create(
existingDomain.getDomainName(), true, trid, deletionTime))) existingDomain.getDomainName(), true, trid, deletionTime)))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
private PollMessage.OneTime createImmediateDeletePollMessage( private PollMessage.OneTime createImmediateDeletePollMessage(
Domain existingDomain, Domain existingDomain, HistoryEntryId domainHistoryId, DateTime now, DateTime deletionTime) {
Key<DomainHistory> domainHistoryKey,
DateTime now,
DateTime deletionTime) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setRegistrarId(existingDomain.getPersistedCurrentSponsorRegistrarId()) .setRegistrarId(existingDomain.getPersistedCurrentSponsorRegistrarId())
.setEventTime(now) .setEventTime(now)
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.setMsg( .setMsg(
String.format( String.format(
"Domain %s was deleted by registry administrator with final deletion effective: %s", "Domain %s was deleted by registry administrator with final deletion effective: %s",

View file

@ -88,7 +88,6 @@ import google.registry.model.domain.DomainCommand.CreateOrUpdate;
import google.registry.model.domain.DomainCommand.InvalidReferencesException; import google.registry.model.domain.DomainCommand.InvalidReferencesException;
import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainCommand.Update;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.ForeignKeyedDesignatedContact; import google.registry.model.domain.ForeignKeyedDesignatedContact;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.fee.BaseFee; import google.registry.model.domain.fee.BaseFee;
@ -121,7 +120,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.Registrar.State;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState; import google.registry.model.tld.Registry.TldState;
import google.registry.model.tld.Registry.TldType; import google.registry.model.tld.Registry.TldType;
@ -429,7 +428,7 @@ public class DomainFlowUtils {
contacts.stream() contacts.stream()
.collect( .collect(
toImmutableSetMultimap( toImmutableSetMultimap(
DesignatedContact::getType, contact -> contact.getContactKey())); DesignatedContact::getType, DesignatedContact::getContactKey));
// If any contact type has multiple contacts: // If any contact type has multiple contacts:
if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) { if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) {
@ -590,7 +589,7 @@ public class DomainFlowUtils {
Domain domain, Domain domain,
Recurring existingRecurring, Recurring existingRecurring,
DateTime newEndTime, DateTime newEndTime,
@Nullable DomainHistoryId historyId) { @Nullable HistoryEntryId historyId) {
Optional<PollMessage.Autorenew> autorenewPollMessage = Optional<PollMessage.Autorenew> autorenewPollMessage =
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage()); tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
@ -773,8 +772,6 @@ public class DomainFlowUtils {
* domain names. * domain names.
*/ */
public static void validateFeeChallenge( public static void validateFeeChallenge(
String domainName,
DateTime priceTime,
final Optional<? extends FeeTransformCommandExtension> feeCommand, final Optional<? extends FeeTransformCommandExtension> feeCommand,
FeesAndCredits feesAndCredits) FeesAndCredits feesAndCredits)
throws EppException { throws EppException {
@ -1135,9 +1132,9 @@ public class DomainFlowUtils {
Duration maxSearchPeriod, Duration maxSearchPeriod,
final ImmutableSet<TransactionReportField> cancelableFields) { final ImmutableSet<TransactionReportField> cancelableFields) {
List<? extends HistoryEntry> recentHistoryEntries = List<DomainHistory> recentHistoryEntries =
findRecentHistoryEntries(domain, now, maxSearchPeriod); findRecentHistoryEntries(domain, now, maxSearchPeriod);
Optional<? extends HistoryEntry> entryToCancel = Optional<DomainHistory> entryToCancel =
Streams.findLast( Streams.findLast(
recentHistoryEntries.stream() recentHistoryEntries.stream()
.filter( .filter(
@ -1174,11 +1171,11 @@ public class DomainFlowUtils {
return recordsBuilder.build(); return recordsBuilder.build();
} }
private static List<? extends HistoryEntry> findRecentHistoryEntries( private static List<DomainHistory> findRecentHistoryEntries(
Domain domain, DateTime now, Duration maxSearchPeriod) { Domain domain, DateTime now, Duration maxSearchPeriod) {
return jpaTm() return jpaTm()
.query( .query(
"FROM DomainHistory WHERE modificationTime >= :beginning AND domainRepoId = " "FROM DomainHistory WHERE modificationTime >= :beginning AND repoId = "
+ ":repoId ORDER BY modificationTime ASC", + ":repoId ORDER BY modificationTime ASC",
DomainHistory.class) DomainHistory.class)
.setParameter("beginning", now.minus(maxSearchPeriod)) .setParameter("beginning", now.minus(maxSearchPeriod))

View file

@ -14,7 +14,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.persistEntityChanges; import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@ -38,7 +38,6 @@ import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.ParameterValueRangeErrorException; import google.registry.flows.EppException.ParameterValueRangeErrorException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -62,7 +61,6 @@ import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Renew; import google.registry.model.domain.DomainCommand.Renew;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.DomainRenewData; import google.registry.model.domain.DomainRenewData;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
@ -83,6 +81,7 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import java.util.Optional; import java.util.Optional;
@ -100,7 +99,7 @@ import org.joda.time.Duration;
* *
* <p>ICANN prohibits any registration from being longer than ten years so if the request would * <p>ICANN prohibits any registration from being longer than ten years so if the request would
* result in a registration greater than ten years long it will fail. In practice this means it's * result in a registration greater than ten years long it will fail. In practice this means it's
* impossible to request a ten year renewal, since that will always cause the new registration to be * impossible to request a ten-year renewal, since that will always cause the new registration to be
* longer than 10 years unless it comes in at the exact millisecond that the domain would have * longer than 10 years unless it comes in at the exact millisecond that the domain would have
* expired. * expired.
* *
@ -200,44 +199,36 @@ public final class DomainRenewFlow implements TransactionalFlow {
now, now,
years, years,
existingRecurringBillingEvent); existingRecurringBillingEvent);
validateFeeChallenge(targetId, now, feeRenew, feesAndCredits); validateFeeChallenge(feeRenew, feesAndCredits);
flowCustomLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder() AfterValidationParameters.newBuilder()
.setExistingDomain(existingDomain) .setExistingDomain(existingDomain)
.setNow(now) .setNow(now)
.setYears(years) .setYears(years)
.build()); .build());
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder.setId(domainHistoryKey.getId()); historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
String tld = existingDomain.getTld(); String tld = existingDomain.getTld();
// Bill for this explicit renew itself. // Bill for this explicit renew itself.
BillingEvent.OneTime explicitRenewEvent = BillingEvent.OneTime explicitRenewEvent =
createRenewBillingEvent( createRenewBillingEvent(
tld, feesAndCredits.getTotalCost(), years, domainHistoryKey, allocationToken, now); tld, feesAndCredits.getTotalCost(), years, domainHistoryId, allocationToken, now);
// Create a new autorenew billing event and poll message starting at the new expiration time. // Create a new autorenew billing event and poll message starting at the new expiration time.
BillingEvent.Recurring newAutorenewEvent = BillingEvent.Recurring newAutorenewEvent =
newAutorenewBillingEvent(existingDomain) newAutorenewBillingEvent(existingDomain)
.setEventTime(newExpirationTime) .setEventTime(newExpirationTime)
.setRenewalPrice(existingRecurringBillingEvent.getRenewalPrice().orElse(null)) .setRenewalPrice(existingRecurringBillingEvent.getRenewalPrice().orElse(null))
.setRenewalPriceBehavior(existingRecurringBillingEvent.getRenewalPriceBehavior()) .setRenewalPriceBehavior(existingRecurringBillingEvent.getRenewalPriceBehavior())
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
PollMessage.Autorenew newAutorenewPollMessage = PollMessage.Autorenew newAutorenewPollMessage =
newAutorenewPollMessage(existingDomain) newAutorenewPollMessage(existingDomain)
.setEventTime(newExpirationTime) .setEventTime(newExpirationTime)
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
// End the old autorenew billing event and poll message now. This may delete the poll message. // End the old autorenew billing event and poll message now. This may delete the poll message.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now, domainHistoryId);
existingDomain,
existingRecurring,
now,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
Domain newDomain = Domain newDomain =
existingDomain existingDomain
.asBuilder() .asBuilder()
@ -260,7 +251,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
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(allocationToken.get(), domainHistory.createVKey())); allocationTokenFlowUtils.redeemToken(
allocationToken.get(), domainHistory.getHistoryEntryId()));
} }
EntityChanges entityChanges = EntityChanges entityChanges =
flowCustomLogic.beforeSave( flowCustomLogic.beforeSave(
@ -339,7 +331,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
String tld, String tld,
Money renewCost, Money renewCost,
int years, int years,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
Optional<AllocationToken> allocationToken, Optional<AllocationToken> allocationToken,
DateTime now) { DateTime now) {
return new BillingEvent.OneTime.Builder() return new BillingEvent.OneTime.Builder()
@ -355,27 +347,27 @@ public final class DomainRenewFlow implements TransactionalFlow {
.map(AllocationToken::createVKey) .map(AllocationToken::createVKey)
.orElse(null)) .orElse(null))
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength())) .setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
private ImmutableList<FeeTransformResponseExtension> createResponseExtensions( private ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
FeesAndCredits feesAndCredits, Optional<FeeRenewCommandExtension> feeRenew) { FeesAndCredits feesAndCredits, Optional<FeeRenewCommandExtension> feeRenew) {
return feeRenew.isPresent() return feeRenew
? ImmutableList.of( .map(
feeRenew feeRenewCommandExtension ->
.get() ImmutableList.of(
.createResponseBuilder() feeRenewCommandExtension
.setCurrency(feesAndCredits.getCurrency()) .createResponseBuilder()
.setFees( .setCurrency(feesAndCredits.getCurrency())
ImmutableList.of( .setFees(
Fee.create( ImmutableList.of(
feesAndCredits.getRenewCost().getAmount(), Fee.create(
FeeType.RENEW, feesAndCredits.getRenewCost().getAmount(),
feesAndCredits.hasPremiumFeesOfType(FeeType.RENEW)))) FeeType.RENEW,
.build()) feesAndCredits.hasPremiumFeesOfType(FeeType.RENEW))))
: ImmutableList.of(); .build()))
.orElseGet(ImmutableList::of);
} }
/** The current expiration date is incorrect. */ /** The current expiration date is incorrect. */

View file

@ -14,7 +14,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
@ -34,7 +34,6 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.CommandUseErrorException;
@ -52,7 +51,6 @@ import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainCommand.Update;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension;
@ -67,6 +65,7 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import java.util.Optional; import java.util.Optional;
@ -85,12 +84,12 @@ import org.joda.time.DateTime;
* *
* <p>This flow is called a restore "request" because technically it is only supposed to signal that * <p>This flow is called a restore "request" because technically it is only supposed to signal that
* the registrar requests the restore, which the registry can choose to process or not based on a * the registrar requests the restore, which the registry can choose to process or not based on a
* restore report that is submitted through an out of band process and details the request. However, * restore report that is submitted through an out-of-band process and details the request. However,
* in practice this flow does the restore immediately. This is allowable because all of the fields * in practice this flow does the restore immediately. This is allowable because all the fields on a
* on a restore report are optional or have default values, and so by policy when the request comes * restore report are optional or have default values, and so by policy when the request comes in we
* in we consider it to have been accompanied by a default-initialized report which we auto-approve. * consider it to have been accompanied by a default-initialized report which we auto-approve.
* *
* <p>Restores cost a fixed restore fee plus a one year renewal fee for the domain. The domain is * <p>Restores cost a fixed restore fee plus a one-year renewal fee for the domain. The domain is
* restored to a single year expiration starting at the restore time, regardless of what the * restored to a single year expiration starting at the restore time, regardless of what the
* original expiration time was. * original expiration time was.
* *
@ -147,10 +146,8 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
Optional<FeeUpdateCommandExtension> feeUpdate = Optional<FeeUpdateCommandExtension> feeUpdate =
eppInput.getSingleExtension(FeeUpdateCommandExtension.class); eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
verifyRestoreAllowed(command, existingDomain, feeUpdate, feesAndCredits, now); verifyRestoreAllowed(command, existingDomain, feeUpdate, feesAndCredits, now);
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder.setId(domainHistoryKey.getId()); historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
DomainHistoryId domainHistoryId =
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId());
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>(); ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
DateTime newExpirationTime = DateTime newExpirationTime =
@ -175,9 +172,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
newAutorenewPollMessage(existingDomain) newAutorenewPollMessage(existingDomain)
.setEventTime(newExpirationTime) .setEventTime(newExpirationTime)
.setAutorenewEndTime(END_OF_TIME) .setAutorenewEndTime(END_OF_TIME)
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
Domain newDomain = Domain newDomain =
performRestore( performRestore(
@ -231,7 +226,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) { if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) {
throw new DomainNotEligibleForRestoreException(); throw new DomainNotEligibleForRestoreException();
} }
validateFeeChallenge(targetId, now, feeUpdate, feesAndCredits); validateFeeChallenge(feeUpdate, feesAndCredits);
} }
private static Domain performRestore( private static Domain performRestore(
@ -259,17 +254,17 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
} }
private OneTime createRenewBillingEvent( private OneTime createRenewBillingEvent(
DomainHistoryId domainHistoryId, Money renewCost, DateTime now) { HistoryEntryId domainHistoryId, Money renewCost, DateTime now) {
return prepareBillingEvent(domainHistoryId, renewCost, now).setReason(Reason.RENEW).build(); return prepareBillingEvent(domainHistoryId, renewCost, now).setReason(Reason.RENEW).build();
} }
private BillingEvent.OneTime createRestoreBillingEvent( private BillingEvent.OneTime createRestoreBillingEvent(
DomainHistoryId domainHistoryId, Money restoreCost, DateTime now) { HistoryEntryId domainHistoryId, Money restoreCost, DateTime now) {
return prepareBillingEvent(domainHistoryId, restoreCost, now).setReason(Reason.RESTORE).build(); return prepareBillingEvent(domainHistoryId, restoreCost, now).setReason(Reason.RESTORE).build();
} }
private OneTime.Builder prepareBillingEvent( private OneTime.Builder prepareBillingEvent(
DomainHistoryId domainHistoryId, Money cost, DateTime now) { HistoryEntryId domainHistoryId, Money cost, DateTime now) {
return new BillingEvent.OneTime.Builder() return new BillingEvent.OneTime.Builder()
.setTargetId(targetId) .setTargetId(targetId)
.setRegistrarId(registrarId) .setRegistrarId(registrarId)
@ -297,15 +292,16 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
FeeType.RENEW, FeeType.RENEW,
feesAndCredits.hasPremiumFeesOfType(FeeType.RENEW))); feesAndCredits.hasPremiumFeesOfType(FeeType.RENEW)));
} }
return feeUpdate.isPresent() return feeUpdate
? ImmutableList.of( .map(
feeUpdate feeUpdateCommandExtension ->
.get() ImmutableList.of(
.createResponseBuilder() feeUpdateCommandExtension
.setCurrency(feesAndCredits.getCurrency()) .createResponseBuilder()
.setFees(fees.build()) .setCurrency(feesAndCredits.getCurrency())
.build()) .setFees(fees.build())
: ImmutableList.of(); .build()))
.orElseGet(ImmutableList::of);
} }
/** Restore command cannot have other changes specified. */ /** Restore command cannot have other changes specified. */

View file

@ -15,7 +15,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.computeExDateForApprovalTime; import static google.registry.flows.ResourceFlowUtils.computeExDateForApprovalTime;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@ -36,7 +36,6 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -52,7 +51,6 @@ import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
@ -62,6 +60,7 @@ import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.DomainTransferData;
@ -146,8 +145,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
// Create a transfer billing event for 1 year, unless the superuser extension was used to set // Create a transfer billing event for 1 year, unless the superuser extension was used to set
// the transfer period to zero. There is not a transfer cost if the transfer period is zero. // the transfer period to zero. There is not a transfer cost if the transfer period is zero.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder.setId(domainHistoryKey.getId()); historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
Optional<BillingEvent.OneTime> billingEvent = Optional<BillingEvent.OneTime> billingEvent =
transferData.getTransferPeriod().getValue() == 0 transferData.getTransferPeriod().getValue() == 0
? Optional.empty() ? Optional.empty()
@ -167,9 +166,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.getRenewCost()) .getRenewCost())
.setEventTime(now) .setEventTime(now)
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength())) .setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build()); .build());
ImmutableList.Builder<ImmutableObject> entitiesToSave = new ImmutableList.Builder<>(); ImmutableList.Builder<ImmutableObject> entitiesToSave = new ImmutableList.Builder<>();
// If we are within an autorenew grace period, cancel the autorenew billing event and don't // If we are within an autorenew grace period, cancel the autorenew billing event and don't
@ -185,20 +182,12 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
if (billingEvent.isPresent()) { if (billingEvent.isPresent()) {
entitiesToSave.add( entitiesToSave.add(
BillingEvent.Cancellation.forGracePeriod( BillingEvent.Cancellation.forGracePeriod(
autorenewGrace, autorenewGrace, now, domainHistoryId, targetId));
now,
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()),
targetId));
} }
} }
// Close the old autorenew event and poll message at the transfer time (aka now). This may end // Close the old autorenew event and poll message at the transfer time (aka now). This may end
// up deleting the poll message. // up deleting the poll message.
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(existingDomain, existingRecurring, now, domainHistoryId);
existingDomain,
existingRecurring,
now,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
DateTime newExpirationTime = DateTime newExpirationTime =
computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod()); computeExDateForApprovalTime(existingDomain, now, transferData.getTransferPeriod());
// Create a new autorenew event starting at the expiration time. // Create a new autorenew event starting at the expiration time.
@ -212,9 +201,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior()) .setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null)) .setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
.setRecurrenceEndTime(END_OF_TIME) .setRecurrenceEndTime(END_OF_TIME)
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
// Create a new autorenew poll message. // Create a new autorenew poll message.
PollMessage.Autorenew gainingClientAutorenewPollMessage = PollMessage.Autorenew gainingClientAutorenewPollMessage =
@ -224,9 +211,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
.setEventTime(newExpirationTime) .setEventTime(newExpirationTime)
.setAutorenewEndTime(END_OF_TIME) .setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.") .setMsg("Domain was auto-renewed.")
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
// Construct the post-transfer domain. // Construct the post-transfer domain.
Domain partiallyApprovedDomain = Domain partiallyApprovedDomain =
@ -264,7 +249,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
// Create a poll message for the gaining client. // Create a poll message for the gaining client.
PollMessage gainingClientPollMessage = PollMessage gainingClientPollMessage =
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, newDomain.getTransferData(), newExpirationTime, now, domainHistoryKey); targetId, newDomain.getTransferData(), newExpirationTime, now, domainHistoryId);
billingEvent.ifPresent(entitiesToSave::add); billingEvent.ifPresent(entitiesToSave::add);
entitiesToSave.add( entitiesToSave.add(
autorenewEvent, autorenewEvent,

View file

@ -14,7 +14,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer;
@ -32,7 +32,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -47,6 +46,7 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
@ -83,7 +83,9 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
@Inject @Superuser boolean isSuperuser; @Inject @Superuser boolean isSuperuser;
@Inject DomainHistory.Builder historyBuilder; @Inject DomainHistory.Builder historyBuilder;
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject DomainTransferCancelFlow() {}
@Inject
DomainTransferCancelFlow() {}
@Override @Override
public EppResponse run() throws EppException { public EppResponse run() throws EppException {
@ -100,9 +102,9 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
} }
Registry registry = Registry.get(existingDomain.getTld()); Registry registry = Registry.get(existingDomain.getTld());
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder historyBuilder
.setId(domainHistoryKey.getId()) .setRevisionId(domainHistoryId.getRevisionId())
.setOtherRegistrarId(existingDomain.getTransferData().getLosingRegistrarId()); .setOtherRegistrarId(existingDomain.getTransferData().getLosingRegistrarId());
Domain newDomain = Domain newDomain =
@ -112,12 +114,12 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
newDomain, newDomain,
domainHistory, domainHistory,
createLosingTransferPollMessage( createLosingTransferPollMessage(
targetId, newDomain.getTransferData(), null, domainHistoryKey)); targetId, newDomain.getTransferData(), null, domainHistoryId));
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This // Reopen the autorenew event and poll message that we closed for the implicit transfer. This
// may recreate the autorenew poll message if it was deleted when the transfer request was made. // may recreate the autorenew poll message if it was deleted when the transfer request was made.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId()); existingDomain, existingRecurring, END_OF_TIME, domainHistory.getHistoryEntryId());
// Delete the billing event and poll messages that were written in case the transfer would have // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
tm().delete(existingDomain.getTransferData().getServerApproveEntities()); tm().delete(existingDomain.getTransferData().getServerApproveEntities());

View file

@ -14,7 +14,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer;
@ -34,7 +34,6 @@ import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.RegistrarId;
@ -49,6 +48,7 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
@ -95,9 +95,9 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
DateTime now = tm().getTransactionTime(); DateTime now = tm().getTransactionTime();
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now); Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
Registry registry = Registry.get(existingDomain.getTld()); Registry registry = Registry.get(existingDomain.getTld());
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder historyBuilder
.setId(domainHistoryKey.getId()) .setRevisionId(domainHistoryId.getRevisionId())
.setOtherRegistrarId(existingDomain.getTransferData().getGainingRegistrarId()); .setOtherRegistrarId(existingDomain.getTransferData().getGainingRegistrarId());
verifyOptionalAuthInfo(authInfo, existingDomain); verifyOptionalAuthInfo(authInfo, existingDomain);
@ -113,12 +113,12 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
newDomain, newDomain,
domainHistory, domainHistory,
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, newDomain.getTransferData(), null, now, domainHistoryKey)); targetId, newDomain.getTransferData(), null, now, domainHistoryId));
// Reopen the autorenew event and poll message that we closed for the implicit transfer. This // Reopen the autorenew event and poll message that we closed for the implicit transfer. This
// may end up recreating the poll message if it was deleted upon the transfer request. // may end up recreating the poll message if it was deleted upon the transfer request.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(
existingDomain, existingRecurring, END_OF_TIME, domainHistory.getDomainHistoryId()); existingDomain, existingRecurring, END_OF_TIME, domainHistory.getHistoryEntryId());
// Delete the billing event and poll messages that were written in case the transfer would have // Delete the billing event and poll messages that were written in case the transfer would have
// been implicitly server approved. // been implicitly server approved.
tm().delete(existingDomain.getTransferData().getServerApproveEntities()); tm().delete(existingDomain.getTransferData().getServerApproveEntities());

View file

@ -14,7 +14,7 @@
package google.registry.flows.domain; package google.registry.flows.domain;
import static google.registry.flows.FlowUtils.createHistoryKey; import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn; import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.computeExDateForApprovalTime; import static google.registry.flows.ResourceFlowUtils.computeExDateForApprovalTime;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@ -38,7 +38,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.batch.AsyncTaskEnqueuer; import google.registry.batch.AsyncTaskEnqueuer;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager; import google.registry.flows.ExtensionManager;
@ -57,7 +56,6 @@ import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Transfer; import google.registry.model.domain.DomainCommand.Transfer;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransferCommandExtension;
import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension;
@ -73,6 +71,7 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.DomainTransferData;
@ -196,16 +195,16 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
// If the period is zero, then there is no fee for the transfer. // If the period is zero, then there is no fee for the transfer.
Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(existingDomain.getAutorenewBillingEvent());
Optional<FeesAndCredits> feesAndCredits = Optional<FeesAndCredits> feesAndCredits =
(period.getValue() == 0) period.getValue() == 0
? Optional.empty() ? Optional.empty()
: Optional.of( : Optional.of(
pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring)); pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
if (feesAndCredits.isPresent()) { if (feesAndCredits.isPresent()) {
validateFeeChallenge(targetId, now, feeTransfer, feesAndCredits.get()); validateFeeChallenge(feeTransfer, feesAndCredits.get());
} }
Key<DomainHistory> domainHistoryKey = createHistoryKey(existingDomain, DomainHistory.class); HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
historyBuilder historyBuilder
.setId(domainHistoryKey.getId()) .setRevisionId(domainHistoryId.getRevisionId())
.setOtherRegistrarId(existingDomain.getCurrentSponsorRegistrarId()); .setOtherRegistrarId(existingDomain.getCurrentSponsorRegistrarId());
DateTime automaticTransferTime = DateTime automaticTransferTime =
superuserExtension superuserExtension
@ -230,7 +229,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
createTransferServerApproveEntities( createTransferServerApproveEntities(
automaticTransferTime, automaticTransferTime,
serverApproveNewExpirationTime, serverApproveNewExpirationTime,
domainHistoryKey, domainHistoryId,
existingDomain, existingDomain,
existingRecurring, existingRecurring,
trid, trid,
@ -241,7 +240,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
DomainTransferData pendingTransferData = DomainTransferData pendingTransferData =
createPendingTransferData( createPendingTransferData(
domainAtTransferTime.getRepoId(), domainAtTransferTime.getRepoId(),
domainHistoryKey.getId(), domainHistoryId.getRevisionId(),
new DomainTransferData.Builder() new DomainTransferData.Builder()
.setTransferRequestTrid(trid) .setTransferRequestTrid(trid)
.setTransferRequestTime(now) .setTransferRequestTime(now)
@ -254,7 +253,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
// Create a poll message to notify the losing registrar that a transfer was requested. // Create a poll message to notify the losing registrar that a transfer was requested.
PollMessage requestPollMessage = PollMessage requestPollMessage =
createLosingTransferPollMessage( createLosingTransferPollMessage(
targetId, pendingTransferData, serverApproveNewExpirationTime, domainHistoryKey) targetId, pendingTransferData, serverApproveNewExpirationTime, domainHistoryId)
.asBuilder() .asBuilder()
.setEventTime(now) .setEventTime(now)
.build(); .build();
@ -263,10 +262,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
// cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones // cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones
// that we've created in this flow and stored in pendingTransferData. // that we've created in this flow and stored in pendingTransferData.
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(
existingDomain, existingDomain, existingRecurring, automaticTransferTime, domainHistoryId);
existingRecurring,
automaticTransferTime,
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()));
Domain newDomain = Domain newDomain =
existingDomain existingDomain
.asBuilder() .asBuilder()
@ -385,7 +381,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
private static ImmutableList<FeeTransformResponseExtension> createResponseExtensions( private static ImmutableList<FeeTransformResponseExtension> createResponseExtensions(
Optional<FeesAndCredits> feesAndCredits, Optional<FeeTransferCommandExtension> feeTransfer) { Optional<FeesAndCredits> feesAndCredits, Optional<FeeTransferCommandExtension> feeTransfer) {
return (feeTransfer.isPresent() && feesAndCredits.isPresent()) return feeTransfer.isPresent() && feesAndCredits.isPresent()
? ImmutableList.of( ? ImmutableList.of(
feeTransfer feeTransfer
.get() .get()

View file

@ -20,20 +20,18 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
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.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
@ -109,7 +107,7 @@ public final class DomainTransferUtils {
public static ImmutableSet<TransferServerApproveEntity> createTransferServerApproveEntities( public static ImmutableSet<TransferServerApproveEntity> createTransferServerApproveEntities(
DateTime automaticTransferTime, DateTime automaticTransferTime,
DateTime serverApproveNewExpirationTime, DateTime serverApproveNewExpirationTime,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
Domain existingDomain, Domain existingDomain,
Recurring existingRecurring, Recurring existingRecurring,
Trid trid, Trid trid,
@ -135,38 +133,38 @@ public final class DomainTransferUtils {
builder.add( builder.add(
createTransferBillingEvent( createTransferBillingEvent(
automaticTransferTime, automaticTransferTime,
domainHistoryKey, domainHistoryId,
targetId, targetId,
gainingRegistrarId, gainingRegistrarId,
registry, registry,
cost))); cost)));
createOptionalAutorenewCancellation( createOptionalAutorenewCancellation(
automaticTransferTime, now, domainHistoryKey, targetId, existingDomain, transferCost) automaticTransferTime, now, domainHistoryId, targetId, existingDomain, transferCost)
.ifPresent(builder::add); .ifPresent(builder::add);
return builder return builder
.add( .add(
createGainingClientAutorenewEvent( createGainingClientAutorenewEvent(
existingRecurring, existingRecurring,
serverApproveNewExpirationTime, serverApproveNewExpirationTime,
domainHistoryKey, domainHistoryId,
targetId, targetId,
gainingRegistrarId)) gainingRegistrarId))
.add( .add(
createGainingClientAutorenewPollMessage( createGainingClientAutorenewPollMessage(
serverApproveNewExpirationTime, domainHistoryKey, targetId, gainingRegistrarId)) serverApproveNewExpirationTime, domainHistoryId, targetId, gainingRegistrarId))
.add( .add(
createGainingTransferPollMessage( createGainingTransferPollMessage(
targetId, targetId,
serverApproveTransferData, serverApproveTransferData,
serverApproveNewExpirationTime, serverApproveNewExpirationTime,
now, now,
domainHistoryKey)) domainHistoryId))
.add( .add(
createLosingTransferPollMessage( createLosingTransferPollMessage(
targetId, targetId,
serverApproveTransferData, serverApproveTransferData,
serverApproveNewExpirationTime, serverApproveNewExpirationTime,
domainHistoryKey)) domainHistoryId))
.build(); .build();
} }
@ -176,7 +174,7 @@ public final class DomainTransferUtils {
TransferData transferData, TransferData transferData,
@Nullable DateTime extendedRegistrationExpirationTime, @Nullable DateTime extendedRegistrationExpirationTime,
DateTime now, DateTime now,
Key<DomainHistory> domainHistoryKey) { HistoryEntryId domainHistoryId) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setRegistrarId(transferData.getGainingRegistrarId()) .setRegistrarId(transferData.getGainingRegistrarId())
.setEventTime(transferData.getPendingTransferExpirationTime()) .setEventTime(transferData.getPendingTransferExpirationTime())
@ -189,8 +187,7 @@ public final class DomainTransferUtils {
transferData.getTransferStatus().isApproved(), transferData.getTransferStatus().isApproved(),
transferData.getTransferRequestTrid(), transferData.getTransferRequestTrid(),
now))) now)))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
@ -199,7 +196,7 @@ public final class DomainTransferUtils {
String targetId, String targetId,
TransferData transferData, TransferData transferData,
@Nullable DateTime extendedRegistrationExpirationTime, @Nullable DateTime extendedRegistrationExpirationTime,
Key<DomainHistory> domainHistoryKey) { HistoryEntryId domainHistoryId) {
return new PollMessage.OneTime.Builder() return new PollMessage.OneTime.Builder()
.setRegistrarId(transferData.getLosingRegistrarId()) .setRegistrarId(transferData.getLosingRegistrarId())
.setEventTime(transferData.getPendingTransferExpirationTime()) .setEventTime(transferData.getPendingTransferExpirationTime())
@ -207,8 +204,7 @@ public final class DomainTransferUtils {
.setResponseData( .setResponseData(
ImmutableList.of( ImmutableList.of(
createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime))) createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime)))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
@ -230,7 +226,7 @@ public final class DomainTransferUtils {
private static PollMessage.Autorenew createGainingClientAutorenewPollMessage( private static PollMessage.Autorenew createGainingClientAutorenewPollMessage(
DateTime serverApproveNewExpirationTime, DateTime serverApproveNewExpirationTime,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
String targetId, String targetId,
String gainingRegistrarId) { String gainingRegistrarId) {
return new PollMessage.Autorenew.Builder() return new PollMessage.Autorenew.Builder()
@ -239,15 +235,14 @@ public final class DomainTransferUtils {
.setEventTime(serverApproveNewExpirationTime) .setEventTime(serverApproveNewExpirationTime)
.setAutorenewEndTime(END_OF_TIME) .setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.") .setMsg("Domain was auto-renewed.")
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
private static BillingEvent.Recurring createGainingClientAutorenewEvent( private static BillingEvent.Recurring createGainingClientAutorenewEvent(
Recurring existingRecurring, Recurring existingRecurring,
DateTime serverApproveNewExpirationTime, DateTime serverApproveNewExpirationTime,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
String targetId, String targetId,
String gainingRegistrarId) { String gainingRegistrarId) {
return new BillingEvent.Recurring.Builder() return new BillingEvent.Recurring.Builder()
@ -259,8 +254,7 @@ public final class DomainTransferUtils {
.setRecurrenceEndTime(END_OF_TIME) .setRecurrenceEndTime(END_OF_TIME)
.setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior()) .setRenewalPriceBehavior(existingRecurring.getRenewalPriceBehavior())
.setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null)) .setRenewalPrice(existingRecurring.getRenewalPrice().orElse(null))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }
@ -283,7 +277,7 @@ public final class DomainTransferUtils {
private static Optional<BillingEvent.Cancellation> createOptionalAutorenewCancellation( private static Optional<BillingEvent.Cancellation> createOptionalAutorenewCancellation(
DateTime automaticTransferTime, DateTime automaticTransferTime,
DateTime now, DateTime now,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
String targetId, String targetId,
Domain existingDomain, Domain existingDomain,
Optional<Money> transferCost) { Optional<Money> transferCost) {
@ -294,11 +288,7 @@ public final class DomainTransferUtils {
if (autorenewGracePeriod != null && transferCost.isPresent()) { if (autorenewGracePeriod != null && transferCost.isPresent()) {
return Optional.of( return Optional.of(
BillingEvent.Cancellation.forGracePeriod( BillingEvent.Cancellation.forGracePeriod(
autorenewGracePeriod, autorenewGracePeriod, now, domainHistoryId, targetId)
now,
new DomainHistoryId(
domainHistoryKey.getParent().getName(), domainHistoryKey.getId()),
targetId)
.asBuilder() .asBuilder()
.setEventTime(automaticTransferTime) .setEventTime(automaticTransferTime)
.build()); .build());
@ -308,7 +298,7 @@ public final class DomainTransferUtils {
private static BillingEvent.OneTime createTransferBillingEvent( private static BillingEvent.OneTime createTransferBillingEvent(
DateTime automaticTransferTime, DateTime automaticTransferTime,
Key<DomainHistory> domainHistoryKey, HistoryEntryId domainHistoryId,
String targetId, String targetId,
String gainingRegistrarId, String gainingRegistrarId,
Registry registry, Registry registry,
@ -321,8 +311,7 @@ public final class DomainTransferUtils {
.setPeriodYears(1) .setPeriodYears(1)
.setEventTime(automaticTransferTime) .setEventTime(automaticTransferTime)
.setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength())) .setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength()))
.setDomainHistoryId( .setDomainHistoryId(domainHistoryId)
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
.build(); .build();
} }

View file

@ -208,12 +208,9 @@ public final class DomainUpdateFlow implements TransactionalFlow {
/** Determines if any of the changes to new domain should trigger DNS update. */ /** Determines if any of the changes to new domain should trigger DNS update. */
private boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain) { private boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain) {
if (existingDomain.shouldPublishToDns() != newDomain.shouldPublishToDns() return existingDomain.shouldPublishToDns() != newDomain.shouldPublishToDns()
|| !Objects.equals(newDomain.getDsData(), existingDomain.getDsData()) || !Objects.equals(newDomain.getDsData(), existingDomain.getDsData())
|| !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts())) { || !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts());
return true;
}
return false;
} }
/** Fail if the object doesn't exist or was deleted. */ /** Fail if the object doesn't exist or was deleted. */
@ -363,7 +360,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
.map(StatusValue::getXmlName) .map(StatusValue::getXmlName)
.collect(toImmutableSortedSet(Ordering.natural())); .collect(toImmutableSortedSet(Ordering.natural()));
String msg = ""; String msg;
if (addedServerStatuses.size() > 0 && removedServerStatuses.size() > 0) { if (addedServerStatuses.size() > 0 && removedServerStatuses.size() > 0) {
msg = msg =
String.format( String.format(

View file

@ -36,7 +36,7 @@ import google.registry.model.domain.token.AllocationToken.TokenBehavior;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.domain.token.AllocationTokenExtension;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.util.List; import java.util.List;
@ -95,12 +95,11 @@ public class AllocationTokenFlowUtils {
} }
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */ /** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
public AllocationToken redeemToken( public AllocationToken redeemToken(AllocationToken token, HistoryEntryId redemptionHistoryId) {
AllocationToken token, VKey<? extends HistoryEntry> redemptionHistoryEntry) {
checkArgument( checkArgument(
TokenType.SINGLE_USE.equals(token.getTokenType()), TokenType.SINGLE_USE.equals(token.getTokenType()),
"Only SINGLE_USE tokens can be marked as redeemed"); "Only SINGLE_USE tokens can be marked as redeemed");
return token.asBuilder().setRedemptionHistoryEntry(redemptionHistoryEntry).build(); return token.asBuilder().setRedemptionHistoryId(redemptionHistoryId).build();
} }
/** /**
@ -110,7 +109,7 @@ public class AllocationTokenFlowUtils {
* *
* @throws EppException if the token is invalid in any way * @throws EppException if the token is invalid in any way
*/ */
private void validateToken( private static void validateToken(
InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now) InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now)
throws EppException { throws EppException {
@ -138,7 +137,7 @@ public class AllocationTokenFlowUtils {
} }
/** Loads a given token and validates that it is not redeemed */ /** Loads a given token and validates that it is not redeemed */
private AllocationToken loadToken(String token) throws EppException { private static AllocationToken loadToken(String token) throws EppException {
if (Strings.isNullOrEmpty(token)) { if (Strings.isNullOrEmpty(token)) {
// We load the token directly from the input XML. If it's null or empty we should throw // We load the token directly from the input XML. If it's null or empty we should throw
// an InvalidAllocationTokenException before the database load attempt fails. // an InvalidAllocationTokenException before the database load attempt fails.

View file

@ -18,12 +18,8 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.annotations.DeleteAfterMigration; import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.common.GaeUserIdConverter; import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.reporting.HistoryEntry;
/** Sets of classes of the Objectify-registered entities in use throughout the model. */ /** Sets of classes of the Objectify-registered entities in use throughout the model. */
@DeleteAfterMigration @DeleteAfterMigration
@ -31,15 +27,7 @@ public final class EntityClasses {
/** Set of entity classes. */ /** Set of entity classes. */
public static final ImmutableSet<Class<? extends ImmutableObject>> ALL_CLASSES = public static final ImmutableSet<Class<? extends ImmutableObject>> ALL_CLASSES =
ImmutableSet.of( ImmutableSet.of(Contact.class, Domain.class, GaeUserIdConverter.class, Host.class);
Contact.class,
ContactHistory.class,
Domain.class,
DomainHistory.class,
GaeUserIdConverter.class,
HistoryEntry.class,
Host.class,
HostHistory.class);
private EntityClasses() {} private EntityClasses() {}
} }

View file

@ -30,7 +30,7 @@ public abstract class UpdateAutoTimestampEntity extends ImmutableObject
implements UnsafeSerializable { implements UnsafeSerializable {
/** /**
* An automatically managed timestamp of when this object was last written to Datastore. * An automatically managed timestamp of when this object was last written to the database.
* *
* <p>Note that this is distinct from the EPP-specified {@link EppResource#lastEppUpdateTime}, in * <p>Note that this is distinct from the EPP-specified {@link EppResource#lastEppUpdateTime}, in
* that this is updated on every save, rather than only in response to an {@code <update>} command * that this is updated on every save, rather than only in response to an {@code <update>} command

View file

@ -32,10 +32,10 @@ import google.registry.model.annotations.OfyIdAllocation;
import google.registry.model.annotations.ReportedOn; import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.TimeOfYear; import google.registry.model.common.TimeOfYear;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod; import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.transfer.TransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey; import google.registry.persistence.WithVKey;
@ -204,8 +204,8 @@ public abstract class BillingEvent extends ImmutableObject
return targetId; return targetId;
} }
public DomainHistoryId getDomainHistoryId() { public HistoryEntryId getHistoryEntryId() {
return new DomainHistoryId(domainRepoId, domainHistoryRevisionId); return new HistoryEntryId(domainRepoId, domainHistoryRevisionId);
} }
public ImmutableSet<Flag> getFlags() { public ImmutableSet<Flag> getFlags() {
@ -259,14 +259,14 @@ public abstract class BillingEvent extends ImmutableObject
return thisCastToDerived(); return thisCastToDerived();
} }
public B setDomainHistoryId(DomainHistoryId domainHistoryId) { public B setDomainHistoryId(HistoryEntryId domainHistoryId) {
getInstance().domainHistoryRevisionId = domainHistoryId.getId(); getInstance().domainHistoryRevisionId = domainHistoryId.getRevisionId();
getInstance().domainRepoId = domainHistoryId.getDomainRepoId(); getInstance().domainRepoId = domainHistoryId.getRepoId();
return thisCastToDerived(); return thisCastToDerived();
} }
public B setDomainHistory(DomainHistory domainHistory) { public B setDomainHistory(DomainHistory domainHistory) {
return setDomainHistoryId(domainHistory.getDomainHistoryId()); return setDomainHistoryId(domainHistory.getHistoryEntryId());
} }
@Override @Override
@ -653,7 +653,7 @@ public abstract class BillingEvent extends ImmutableObject
public static Cancellation forGracePeriod( public static Cancellation forGracePeriod(
GracePeriod gracePeriod, GracePeriod gracePeriod,
DateTime eventTime, DateTime eventTime,
DomainHistoryId domainHistoryId, HistoryEntryId domainHistoryId,
String targetId) { String targetId) {
checkArgument( checkArgument(
gracePeriod.hasBillingEvent(), gracePeriod.hasBillingEvent(),

View file

@ -14,24 +14,18 @@
package google.registry.model.contact; package google.registry.model.contact;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.io.Serializable;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
import javax.persistence.AttributeOverride;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Index;
import javax.persistence.IdClass; import javax.persistence.Table;
import javax.persistence.PostLoad;
/** /**
* A persisted history entry representing an EPP modification to a contact. * A persisted history entry representing an EPP modification to a contact.
@ -39,53 +33,26 @@ import javax.persistence.PostLoad;
* <p>In addition to the general history fields (e.g. action time, registrar ID) we also persist a * <p>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 * 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. * the foreign-keyed fields in that class can refer to this object.
*
* <p>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 @Entity
@javax.persistence.Table( @Table(
indexes = { indexes = {
@javax.persistence.Index(columnList = "creationTime"), @Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "historyRegistrarId"), @Index(columnList = "historyRegistrarId"),
@javax.persistence.Index(columnList = "historyType"), @Index(columnList = "historyType"),
@javax.persistence.Index(columnList = "historyModificationTime") @Index(columnList = "historyModificationTime")
}) })
@EntitySubclass @AttributeOverride(name = "repoId", column = @Column(name = "contactRepoId"))
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
@IdClass(ContactHistoryId.class) public class ContactHistory extends HistoryEntry {
public class ContactHistory extends HistoryEntry implements UnsafeSerializable {
// Store ContactBase instead of Contact so we don't pick up its @Id // Store ContactBase instead of Contact, so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects // @Nullable for the sake of pre-Registry-3.0 history objects
@Nullable ContactBase contactBase; @Nullable ContactBase resource;
@Id
@Access(AccessType.PROPERTY)
public String getContactRepoId() {
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setContactRepoId(String contactRepoId) {
parent = Key.create(Contact.class, contactRepoId);
}
@Id
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override @Override
public long getId() { protected ContactBase getResource() {
return super.getId(); return resource;
}
public ContactHistoryId getContactHistoryId() {
return new ContactHistoryId(getContactRepoId(), getId());
} }
/** /**
@ -95,19 +62,13 @@ public class ContactHistory extends HistoryEntry implements UnsafeSerializable {
* <p>Will be absent for objects created prior to the Registry 3.0 SQL migration. * <p>Will be absent for objects created prior to the Registry 3.0 SQL migration.
*/ */
public Optional<ContactBase> getContactBase() { public Optional<ContactBase> getContactBase() {
return Optional.ofNullable(contactBase); return Optional.ofNullable(resource);
}
/** The key to the {@link Contact} this is based off of. */
public VKey<Contact> getParentVKey() {
return VKey.create(Contact.class, getContactRepoId());
} }
/** Creates a {@link VKey} instance for this entity. */ /** Creates a {@link VKey} instance for this entity. */
@SuppressWarnings("unchecked")
@Override @Override
public VKey<ContactHistory> createVKey() { public VKey<ContactHistory> createVKey() {
return (VKey<ContactHistory>) createVKey(Key.create(this)); return VKey.createSql(ContactHistory.class, getHistoryEntryId());
} }
@Override @Override
@ -115,77 +76,6 @@ public class ContactHistory extends HistoryEntry implements UnsafeSerializable {
return getContactBase().map(contactBase -> new Contact.Builder().copyFrom(contactBase).build()); return getContactBase().map(contactBase -> new Contact.Builder().copyFrom(contactBase).build());
} }
@PostLoad
void postLoad() {
// Normally Hibernate would see that the contact fields are all null and would fill contactBase
// with a null object. Unfortunately, the updateTimestamp is never null in SQL.
if (contactBase != null && contactBase.getContactId() == null) {
contactBase = null;
}
if (contactBase != null && contactBase.getRepoId() == null) {
// contactBase hasn't been fully constructed yet, so it's ok to go in and mutate it. Though
// the use of the Builder is not necessarily problematic in this case, this is still safer as
// the Builder can do things like comparisons that compute the hash code.
contactBase.setRepoId(parent.getName());
}
}
/** Class to represent the composite primary key of {@link ContactHistory} entity. */
public static class ContactHistoryId extends ImmutableObject implements Serializable {
private String contactRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private ContactHistoryId() {}
public ContactHistoryId(String contactRepoId, long id) {
this.contactRepoId = contactRepoId;
this.id = id;
}
/**
* Returns the contact repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
public String getContactRepoId() {
return contactRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
public long getId() {
return id;
}
/**
* Sets the contact repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setContactRepoId(String contactRepoId) {
this.contactRepoId = contactRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override @Override
public Builder asBuilder() { public Builder asBuilder() {
return new Builder(clone(this)); return new Builder(clone(this));
@ -199,23 +89,13 @@ public class ContactHistory extends HistoryEntry implements UnsafeSerializable {
super(instance); super(instance);
} }
public Builder setContact(@Nullable ContactBase contactBase) { public Builder setContact(ContactBase contactBase) {
// Nullable for the sake of pre-Registry-3.0 history objects getInstance().resource = contactBase;
if (contactBase == null) { return setRepoId(contactBase);
return this;
}
getInstance().contactBase = contactBase;
return super.setParent(contactBase);
}
public Builder setContactRepoId(String contactRepoId) {
getInstance().parent = Key.create(Contact.class, contactRepoId);
return this;
} }
public Builder wipeOutPii() { public Builder wipeOutPii() {
getInstance().contactBase = getInstance().resource = getInstance().resource.asBuilder().wipeOut().build();
getInstance().getContactBase().get().asBuilder().wipeOut().build();
return this; return this;
} }
} }

View file

@ -18,12 +18,8 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.GracePeriod.GracePeriodHistory; import google.registry.model.domain.GracePeriod.GracePeriodHistory;
import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.domain.secdns.DomainDsDataHistory; import google.registry.model.domain.secdns.DomainDsDataHistory;
@ -31,7 +27,6 @@ import google.registry.model.host.Host;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.io.Serializable;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -45,8 +40,6 @@ import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Index; import javax.persistence.Index;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns; import javax.persistence.JoinColumns;
@ -62,11 +55,6 @@ import org.hibernate.Hibernate;
* <p>In addition to the general history fields (e.g. action time, registrar ID) we also persist a * <p>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 DomainBase} so that the * copy of the domain entity at this point in time. We persist a raw {@link DomainBase} so that the
* foreign-keyed fields in that class can refer to this object. * foreign-keyed fields in that class can refer to this object.
*
* <p>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 @Entity
@Table( @Table(
@ -76,49 +64,44 @@ import org.hibernate.Hibernate;
@Index(columnList = "historyType"), @Index(columnList = "historyType"),
@Index(columnList = "historyModificationTime") @Index(columnList = "historyModificationTime")
}) })
@EntitySubclass
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
@IdClass(DomainHistoryId.class) @AttributeOverride(name = "repoId", column = @Column(name = "domainRepoId"))
public class DomainHistory extends HistoryEntry { public class DomainHistory extends HistoryEntry {
// Store DomainBase instead of Domain so we don't pick up its @Id // Store DomainBase instead of Domain, so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects // @Nullable for the sake of pre-Registry-3.0 history objects
@Nullable DomainBase domainBase; @Nullable DomainBase resource;
@Id @Override
@Access(AccessType.PROPERTY) protected DomainBase getResource() {
public String getDomainRepoId() { return resource;
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
} }
/** This method is private because it is only used by Hibernate. */ /**
@SuppressWarnings("unused") * The values of all the fields on the {@link DomainBase} object after the action represented by
private void setDomainRepoId(String domainRepoId) { * this history object was executed.
parent = Key.create(Domain.class, domainRepoId); *
* <p>Will be absent for objects created prior to the Registry 3.0 SQL migration.
*/
public Optional<DomainBase> getDomainBase() {
return Optional.ofNullable(resource);
} }
// We could have reused domainBase.nsHosts here, but Hibernate throws a weird exception after // We could have reused domainBase.nsHosts here, but Hibernate throws a weird exception after
// we change to use a composite primary key. // we change to use a composite primary key.
// TODO(b/166776754): Investigate if we can reuse domainBase.nsHosts for storing host keys.
@ElementCollection @ElementCollection
@JoinTable( @JoinTable(
name = "DomainHistoryHost", name = "DomainHistoryHost",
indexes = { indexes =
@Index( @Index(
columnList = columnList =
"domain_history_history_revision_id,domain_history_domain_repo_id,host_repo_id", "domain_history_history_revision_id,domain_history_domain_repo_id,host_repo_id",
unique = true), unique = true))
}) @EmptySetToNull
@ImmutableObject.EmptySetToNull
@Column(name = "host_repo_id") @Column(name = "host_repo_id")
Set<VKey<Host>> nsHosts; Set<VKey<Host>> nsHosts;
@OneToMany( @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@JoinColumns({ @JoinColumns({
@JoinColumn( @JoinColumn(
name = "domainHistoryRevisionId", name = "domainHistoryRevisionId",
@ -135,10 +118,7 @@ public class DomainHistory extends HistoryEntry {
@Ignore @Ignore
Set<DomainDsDataHistory> dsDataHistories = new HashSet<>(); Set<DomainDsDataHistory> dsDataHistories = new HashSet<>();
@OneToMany( @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@JoinColumns({ @JoinColumns({
@JoinColumn( @JoinColumn(
name = "domainHistoryRevisionId", name = "domainHistoryRevisionId",
@ -152,19 +132,14 @@ public class DomainHistory extends HistoryEntry {
updatable = false) updatable = false)
}) })
// HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request // HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
@Ignore
Set<GracePeriodHistory> gracePeriodHistories = new HashSet<>(); Set<GracePeriodHistory> gracePeriodHistories = new HashSet<>();
@Override /** The length of time that a create, allocate, renewal, or transfer request was issued for. */
@Nullable
@Access(AccessType.PROPERTY)
@AttributeOverrides({ @AttributeOverrides({
@AttributeOverride(name = "unit", column = @Column(name = "historyPeriodUnit")), @AttributeOverride(name = "unit", column = @Column(name = "historyPeriodUnit")),
@AttributeOverride(name = "value", column = @Column(name = "historyPeriodValue")) @AttributeOverride(name = "value", column = @Column(name = "historyPeriodValue"))
}) })
public Period getPeriod() { Period period;
return super.getPeriod();
}
/** /**
* For transfers, the id of the other registrar. * For transfers, the id of the other registrar.
@ -174,56 +149,29 @@ public class DomainHistory extends HistoryEntry {
* registrar is the gaining party. * registrar is the gaining party.
*/ */
@Nullable @Nullable
@Access(AccessType.PROPERTY)
@Column(name = "historyOtherRegistrarId") @Column(name = "historyOtherRegistrarId")
@Override String otherRegistrarId;
public String getOtherRegistrarId() {
return super.getOtherRegistrarId();
}
/** /**
* Logging field for transaction reporting. * Logging field for transaction reporting.
* *
* <p>This will be empty for any DomainHistory/HistoryEntry generated before this field was added, * <p>This will be empty for any DomainHistory/HistoryEntry generated before this field was added
* mid-2017, as well as any action that does not generate billable events (e.g. updates). * (mid-2017), as well as any action that does not generate billable events (e.g. contact/host
* * updates). *
* <p>This method is dedicated for Hibernate, external caller should use {@link
* #getDomainTransactionRecords()}.
*/ */
@Access(AccessType.PROPERTY) @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@OneToMany(
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@JoinColumn(name = "historyRevisionId", referencedColumnName = "historyRevisionId") @JoinColumn(name = "historyRevisionId", referencedColumnName = "historyRevisionId")
@JoinColumn(name = "domainRepoId", referencedColumnName = "domainRepoId") @JoinColumn(name = "domainRepoId", referencedColumnName = "domainRepoId")
@SuppressWarnings("unused") @EmptySetToNull
private Set<DomainTransactionRecord> getInternalDomainTransactionRecords() { Set<DomainTransactionRecord> domainTransactionRecords;
return domainTransactionRecords;
}
/** Sets the domain transaction records. This method is dedicated for Hibernate. */ public Set<DomainTransactionRecord> getDomainTransactionRecords() {
@SuppressWarnings("unused") return nullToEmptyImmutableCopy(domainTransactionRecords);
private void setInternalDomainTransactionRecords(
Set<DomainTransactionRecord> domainTransactionRecords) {
super.setDomainTransactionRecords(domainTransactionRecords);
}
@Id
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override
public long getId() {
return super.getId();
}
public DomainHistoryId getDomainHistoryId() {
return new DomainHistoryId(getDomainRepoId(), getId());
} }
/** Returns keys to the {@link Host} that are the nameservers for the domain. */ /** Returns keys to the {@link Host} that are the nameservers for the domain. */
public Set<VKey<Host>> getNsHosts() { public Set<VKey<Host>> getNsHosts() {
return nsHosts; return ImmutableSet.copyOf(nsHosts);
} }
/** Returns the collection of {@link DomainDsDataHistory} instances. */ /** Returns the collection of {@link DomainDsDataHistory} instances. */
@ -231,30 +179,14 @@ public class DomainHistory extends HistoryEntry {
return nullToEmptyImmutableCopy(dsDataHistories); return nullToEmptyImmutableCopy(dsDataHistories);
} }
/**
* The values of all the fields on the {@link DomainBase} object after the action represented by
* this history object was executed.
*
* <p>Will be absent for objects created prior to the Registry 3.0 SQL migration.
*/
public Optional<DomainBase> getDomainBase() {
return Optional.ofNullable(domainBase);
}
/** The key to the {@link Domain} this is based off of. */
public VKey<Domain> getParentVKey() {
return VKey.create(Domain.class, getDomainRepoId());
}
public Set<GracePeriodHistory> getGracePeriodHistories() { public Set<GracePeriodHistory> getGracePeriodHistories() {
return nullToEmptyImmutableCopy(gracePeriodHistories); return nullToEmptyImmutableCopy(gracePeriodHistories);
} }
/** Creates a {@link VKey} instance for this entity. */ /** Creates a {@link VKey} instance for this entity. */
@SuppressWarnings("unchecked")
@Override @Override
public VKey<DomainHistory> createVKey() { public VKey<DomainHistory> createVKey() {
return (VKey<DomainHistory>) createVKey(Key.create(this)); return VKey.createSql(DomainHistory.class, getHistoryEntryId());
} }
@Override @Override
@ -262,102 +194,55 @@ public class DomainHistory extends HistoryEntry {
return getDomainBase().map(domainBase -> new Domain.Builder().copyFrom(domainBase).build()); return getDomainBase().map(domainBase -> new Domain.Builder().copyFrom(domainBase).build());
} }
public String getOtherRegistrarId() {
return otherRegistrarId;
}
public Period getPeriod() {
return period;
}
@Override
@PostLoad @PostLoad
void postLoad() { protected void postLoad() {
// TODO(b/188044616): Determine why Eager loading doesn't work here. // TODO(b/188044616): Determine why Eager loading doesn't work here.
Hibernate.initialize(domainTransactionRecords); Hibernate.initialize(domainTransactionRecords);
Hibernate.initialize(nsHosts); Hibernate.initialize(nsHosts);
Hibernate.initialize(dsDataHistories); Hibernate.initialize(dsDataHistories);
Hibernate.initialize(gracePeriodHistories); Hibernate.initialize(gracePeriodHistories);
if (domainBase != null) { if (resource != null) {
domainBase.nsHosts = nullToEmptyImmutableCopy(nsHosts); resource.nsHosts = nullToEmptyImmutableCopy(nsHosts);
domainBase.gracePeriods = resource.gracePeriods =
gracePeriodHistories.stream() gracePeriodHistories.stream()
.map(GracePeriod::createFromHistory) .map(GracePeriod::createFromHistory)
.collect(toImmutableSet()); .collect(toImmutableSet());
domainBase.dsData = resource.dsData =
dsDataHistories.stream().map(DomainDsData::create).collect(toImmutableSet()); dsDataHistories.stream().map(DomainDsData::create).collect(toImmutableSet());
// Normally Hibernate would see that the domain fields are all null and would fill
// domainBase with a null object. Unfortunately, the updateTimestamp is never null in SQL.
if (domainBase.getDomainName() == null) {
domainBase = null;
} else {
if (domainBase.getRepoId() == null) {
// domainBase still hasn't been fully constructed yet, so it's ok to go in and mutate
// it. In fact, we have to because going through the builder causes the hash codes of
// contained objects to be calculated prematurely.
domainBase.setRepoId(parent.getName());
}
}
} }
processResourcePostLoad();
} }
private static void fillAuxiliaryFieldsFromDomain(DomainHistory domainHistory) { private static void fillAuxiliaryFieldsFromDomain(DomainHistory domainHistory) {
if (domainHistory.domainBase != null) { DomainBase domainBase = domainHistory.resource;
domainHistory.nsHosts = nullToEmptyImmutableCopy(domainHistory.domainBase.nsHosts); if (domainBase != null) {
domainHistory.nsHosts = nullToEmptyImmutableCopy(domainBase.nsHosts);
domainHistory.dsDataHistories = domainHistory.dsDataHistories =
nullToEmptyImmutableCopy(domainHistory.domainBase.getDsData()).stream() nullToEmptyImmutableCopy(domainBase.getDsData()).stream()
.filter(dsData -> dsData.getDigest() != null && dsData.getDigest().length > 0) .filter(dsData -> dsData.getDigest() != null && dsData.getDigest().length > 0)
.map(dsData -> DomainDsDataHistory.createFrom(domainHistory.id, dsData)) .map(dsData -> DomainDsDataHistory.createFrom(domainHistory.getRevisionId(), dsData))
.collect(toImmutableSet()); .collect(toImmutableSet());
domainHistory.gracePeriodHistories = domainHistory.gracePeriodHistories =
nullToEmptyImmutableCopy(domainHistory.domainBase.getGracePeriods()).stream() nullToEmptyImmutableCopy(domainBase.getGracePeriods()).stream()
.map(gracePeriod -> GracePeriodHistory.createFrom(domainHistory.id, gracePeriod)) .map(
gracePeriod ->
GracePeriodHistory.createFrom(domainHistory.getRevisionId(), gracePeriod))
.collect(toImmutableSet()); .collect(toImmutableSet());
} else { } else {
domainHistory.nsHosts = ImmutableSet.of(); domainHistory.nsHosts = ImmutableSet.of();
} }
} }
/** Class to represent the composite primary key of {@link DomainHistory} entity. */
public static class DomainHistoryId extends ImmutableObject implements Serializable {
private String domainRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private DomainHistoryId() {}
public DomainHistoryId(String domainRepoId, long id) {
this.domainRepoId = domainRepoId;
this.id = id;
}
/** Returns the domain repository id. */
public String getDomainRepoId() {
return domainRepoId;
}
/** Returns the history revision id. */
public long getId() {
return id;
}
/**
* Sets the domain repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override @Override
public Builder asBuilder() { public Builder asBuilder() {
return new Builder(clone(this)); return new Builder(clone(this));
@ -371,35 +256,30 @@ public class DomainHistory extends HistoryEntry {
super(instance); super(instance);
} }
public Builder setDomain(@Nullable DomainBase domainBase) { public Builder setDomain(DomainBase domainBase) {
// Nullable for the sake of pre-Registry-3.0 history objects getInstance().resource = domainBase;
if (domainBase == null) { return setRepoId(domainBase);
return this; }
}
// TODO(b/203609982): if actual type of domainBase is Domain, convert to DomainBase public Builder setPeriod(Period period) {
// Note: a DomainHistory fetched by JPA has DomainBase in this field. Allowing Domain getInstance().period = period;
// in the setter makes equality checks messy.
getInstance().domainBase = domainBase;
if (domainBase instanceof Domain) {
super.setParent(domainBase);
} else {
super.setParent(Key.create(Domain.class, domainBase.getRepoId()));
}
return this; return this;
} }
public Builder setDomainRepoId(String domainRepoId) { public Builder setOtherRegistrarId(String otherRegistrarId) {
getInstance().parent = Key.create(Domain.class, domainRepoId); getInstance().otherRegistrarId = otherRegistrarId;
return this;
}
public Builder setDomainTransactionRecords(
ImmutableSet<DomainTransactionRecord> domainTransactionRecords) {
getInstance().domainTransactionRecords = domainTransactionRecords;
return this; return this;
} }
@Override @Override
public DomainHistory build() { public DomainHistory build() {
DomainHistory instance = super.build(); DomainHistory instance = super.build();
// TODO(b/171990736): Assert instance.domainBase is not null after database migration.
// Note that we cannot assert that instance.domainBase is not null here because this
// builder is also used to convert legacy HistoryEntry objects to DomainHistory, when
// domainBase is not available.
fillAuxiliaryFieldsFromDomain(instance); fillAuxiliaryFieldsFromDomain(instance);
return instance; return instance;
} }

View file

@ -21,8 +21,8 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Access; import javax.persistence.Access;
@ -63,7 +63,8 @@ public class GracePeriod extends GracePeriodBase {
@Nullable VKey<BillingEvent.OneTime> billingEventOneTime, @Nullable VKey<BillingEvent.OneTime> billingEventOneTime,
@Nullable VKey<BillingEvent.Recurring> billingEventRecurring, @Nullable VKey<BillingEvent.Recurring> billingEventRecurring,
@Nullable Long gracePeriodId) { @Nullable Long gracePeriodId) {
checkArgument((billingEventOneTime == null) || (billingEventRecurring == null), checkArgument(
billingEventOneTime == null || billingEventRecurring == null,
"A grace period can have at most one billing event"); "A grace period can have at most one billing event");
checkArgument( checkArgument(
(billingEventRecurring != null) == GracePeriodStatus.AUTO_RENEW.equals(type), (billingEventRecurring != null) == GracePeriodStatus.AUTO_RENEW.equals(type),
@ -176,18 +177,6 @@ public class GracePeriod extends GracePeriodBase {
billingEvent.createVKey()); billingEvent.createVKey());
} }
/**
* Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value
* and reconstructed history ids.
*
* <p>TODO(b/162739503): Remove this function after fully migrating to Cloud SQL.
*/
GracePeriod cloneAfterOfyLoad(String domainRepoId) {
GracePeriod clone = clone(this);
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
return clone;
}
/** Entity class to represent a historic {@link GracePeriod}. */ /** Entity class to represent a historic {@link GracePeriod}. */
@Entity(name = "GracePeriodHistory") @Entity(name = "GracePeriodHistory")
@Table(indexes = @Index(columnList = "domainRepoId")) @Table(indexes = @Index(columnList = "domainRepoId"))
@ -203,8 +192,8 @@ public class GracePeriod extends GracePeriodBase {
return super.getGracePeriodId(); return super.getGracePeriodId();
} }
public DomainHistoryId getDomainHistoryId() { public HistoryEntryId getHistoryEntryId() {
return new DomainHistoryId(getDomainRepoId(), domainHistoryRevisionId); return new HistoryEntryId(getDomainRepoId(), domainHistoryRevisionId);
} }
static GracePeriodHistory createFrom(long historyRevisionId, GracePeriod gracePeriod) { static GracePeriodHistory createFrom(long historyRevisionId, GracePeriod gracePeriod) {

View file

@ -16,9 +16,8 @@ package google.registry.model.domain.secdns;
import static google.registry.model.IdService.allocateId; import static google.registry.model.IdService.allocateId;
import google.registry.model.UnsafeSerializable;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
import javax.persistence.Column; import javax.persistence.Column;
@ -27,7 +26,7 @@ import javax.persistence.Id;
/** Entity class to represent a historic {@link DomainDsData}. */ /** Entity class to represent a historic {@link DomainDsData}. */
@Entity @Entity
public class DomainDsDataHistory extends DomainDsDataBase implements UnsafeSerializable { public class DomainDsDataHistory extends DomainDsDataBase {
@Id Long dsDataHistoryRevisionId; @Id Long dsDataHistoryRevisionId;
@ -53,8 +52,8 @@ public class DomainDsDataHistory extends DomainDsDataBase implements UnsafeSeria
return instance; return instance;
} }
public DomainHistory.DomainHistoryId getDomainHistoryId() { public HistoryEntryId getHistoryEntryId() {
return new DomainHistoryId(getDomainRepoId(), domainHistoryRevisionId); return new HistoryEntryId(getDomainRepoId(), domainHistoryRevisionId);
} }
@Override @Override

View file

@ -38,8 +38,7 @@ import google.registry.model.CreateAutoTimestamp;
import google.registry.model.UpdateAutoTimestampEntity; import google.registry.model.UpdateAutoTimestampEntity;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.common.TimedTransitionProperty; import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey; import google.registry.persistence.WithVKey;
import java.util.Optional; import java.util.Optional;
@ -154,11 +153,9 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
@Nullable @Nullable
@AttributeOverrides({ @AttributeOverrides({
@AttributeOverride(name = "repoId", column = @Column(name = "redemption_domain_repo_id")), @AttributeOverride(name = "repoId", column = @Column(name = "redemption_domain_repo_id")),
@AttributeOverride( @AttributeOverride(name = "revisionId", column = @Column(name = "redemption_domain_history_id"))
name = "historyRevisionId",
column = @Column(name = "redemption_domain_history_id"))
}) })
DomainHistoryVKey redemptionHistoryEntry; HistoryEntryId redemptionHistoryId;
/** The fully-qualified domain name that this token is limited to, if any. */ /** The fully-qualified domain name that this token is limited to, if any. */
@Nullable String domainName; @Nullable String domainName;
@ -212,13 +209,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return token; return token;
} }
public Optional<VKey<? extends HistoryEntry>> getRedemptionHistoryEntry() { public Optional<HistoryEntryId> getRedemptionHistoryId() {
return Optional.ofNullable( return Optional.ofNullable(redemptionHistoryId);
redemptionHistoryEntry == null ? null : redemptionHistoryEntry.createDomainHistoryVKey());
} }
public boolean isRedeemed() { public boolean isRedeemed() {
return redemptionHistoryEntry != null; return redemptionHistoryId != null;
} }
public Optional<String> getDomainName() { public Optional<String> getDomainName() {
@ -309,7 +305,7 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType), getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
"Domain name can only be specified for SINGLE_USE tokens"); "Domain name can only be specified for SINGLE_USE tokens");
checkArgument( checkArgument(
getInstance().redemptionHistoryEntry == null getInstance().redemptionHistoryId == null
|| TokenType.SINGLE_USE.equals(getInstance().tokenType), || TokenType.SINGLE_USE.equals(getInstance().tokenType),
"Redemption history entry can only be specified for SINGLE_USE tokens"); "Redemption history entry can only be specified for SINGLE_USE tokens");
checkArgument( checkArgument(
@ -345,10 +341,9 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return this; return this;
} }
public Builder setRedemptionHistoryEntry(VKey<? extends HistoryEntry> redemptionHistoryEntry) { public Builder setRedemptionHistoryId(HistoryEntryId redemptionHistoryId) {
checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null"); checkArgumentNotNull(redemptionHistoryId, "Redemption history entry ID must not be null");
getInstance().redemptionHistoryEntry = getInstance().redemptionHistoryId = redemptionHistoryId;
DomainHistoryVKey.create(redemptionHistoryEntry.getOfyKey());
return this; return this;
} }

View file

@ -1,11 +1,8 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved. // Copyright 2020 The Nomulus Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file
// you may not use this file except in compliance with the License. // except in compliance with the License. // You may obtain a copy of the License at // //
// You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 //
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -14,24 +11,18 @@
package google.registry.model.host; package google.registry.model.host;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.io.Serializable;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
import javax.persistence.AttributeOverride;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Index;
import javax.persistence.IdClass; import javax.persistence.Table;
import javax.persistence.PostLoad;
/** /**
* A persisted history entry representing an EPP modification to a host. * A persisted history entry representing an EPP modification to a host.
@ -39,54 +30,27 @@ import javax.persistence.PostLoad;
* <p>In addition to the general history fields (e.g. action time, registrar ID) we also persist a * <p>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 * 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. * foreign-keyed fields in that class can refer to this object.
*
* <p>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 @Entity
@javax.persistence.Table( @Table(
indexes = { indexes = {
@javax.persistence.Index(columnList = "creationTime"), @Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "historyRegistrarId"), @Index(columnList = "historyRegistrarId"),
@javax.persistence.Index(columnList = "hostName"), @Index(columnList = "hostName"),
@javax.persistence.Index(columnList = "historyType"), @Index(columnList = "historyType"),
@javax.persistence.Index(columnList = "historyModificationTime") @Index(columnList = "historyModificationTime")
}) })
@EntitySubclass
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
@IdClass(HostHistoryId.class) @AttributeOverride(name = "repoId", column = @Column(name = "hostRepoId"))
public class HostHistory extends HistoryEntry implements UnsafeSerializable { public class HostHistory extends HistoryEntry {
// Store HostBase instead of Host so we don't pick up its @Id // Store HostBase instead of Host, so we don't pick up its @Id
// Nullable for the sake of pre-Registry-3.0 history objects // @Nullable for the sake of pre-Registry-3.0 history objects
@Nullable HostBase hostBase; @Nullable HostBase resource;
@Id
@Access(AccessType.PROPERTY)
public String getHostRepoId() {
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
parent = Key.create(Host.class, hostRepoId);
}
@Id
@Column(name = "historyRevisionId")
@Access(AccessType.PROPERTY)
@Override @Override
public long getId() { protected HostBase getResource() {
return super.getId(); return resource;
}
public HostHistoryId getHostHistoryId() {
return new HostHistoryId(getHostRepoId(), getId());
} }
/** /**
@ -96,19 +60,13 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
* <p>Will be absent for objects created prior to the Registry 3.0 SQL migration. * <p>Will be absent for objects created prior to the Registry 3.0 SQL migration.
*/ */
public Optional<HostBase> getHostBase() { public Optional<HostBase> getHostBase() {
return Optional.ofNullable(hostBase); return Optional.ofNullable(resource);
}
/** The key to the {@link Host} this is based off of. */
public VKey<Host> getParentVKey() {
return VKey.create(Host.class, getHostRepoId());
} }
/** Creates a {@link VKey} instance for this entity. */ /** Creates a {@link VKey} instance for this entity. */
@SuppressWarnings("unchecked")
@Override @Override
public VKey<HostHistory> createVKey() { public VKey<HostHistory> createVKey() {
return (VKey<HostHistory>) createVKey(Key.create(this)); return VKey.createSql(HostHistory.class, getHistoryEntryId());
} }
@Override @Override
@ -116,82 +74,13 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
return getHostBase().map(hostBase -> new Host.Builder().copyFrom(hostBase).build()); return getHostBase().map(hostBase -> new Host.Builder().copyFrom(hostBase).build());
} }
@PostLoad
void postLoad() {
// Normally Hibernate would see that the host fields are all null and would fill hostBase
// with a null object. Unfortunately, the updateTimestamp is never null in SQL.
if (hostBase != null && hostBase.getHostName() == null) {
hostBase = null;
}
if (hostBase != null && hostBase.getRepoId() == null) {
// hostBase hasn't been fully constructed yet, so it's ok to go in and mutate it. Though the
// use of the Builder is not necessarily problematic in this case, this is still safer as the
// Builder can do things like comparisons that compute the hash code.
hostBase.setRepoId(parent.getName());
}
}
/** Class to represent the composite primary key of {@link HostHistory} entity. */
public static class HostHistoryId extends ImmutableObject implements Serializable {
private String hostRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private HostHistoryId() {}
public HostHistoryId(String hostRepoId, long id) {
this.hostRepoId = hostRepoId;
this.id = id;
}
/**
* Returns the host repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
public String getHostRepoId() {
return hostRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
public long getId() {
return id;
}
/**
* Sets the host repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
this.hostRepoId = hostRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override @Override
public Builder asBuilder() { public Builder asBuilder() {
return new Builder(clone(this)); return new Builder(clone(this));
} }
public static class Builder extends HistoryEntry.Builder<HostHistory, Builder> { public static class Builder extends HistoryEntry.Builder<HostHistory, Builder> {
public Builder() {} public Builder() {}
@ -200,18 +89,9 @@ public class HostHistory extends HistoryEntry implements UnsafeSerializable {
super(instance); super(instance);
} }
public Builder setHost(@Nullable HostBase hostBase) { public Builder setHost(HostBase hostBase) {
// Nullable for the sake of pre-Registry-3.0 history objects getInstance().resource = hostBase;
if (hostBase == null) { return setRepoId(hostBase);
return this;
}
getInstance().hostBase = hostBase;
return super.setParent(hostBase);
}
public Builder setHostRepoId(String hostRepoId) {
getInstance().parent = Key.create(Host.class, hostRepoId);
return this;
} }
} }
} }

View file

@ -40,7 +40,6 @@ import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory; import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory; import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory; import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.EppHistoryVKeyTranslatorFactory;
import google.registry.model.translators.InetAddressTranslatorFactory; import google.registry.model.translators.InetAddressTranslatorFactory;
import google.registry.model.translators.ReadableInstantUtcTranslatorFactory; import google.registry.model.translators.ReadableInstantUtcTranslatorFactory;
import google.registry.model.translators.VKeyTranslatorFactory; import google.registry.model.translators.VKeyTranslatorFactory;
@ -118,7 +117,6 @@ public class ObjectifyService {
new CidrAddressBlockTranslatorFactory(), new CidrAddressBlockTranslatorFactory(),
new CurrencyUnitTranslatorFactory(), new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(), new DurationTranslatorFactory(),
new EppHistoryVKeyTranslatorFactory(),
new InetAddressTranslatorFactory(), new InetAddressTranslatorFactory(),
new MoneyStringTranslatorFactory(), new MoneyStringTranslatorFactory(),
new ReadableInstantUtcTranslatorFactory(), new ReadableInstantUtcTranslatorFactory(),

View file

@ -28,19 +28,17 @@ import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.OfyIdAllocation; import google.registry.model.annotations.OfyIdAllocation;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.DomainRenewData; import google.registry.model.domain.DomainRenewData;
import google.registry.model.eppoutput.EppResponse.ResponseData; import google.registry.model.eppoutput.EppResponse.ResponseData;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.HostPendingActionNotificationResponse;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.transfer.TransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.model.transfer.TransferResponse; import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferResponse.ContactTransferResponse; import google.registry.model.transfer.TransferResponse.ContactTransferResponse;
@ -197,10 +195,10 @@ public abstract class PollMessage extends ImmutableObject
} }
/** /**
* Gets the name of the underlying resource that the PollMessage is for, regardless of the type of * Gets the repo ID of the underlying resource that the PollMessage is for, regardless of the type
* the resource. * of the resource.
*/ */
public String getResourceName() { public String getResourceId() {
return domainRepoId != null ? domainRepoId : contactRepoId != null ? contactRepoId : hostRepoId; return domainRepoId != null ? domainRepoId : contactRepoId != null ? contactRepoId : hostRepoId;
} }
@ -262,34 +260,35 @@ public abstract class PollMessage extends ImmutableObject
return thisCastToDerived(); return thisCastToDerived();
} }
public B setDomainHistoryId(DomainHistoryId historyId) { public B setDomainHistoryId(HistoryEntryId historyId) {
getInstance().domainRepoId = historyId.getDomainRepoId(); getInstance().domainRepoId = historyId.getRepoId();
getInstance().domainHistoryRevisionId = historyId.getId(); getInstance().domainHistoryRevisionId = historyId.getRevisionId();
return thisCastToDerived(); return thisCastToDerived();
} }
public B setContactHistoryId(ContactHistoryId historyId) { public B setContactHistoryId(HistoryEntryId historyId) {
getInstance().contactRepoId = historyId.getContactRepoId(); getInstance().contactRepoId = historyId.getRepoId();
getInstance().contactHistoryRevisionId = historyId.getId(); getInstance().contactHistoryRevisionId = historyId.getRevisionId();
return thisCastToDerived(); return thisCastToDerived();
} }
public B setHostHistoryId(HostHistoryId historyId) { public B setHostHistoryId(HistoryEntryId historyId) {
getInstance().hostRepoId = historyId.getHostRepoId(); getInstance().hostRepoId = historyId.getRepoId();
getInstance().hostHistoryRevisionId = historyId.getId(); getInstance().hostHistoryRevisionId = historyId.getRevisionId();
return thisCastToDerived(); return thisCastToDerived();
} }
public B setHistoryEntry(HistoryEntry history) { public B setHistoryEntry(HistoryEntry history) {
HistoryEntryId historyId = history.getHistoryEntryId();
// Set the appropriate field based on the history entry type. // Set the appropriate field based on the history entry type.
if (history instanceof DomainHistory) { if (history instanceof DomainHistory) {
return setDomainHistoryId(((DomainHistory) history).getDomainHistoryId()); return setDomainHistoryId(historyId);
} }
if (history instanceof ContactHistory) { if (history instanceof ContactHistory) {
return setContactHistoryId(((ContactHistory) history).getContactHistoryId()); return setContactHistoryId(historyId);
} }
if (history instanceof HostHistory) { if (history instanceof HostHistory) {
return setHostHistoryId(((HostHistory) history).getHostHistoryId()); return setHostHistoryId(historyId);
} }
return thisCastToDerived(); return thisCastToDerived();
} }

View file

@ -21,7 +21,7 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable; import google.registry.model.UnsafeSerializable;
import google.registry.model.domain.DomainHistory.DomainHistoryId; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType; import javax.persistence.EnumType;
@ -48,6 +48,7 @@ public class DomainTransactionRecord extends ImmutableObject
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@Insignificant @Insignificant
@SuppressWarnings("unused")
Long id; Long id;
/** The TLD this record operates on. */ /** The TLD this record operates on. */
@ -66,8 +67,8 @@ public class DomainTransactionRecord extends ImmutableObject
* The time this Transaction takes effect (counting grace periods and other nuances). * The time this Transaction takes effect (counting grace periods and other nuances).
* *
* <p>Net adds, renews and transfers are modificationTime + 5 days for the grace period, while * <p>Net adds, renews and transfers are modificationTime + 5 days for the grace period, while
* Autorenews have a 45 day grace period. For deletions, this is the purge date of the domain. And * Autorenews have a 45-day grace period. For deletions, this is the purge date of the domain. And
* for restored names, this is the modificationTime, if done in the 30 day redemption period. * for restored names, this is the modificationTime, if done in the 30-day redemption period.
* *
* @see <a * @see <a
* href="https://www.icann.org/resources/unthemed-pages/registry-agmt-appc-10-2001-05-11-en"> * href="https://www.icann.org/resources/unthemed-pages/registry-agmt-appc-10-2001-05-11-en">
@ -178,8 +179,8 @@ public class DomainTransactionRecord extends ImmutableObject
} }
} }
public DomainHistoryId getDomainHistoryId() { public HistoryEntryId getHistoryEntryId() {
return new DomainHistoryId(domainRepoId, historyRevisionId); return new HistoryEntryId(domainRepoId, historyRevisionId);
} }
public DateTime getReportingTime() { public DateTime getReportingTime() {

View file

@ -15,75 +15,49 @@
package google.registry.model.reporting; package google.registry.model.reporting;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.googlecode.objectify.Key.getKind;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable; import google.registry.model.UnsafeSerializable;
import google.registry.model.annotations.ReportedOn; import google.registry.model.annotations.OfyIdAllocation;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactBase; import google.registry.model.contact.ContactBase;
import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactHistory;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory; 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.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.host.HostBase; import google.registry.model.host.HostBase;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.host.HostHistory.HostHistoryId; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides; import javax.persistence.AttributeOverrides;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EnumType; import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.MappedSuperclass; import javax.persistence.MappedSuperclass;
import javax.persistence.Transient; import javax.persistence.PostLoad;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.hibernate.collection.internal.PersistentSet;
import org.joda.time.DateTime; 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.
* *
* <p>Due to historical reasons this class is persisted only to Datastore. It has three subclasses * <p>This abstract class has three subclasses that include the parent resource itself and are
* that include the parent resource itself which are persisted to Cloud SQL. During migration this * persisted to Cloud SQL.
* 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.
*
* <p>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 @MappedSuperclass
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSerializable { @IdClass(HistoryEntryId.class)
public abstract class HistoryEntry extends ImmutableObject
implements Buildable, UnsafeSerializable {
/** Represents the type of history entry. */ /** Represents the type of history entry. */
public enum Type { public enum Type {
@ -127,36 +101,34 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
/** Resource was created by an escrow file import. */ /** Resource was created by an escrow file import. */
RDE_IMPORT, RDE_IMPORT,
/** /**
* A synthetic history entry created by a tool or back-end migration script outside of the scope * A synthetic history entry created by a tool or back-end migration script outside the scope of
* of usual EPP flows. These are sometimes needed to serve as parents for billing events or poll * usual EPP flows. These are sometimes needed to serve as parents for billing events or poll
* messages that otherwise wouldn't have a suitable parent. * messages that otherwise wouldn't have a suitable parent.
*/ */
SYNTHETIC SYNTHETIC
} }
/** /** The autogenerated id of this event. */
* The autogenerated id of this event. Note that, this field is marked as {@link Transient} in the @Id
* SQL schema, this is because the child class of {@link HistoryEntry}, e.g. {@link @OfyIdAllocation
* DomainHistory}, uses a composite primary key which the id is part of, and Hibernate requires @Column(nullable = false, name = "historyRevisionId")
* that all the {@link javax.persistence.Id} fields must be put in the exact same class. protected Long revisionId;
*/
@Id @Transient @VisibleForTesting public Long id;
/** The resource this event mutated. */ /**
@Parent @Transient protected Key<? extends EppResource> parent; * The repo ID of the embedded {@link EppResource} that this event mutated.
*
* <p>Note that the embedded EPP resource is of a base type for which the repo ID field is
* {@code @Transient}, which is NOT persisted as part of the embedded entity. After a {@link
* HistoryEntry} is loaded from SQL, the {@link #postLoad()} methods re-populates the field inside
* the EPP resource.
*/
@Id protected String repoId;
/** The type of history entry. */ /** The type of history entry. */
@Column(nullable = false, name = "historyType") @Column(nullable = false, name = "historyType")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
Type type; Type type;
/**
* The length of time that a create, allocate, renewal, or transfer request was issued for. Will
* be null for all other types.
*/
@Ignore @Transient // domain-specific
Period period;
/** /**
* The actual EPP xml of the command, stored as bytes to be agnostic of encoding. * The actual EPP xml of the command, stored as bytes to be agnostic of encoding.
* *
@ -166,28 +138,15 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
byte[] xmlBytes; byte[] xmlBytes;
/** The time the command occurred, represented by the ofy transaction time. */ /** The time the command occurred, represented by the ofy transaction time. */
@Index
@Column(nullable = false, name = "historyModificationTime") @Column(nullable = false, name = "historyModificationTime")
DateTime modificationTime; DateTime modificationTime;
/** The id of the registrar that sent the command. */ /** The id of the registrar that sent the command. */
@Index
@Column(name = "historyRegistrarId") @Column(name = "historyRegistrarId")
String clientId; String clientId;
/**
* For transfers, the id of the other registrar.
*
* <p>For requests and cancels, the other registrar is the losing party (because the registrar
* sending the EPP transfer command is the gaining party). For approves and rejects, the other
* registrar is the gaining party.
*/
@Transient // domain-specific
String otherClientId;
/** Transaction id that made this change, or null if the entry was not created by a flow. */ /** Transaction id that made this change, or null if the entry was not created by a flow. */
@Nullable @Nullable
@Ignore
@AttributeOverrides({ @AttributeOverrides({
@AttributeOverride( @AttributeOverride(
name = "clientTransactionId", name = "clientTransactionId",
@ -210,51 +169,33 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
@Column(name = "historyRequestedByRegistrar") @Column(name = "historyRequestedByRegistrar")
Boolean requestedByRegistrar; Boolean requestedByRegistrar;
/** public long getRevisionId() {
* Logging field for transaction reporting. // For some reason, Hibernate throws NPE during some initialization phases if we don't deal with
*
* <p>This will be empty for any HistoryEntry generated before this field was added. This will
* also be empty if the HistoryEntry refers to an EPP mutation that does not affect domain
* transaction counts (such as contact or host mutations).
*/
@Ignore
@Transient // domain-specific
@ImmutableObject.EmptySetToNull
protected Set<DomainTransactionRecord> 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 // the null case. Setting the id to 0L when it is null should be fine because 0L for primitive
// type is considered as null for wrapper class in the Hibernate context. // type is considered as null for wrapper class in the Hibernate context.
return id == null ? 0L : id; return revisionId == null ? 0L : revisionId;
} }
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */ protected abstract EppResource getResource();
@SuppressWarnings("UnusedMethod")
private void setId(long id) { public Class<? extends EppResource> getResourceType() {
this.id = id; return getResource().getClass();
} }
public Key<? extends EppResource> getParent() { public String getRepoId() {
return parent; return repoId;
}
public HistoryEntryId getHistoryEntryId() {
return new HistoryEntryId(repoId, revisionId);
} }
public Type getType() { public Type getType() {
return type; return type;
} }
public Period getPeriod() {
return period;
}
public byte[] getXmlBytes() { public byte[] getXmlBytes() {
return xmlBytes; return xmlBytes == null ? null : xmlBytes.clone();
} }
public DateTime getModificationTime() { public DateTime getModificationTime() {
@ -265,10 +206,6 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
return clientId; return clientId;
} }
public String getOtherRegistrarId() {
return otherClientId;
}
/** Returns the TRID, which may be null if the entry was not created by a normal flow. */ /** Returns the TRID, which may be null if the entry was not created by a normal flow. */
@Nullable @Nullable
public Trid getTrid() { public Trid getTrid() {
@ -287,146 +224,36 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
return requestedByRegistrar; return requestedByRegistrar;
} }
public Set<DomainTransactionRecord> getDomainTransactionRecords() { public abstract Optional<? extends EppResource> getResourceAtPointInTime();
return nullToEmptyImmutableCopy(domainTransactionRecords);
}
/** protected void processResourcePostLoad() {
* Throws an error when attempting to retrieve the EppResource at this point in time. // Post-Registry 3.0 entity should always have the resource field, whereas pre-Registry 3.0
* // will return a null resource.
* <p>Subclasses must override this to return the resource; it is non-abstract for legacy reasons if (getResource() != null && getResource().getRepoId() == null) {
* and objects created prior to the Registry 3.0 migration. // The repoId field in EppResource is transient, so we go ahead and set it to the value read
*/ // from SQL.
public Optional<? extends EppResource> getResourceAtPointInTime() { getResource().setRepoId(repoId);
throw new UnsupportedOperationException(
"Raw HistoryEntry objects do not store the resource at that point in time.");
}
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
@SuppressWarnings("UnusedMethod")
private void setPeriod(Period period) {
this.period = period;
}
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
@SuppressWarnings("UnusedMethod")
private void setOtherRegistrarId(String otherRegistrarId) {
this.otherClientId = otherRegistrarId;
}
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
@SuppressWarnings("UnusedMethod")
protected void setDomainTransactionRecords(
Set<DomainTransactionRecord> domainTransactionRecords) {
// Note: how we wish to treat this Hibernate setter depends on the current state of the object
// and what's passed in. The key principle is that we wish to maintain the link between parent
// and child objects, meaning that we should keep around whichever of the two sets (the
// parameter vs the class variable) and clear/populate that as appropriate.
//
// If the class variable is a PersistentSet and we overwrite it here, Hibernate will throw
// an exception "A collection with cascade=”all-delete-orphan” was no longer referenced by the
// owning entity instance". See https://stackoverflow.com/questions/5587482 for more details.
if (this.domainTransactionRecords instanceof PersistentSet) {
Set<DomainTransactionRecord> nonNullRecords = nullToEmpty(domainTransactionRecords);
this.domainTransactionRecords.retainAll(nonNullRecords);
this.domainTransactionRecords.addAll(nonNullRecords);
} else {
this.domainTransactionRecords = domainTransactionRecords;
} }
} }
/** @PostLoad
* Throws an error when trying to get a builder from a bare {@link HistoryEntry}. protected void postLoad() {
* processResourcePostLoad();
* <p>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<? extends HistoryEntry, ?> 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.");
} }
/** @Override
* Clones and returns a {@code HistoryEntry} objec public abstract Builder<? extends HistoryEntry, ?> asBuilder();
*
* <p>This is useful when converting a subclass to the base class to persist to Datastore.
*/
public HistoryEntry asHistoryEntry() {
HistoryEntry historyEntry = new HistoryEntry();
copy(this, historyEntry);
return historyEntry;
}
protected static void copy(HistoryEntry src, HistoryEntry dst) { protected static void copy(HistoryEntry src, HistoryEntry dst) {
dst.id = src.id; dst.revisionId = src.revisionId;
dst.parent = src.parent;
dst.type = src.type; dst.type = src.type;
dst.period = src.period;
dst.xmlBytes = src.xmlBytes; dst.xmlBytes = src.xmlBytes;
dst.modificationTime = src.modificationTime; dst.modificationTime = src.modificationTime;
dst.clientId = src.clientId; dst.clientId = src.clientId;
dst.otherClientId = src.otherClientId;
dst.trid = src.trid; dst.trid = src.trid;
dst.bySuperuser = src.bySuperuser; dst.bySuperuser = src.bySuperuser;
dst.reason = src.reason; dst.reason = src.reason;
dst.requestedByRegistrar = src.requestedByRegistrar; dst.requestedByRegistrar = src.requestedByRegistrar;
dst.domainTransactionRecords =
src.domainTransactionRecords == null
? null
: ImmutableSet.copyOf(src.domainTransactionRecords);
}
@SuppressWarnings("unchecked")
public HistoryEntry toChildHistoryEntity() {
String parentKind = getParent().getKind();
final HistoryEntry resultEntity;
// can't use a switch statement since we're calling getKind()
if (parentKind.equals(getKind(Domain.class))) {
resultEntity =
new DomainHistory.Builder().copyFrom(this).setDomainRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(Host.class))) {
resultEntity =
new HostHistory.Builder().copyFrom(this).setHostRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(Contact.class))) {
resultEntity =
new ContactHistory.Builder().copyFrom(this).setContactRepoId(parent.getName()).build();
} else {
throw new IllegalStateException(
String.format("Unknown kind of HistoryEntry parent %s", parentKind));
}
return resultEntity;
}
/** Creates a {@link VKey} instance from a {@link Key} instance. */
public static VKey<? extends HistoryEntry> createVKey(Key<HistoryEntry> key) {
String repoId = key.getParent().getName();
long id = key.getId();
Key<EppResource> parent = key.getParent();
String parentKind = parent.getKind();
if (parentKind.equals(getKind(Domain.class))) {
return VKey.create(
DomainHistory.class,
new DomainHistoryId(repoId, id),
Key.create(parent, DomainHistory.class, id));
} else if (parentKind.equals(getKind(Host.class))) {
return VKey.create(
HostHistory.class,
new HostHistoryId(repoId, id),
Key.create(parent, HostHistory.class, id));
} else if (parentKind.equals(getKind(Contact.class))) {
return VKey.create(
ContactHistory.class,
new ContactHistoryId(repoId, id),
Key.create(parent, ContactHistory.class, id));
} else {
throw new IllegalStateException(
String.format("Unknown kind of HistoryEntry parent %s", parentKind));
}
} }
/** A builder for {@link HistoryEntry} since it is immutable */ /** A builder for {@link HistoryEntry} since it is immutable */
@ -451,7 +278,8 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
@Override @Override
public T build() { public T build() {
// TODO(mcilwain): Add null checking for id/parent once DB migration is complete. checkArgumentNotNull(getInstance().getResource(), "EPP resource must be specified");
checkArgumentNotNull(getInstance().repoId, "repoId must be specified");
checkArgumentNotNull(getInstance().type, "History entry type must be specified"); checkArgumentNotNull(getInstance().type, "History entry type must be specified");
checkArgumentNotNull(getInstance().modificationTime, "Modification time must be specified"); checkArgumentNotNull(getInstance().modificationTime, "Modification time must be specified");
checkArgumentNotNull(getInstance().clientId, "Registrar ID must be specified"); checkArgumentNotNull(getInstance().clientId, "Registrar ID must be specified");
@ -462,19 +290,15 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
return super.build(); return super.build();
} }
public B setId(Long id) { public B setRevisionId(Long revisionId) {
getInstance().id = id; getInstance().revisionId = revisionId;
return thisCastToDerived(); return thisCastToDerived();
} }
protected B setParent(EppResource parent) { protected B setRepoId(EppResource eppResource) {
getInstance().parent = Key.create(parent); if (eppResource != null) {
return thisCastToDerived(); getInstance().repoId = eppResource.getRepoId();
} }
// Until we move completely to SQL, override this in subclasses (e.g. HostHistory) to set VKeys
protected B setParent(Key<? extends EppResource> parent) {
getInstance().parent = parent;
return thisCastToDerived(); return thisCastToDerived();
} }
@ -483,13 +307,8 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
return thisCastToDerived(); return thisCastToDerived();
} }
public B setPeriod(Period period) {
getInstance().period = period;
return thisCastToDerived();
}
public B setXmlBytes(byte[] xmlBytes) { public B setXmlBytes(byte[] xmlBytes) {
getInstance().xmlBytes = xmlBytes; getInstance().xmlBytes = xmlBytes == null ? null : xmlBytes.clone();
return thisCastToDerived(); return thisCastToDerived();
} }
@ -503,11 +322,6 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
return thisCastToDerived(); return thisCastToDerived();
} }
public B setOtherRegistrarId(String otherRegistrarId) {
getInstance().otherClientId = otherRegistrarId;
return thisCastToDerived();
}
public B setTrid(Trid trid) { public B setTrid(Trid trid) {
getInstance().trid = trid; getInstance().trid = trid;
return thisCastToDerived(); return thisCastToDerived();
@ -527,12 +341,6 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
getInstance().requestedByRegistrar = requestedByRegistrar; getInstance().requestedByRegistrar = requestedByRegistrar;
return thisCastToDerived(); return thisCastToDerived();
} }
public B setDomainTransactionRecords(
ImmutableSet<DomainTransactionRecord> domainTransactionRecords) {
getInstance().setDomainTransactionRecords(domainTransactionRecords);
return thisCastToDerived();
}
} }
public static <E extends EppResource> public static <E extends EppResource>
@ -549,4 +357,41 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
"Class %s does not have an associated HistoryEntry", parent.getClass().getName())); "Class %s does not have an associated HistoryEntry", parent.getClass().getName()));
} }
} }
/** Class to represent the composite primary key of a {@link HistoryEntry}. */
@Embeddable
@Access(AccessType.PROPERTY)
public static class HistoryEntryId extends ImmutableObject implements UnsafeSerializable {
private String repoId;
private long revisionId;
protected HistoryEntryId() {}
public HistoryEntryId(String repoId, long revisionId) {
this.repoId = repoId;
this.revisionId = revisionId;
}
/** Returns the {@code history_revision_id} of the {@link HistoryEntry}. */
public long getRevisionId() {
return revisionId;
}
@SuppressWarnings("unused")
private void setRevisionId(long revisionId) {
this.revisionId = revisionId;
}
/** Returns the {@code [domain|contact|host]_repo_id} of the {@link HistoryEntry}. */
public String getRepoId() {
return repoId;
}
@SuppressWarnings("unused")
private void setRepoId(String repoId) {
this.repoId = repoId;
}
}
} }

View file

@ -40,12 +40,7 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /** Retrieves {@link HistoryEntry} descendants (e.g. {@link DomainHistory}). */
* Retrieves {@link HistoryEntry} descendants (e.g. {@link DomainHistory}).
*
* <p>This class is configured to retrieve either from Datastore or SQL, depending on which database
* is currently considered the primary database.
*/
public class HistoryEntryDao { public class HistoryEntryDao {
public static ImmutableMap<Class<? extends EppResource>, Class<? extends HistoryEntry>> public static ImmutableMap<Class<? extends EppResource>, Class<? extends HistoryEntry>>
@ -58,15 +53,6 @@ public class HistoryEntryDao {
Host.class, Host.class,
HostHistory.class); HostHistory.class);
public static ImmutableMap<Class<? extends HistoryEntry>, String> REPO_ID_FIELD_NAMES =
ImmutableMap.of(
ContactHistory.class,
"contactRepoId",
DomainHistory.class,
"domainRepoId",
HostHistory.class,
"hostRepoId");
/** Loads all history objects in the times specified, including all types. */ /** Loads all history objects in the times specified, including all types. */
public static ImmutableList<HistoryEntry> loadAllHistoryObjects( public static ImmutableList<HistoryEntry> loadAllHistoryObjects(
DateTime afterTime, DateTime beforeTime) { DateTime afterTime, DateTime beforeTime) {
@ -74,59 +60,58 @@ public class HistoryEntryDao {
.transact( .transact(
() -> () ->
new ImmutableList.Builder<HistoryEntry>() new ImmutableList.Builder<HistoryEntry>()
.addAll( .addAll(loadAllHistoryObjects(ContactHistory.class, afterTime, beforeTime))
loadAllHistoryObjectsFromSql(ContactHistory.class, afterTime, beforeTime)) .addAll(loadAllHistoryObjects(DomainHistory.class, afterTime, beforeTime))
.addAll( .addAll(loadAllHistoryObjects(HostHistory.class, afterTime, beforeTime))
loadAllHistoryObjectsFromSql(DomainHistory.class, afterTime, beforeTime))
.addAll(loadAllHistoryObjectsFromSql(HostHistory.class, afterTime, beforeTime))
.build()); .build());
} }
/** Loads all history objects corresponding to the given {@link EppResource}. */ /** Loads all history objects corresponding to the given {@link EppResource}. */
public static ImmutableList<HistoryEntry> loadHistoryObjectsForResource( public static ImmutableList<HistoryEntry> loadHistoryObjectsForResource(
VKey<? extends EppResource> parentKey) { VKey<? extends EppResource> resourceKey) {
return loadHistoryObjectsForResource(parentKey, START_OF_TIME, END_OF_TIME); return loadHistoryObjectsForResource(resourceKey, START_OF_TIME, END_OF_TIME);
} }
/** /**
* Loads all history objects corresponding to the given {@link EppResource} and casted to the * Loads all history objects corresponding to the given {@link EppResource} and cast to the
* appropriate subclass. * appropriate subclass.
*/ */
public static <T extends HistoryEntry> ImmutableList<T> loadHistoryObjectsForResource( public static <T extends HistoryEntry> ImmutableList<T> loadHistoryObjectsForResource(
VKey<? extends EppResource> parentKey, Class<T> subclazz) { VKey<? extends EppResource> resourceKey, Class<T> subclazz) {
return loadHistoryObjectsForResource(parentKey, START_OF_TIME, END_OF_TIME, subclazz); return loadHistoryObjectsForResource(resourceKey, START_OF_TIME, END_OF_TIME, subclazz);
} }
/** Loads all history objects in the time period specified for the given {@link EppResource}. */ /** Loads all history objects in the time period specified for the given {@link EppResource}. */
public static ImmutableList<HistoryEntry> loadHistoryObjectsForResource( public static ImmutableList<HistoryEntry> loadHistoryObjectsForResource(
VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) { VKey<? extends EppResource> resourceKey, DateTime afterTime, DateTime beforeTime) {
return jpaTm() return jpaTm()
.transact(() -> loadHistoryObjectsForResourceFromSql(parentKey, afterTime, beforeTime)); .transact(() -> loadHistoryObjectsForResourceInternal(resourceKey, afterTime, beforeTime));
} }
/** /**
* Loads all history objects in the time period specified for the given {@link EppResource} and * Loads all history objects in the time period specified for the given {@link EppResource} and
* casted to the appropriate subclass. * cast to the appropriate subclass.
* *
* <p>Note that the subclass must be explicitly provided because we need the compile time * <p>Note that the subclass must be explicitly provided because we need compile time information
* information of T to return an {@code ImmutableList<T>}, even though at runtime we can call * of T to return an {@code ImmutableList<T>}, even though at runtime we can call {@link
* {@link #getHistoryClassFromParent(Class)} to obtain it, which we also did to confirm that the * #getHistoryClassFromParent(Class)} to obtain it, which we also did to confirm that the provided
* provided subclass is indeed correct. * subclass is indeed correct.
*/ */
public static <T extends HistoryEntry> ImmutableList<T> loadHistoryObjectsForResource( private static <T extends HistoryEntry> ImmutableList<T> loadHistoryObjectsForResource(
VKey<? extends EppResource> parentKey, VKey<? extends EppResource> resourceKey,
DateTime afterTime, DateTime afterTime,
DateTime beforeTime, DateTime beforeTime,
Class<T> subclazz) { Class<T> subclazz) {
Class<? extends HistoryEntry> expectedSubclazz = getHistoryClassFromParent(parentKey.getKind()); Class<? extends HistoryEntry> expectedSubclazz =
getHistoryClassFromParent(resourceKey.getKind());
checkArgument( checkArgument(
subclazz.equals(expectedSubclazz), subclazz.equals(expectedSubclazz),
"The supplied HistoryEntry subclass %s is incompatible with the EppResource %s, " "The supplied HistoryEntry subclass %s is incompatible with the EppResource %s, "
+ "use %s instead", + "use %s instead",
subclazz.getSimpleName(), subclazz.getSimpleName(),
parentKey.getKind().getSimpleName(), resourceKey.getKind().getSimpleName(),
expectedSubclazz.getSimpleName()); expectedSubclazz.getSimpleName());
return loadHistoryObjectsForResource(parentKey, afterTime, beforeTime).stream() return loadHistoryObjectsForResource(resourceKey, afterTime, beforeTime).stream()
.map(subclazz::cast) .map(subclazz::cast)
.collect(toImmutableList()); .collect(toImmutableList());
} }
@ -138,14 +123,14 @@ public class HistoryEntryDao {
.transact( .transact(
() -> () ->
Streams.concat( Streams.concat(
loadHistoryObjectFromSqlByRegistrars(ContactHistory.class, registrarIds), loadHistoryObjectByRegistrarsInternal(ContactHistory.class, registrarIds),
loadHistoryObjectFromSqlByRegistrars(DomainHistory.class, registrarIds), loadHistoryObjectByRegistrarsInternal(DomainHistory.class, registrarIds),
loadHistoryObjectFromSqlByRegistrars(HostHistory.class, registrarIds)) loadHistoryObjectByRegistrarsInternal(HostHistory.class, registrarIds))
.sorted(Comparator.comparing(HistoryEntry::getModificationTime)) .sorted(Comparator.comparing(HistoryEntry::getModificationTime))
.collect(toImmutableList())); .collect(toImmutableList()));
} }
private static <T extends HistoryEntry> Stream<T> loadHistoryObjectFromSqlByRegistrars( private static <T extends HistoryEntry> Stream<T> loadHistoryObjectByRegistrarsInternal(
Class<T> historyClass, ImmutableCollection<String> registrarIds) { Class<T> historyClass, ImmutableCollection<String> registrarIds) {
return jpaTm() return jpaTm()
.criteriaQuery( .criteriaQuery(
@ -155,45 +140,33 @@ public class HistoryEntryDao {
.getResultStream(); .getResultStream();
} }
private static ImmutableList<HistoryEntry> loadHistoryObjectsForResourceFromSql( private static ImmutableList<HistoryEntry> loadHistoryObjectsForResourceInternal(
VKey<? extends EppResource> parentKey, DateTime afterTime, DateTime beforeTime) { VKey<? extends EppResource> resourceKey, DateTime afterTime, DateTime beforeTime) {
// The class we're searching from is based on which parent type (e.g. Domain) we have // The class we're searching from is based on which resource type (e.g. Domain) we have
Class<? extends HistoryEntry> historyClass = getHistoryClassFromParent(parentKey.getKind()); Class<? extends HistoryEntry> historyClass = getHistoryClassFromParent(resourceKey.getKind());
// The field representing repo ID unfortunately varies by history class
String repoIdFieldName = getRepoIdFieldNameFromHistoryClass(historyClass);
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
CriteriaQuery<? extends HistoryEntry> criteriaQuery = CriteriaQuery<? extends HistoryEntry> criteriaQuery =
CriteriaQueryBuilder.create(historyClass) CriteriaQueryBuilder.create(historyClass)
.where("modificationTime", criteriaBuilder::greaterThanOrEqualTo, afterTime) .where("modificationTime", criteriaBuilder::greaterThanOrEqualTo, afterTime)
.where("modificationTime", criteriaBuilder::lessThanOrEqualTo, beforeTime) .where("modificationTime", criteriaBuilder::lessThanOrEqualTo, beforeTime)
.where(repoIdFieldName, criteriaBuilder::equal, parentKey.getSqlKey().toString()) .where("repoId", criteriaBuilder::equal, resourceKey.getSqlKey().toString())
.orderByAsc("id") .orderByAsc("revisionId")
.orderByAsc("modificationTime")
.build(); .build();
return ImmutableList.sortedCopyOf( return ImmutableList.copyOf(jpaTm().criteriaQuery(criteriaQuery).getResultList());
Comparator.comparing(HistoryEntry::getModificationTime),
jpaTm().criteriaQuery(criteriaQuery).getResultList());
} }
public static Class<? extends HistoryEntry> getHistoryClassFromParent( public static Class<? extends HistoryEntry> getHistoryClassFromParent(
Class<? extends EppResource> parent) { Class<? extends EppResource> resourceType) {
if (!RESOURCE_TYPES_TO_HISTORY_TYPES.containsKey(parent)) { if (!RESOURCE_TYPES_TO_HISTORY_TYPES.containsKey(resourceType)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format("Unknown history type for parent %s", parent.getName())); String.format("Unknown history type for resourceType %s", resourceType.getName()));
} }
return RESOURCE_TYPES_TO_HISTORY_TYPES.get(parent); return RESOURCE_TYPES_TO_HISTORY_TYPES.get(resourceType);
} }
public static String getRepoIdFieldNameFromHistoryClass( private static <T extends HistoryEntry> List<T> loadAllHistoryObjects(
Class<? extends HistoryEntry> historyClass) {
if (!REPO_ID_FIELD_NAMES.containsKey(historyClass)) {
throw new IllegalArgumentException(
String.format("Unknown history type %s", historyClass.getName()));
}
return REPO_ID_FIELD_NAMES.get(historyClass);
}
private static <T extends HistoryEntry> List<T> loadAllHistoryObjectsFromSql(
Class<T> historyClass, DateTime afterTime, DateTime beforeTime) { Class<T> historyClass, DateTime afterTime, DateTime beforeTime) {
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
return jpaTm() return jpaTm()

View file

@ -1,107 +0,0 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.function.Function.identity;
import com.google.appengine.api.datastore.Key;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.EppHistoryVKey;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.annotation.Nullable;
/** Translator factory for {@link EppHistoryVKey}. */
public class EppHistoryVKeyTranslatorFactory
extends AbstractSimpleTranslatorFactory<EppHistoryVKey, Key> {
public EppHistoryVKeyTranslatorFactory() {
super(EppHistoryVKey.class);
}
// This map is used when we need to convert the raw Datastore key to its VKey instance. We have
// one dedicated VKey class, e.g. DomainHistoryVKey, for each such kind of entity, and we need
// a way to map the raw Datastore key to its VKey class. So, we use the kind path as the key of
// the map, and the kind path is created by concatenating all the kind strings in a raw Datastore
// key, e.g. the map key for ContactPollMessageVKey is "Contact/HistoryEntry/PollMessage".
@VisibleForTesting
static final ImmutableMap<String, Class<? extends EppHistoryVKey>> kindPathToVKeyClass =
ImmutableSet.of(DomainHistoryVKey.class).stream()
.collect(toImmutableMap(EppHistoryVKeyTranslatorFactory::getKindPath, identity()));
/**
* Gets the kind path string for the given {@link Class}.
*
* <p>This method calls the getKindPath method on an instance of the given {@link Class} to get
* the kind path string.
*/
private static String getKindPath(Class<? extends EppHistoryVKey> clazz) {
try {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object instance = constructor.newInstance();
Method getKindPathMethod = EppHistoryVKey.class.getDeclaredMethod("getKindPath");
getKindPathMethod.setAccessible(true);
return (String) getKindPathMethod.invoke(instance);
} catch (Throwable t) {
throw new IllegalStateException(t);
}
}
@Override
SimpleTranslator<EppHistoryVKey, Key> createTranslator() {
return new SimpleTranslator<EppHistoryVKey, Key>() {
@Nullable
@Override
public EppHistoryVKey loadValue(@Nullable Key datastoreValue) {
if (datastoreValue == null) {
return null;
} else {
com.googlecode.objectify.Key<?> ofyKey =
com.googlecode.objectify.Key.create(datastoreValue);
String kindPath = EppHistoryVKey.createKindPath(ofyKey);
if (kindPathToVKeyClass.containsKey(kindPath)) {
Class<? extends EppHistoryVKey> vKeyClass = kindPathToVKeyClass.get(kindPath);
try {
Method createVKeyMethod =
vKeyClass.getDeclaredMethod("create", com.googlecode.objectify.Key.class);
return (EppHistoryVKey) createVKeyMethod.invoke(null, ofyKey);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"Missing static method create(com.googlecode.objectify.Key) on " + vKeyClass);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Error invoking createVKey on " + vKeyClass, e);
}
} else {
throw new IllegalStateException(
"Missing EppHistoryVKey implementation for kind path: " + kindPath);
}
}
}
@Nullable
@Override
public Key saveValue(@Nullable EppHistoryVKey pojoValue) {
throw new UnsupportedOperationException("saveValue for EppHistory keys removed.");
}
};
}
}

View file

@ -1,57 +0,0 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.Key;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.reporting.HistoryEntry;
import java.io.Serializable;
import javax.persistence.Embeddable;
/** {@link VKey} for {@link HistoryEntry} which parent is {@link Domain}. */
@Embeddable
public class DomainHistoryVKey extends EppHistoryVKey<HistoryEntry, Domain> {
// Hibernate requires a default constructor
private DomainHistoryVKey() {}
private DomainHistoryVKey(String repoId, long historyRevisionId) {
super(repoId, historyRevisionId);
}
@Override
public Serializable createSqlKey() {
return new DomainHistoryId(repoId, historyRevisionId);
}
/** Creates {@link DomainHistoryVKey} from the given {@link Key} instance. */
public static DomainHistoryVKey create(Key<? extends HistoryEntry> ofyKey) {
checkArgumentNotNull(ofyKey, "ofyKey must be specified");
String repoId = ofyKey.getParent().getName();
long historyRevisionId = ofyKey.getId();
return new DomainHistoryVKey(repoId, historyRevisionId);
}
public VKey<? extends HistoryEntry> createDomainHistoryVKey() {
return VKey.create(
DomainHistory.class,
createSqlKey(),
Key.create(Key.create(Domain.class, repoId), DomainHistory.class, historyRevisionId));
}
}

View file

@ -1,106 +0,0 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence;
import com.google.common.base.Joiner;
import com.googlecode.objectify.Key;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.reporting.HistoryEntry;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.io.Serializable;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.MappedSuperclass;
/**
* Base class for {@link VKey} which ofyKey has a {@link HistoryEntry} key as its parent and a key
* for EPP resource as its grandparent.
*
* <p>For such a {@link VKey}, we need to provide two type parameters to indicate the type of {@link
* VKey} itself and the type of EPP resource respectively.
*
* @param <K> type of the {@link VKey}
* @param <E> type of the EPP resource that the key belongs to
*/
@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class EppHistoryVKey<K extends ImmutableObject, E extends EppResource>
extends ImmutableObject implements Serializable {
private static final long serialVersionUID = -3906580677709539818L;
String repoId;
Long historyRevisionId;
// Hibernate requires a default constructor.
EppHistoryVKey() {}
EppHistoryVKey(String repoId, long historyRevisionId) {
this.repoId = repoId;
this.historyRevisionId = historyRevisionId;
}
/**
* Returns the kind path for the ofyKey in this instance.
*
* <p>This method is only used reflectively by {@link EppHistoryVKeyTranslatorFactory} to get the
* kind path for a given {@link EppHistoryVKey} instance so it is marked as a private method.
*
* @see #createKindPath(Key)
*/
@SuppressWarnings("unused")
private String getKindPath() {
String eppKind = Key.getKind(new TypeInstantiator<E>(getClass()) {}.getExactType());
String keyKind = Key.getKind(new TypeInstantiator<K>(getClass()) {}.getExactType());
if (keyKind.equals(Key.getKind(HistoryEntry.class))) {
return createKindPath(eppKind, keyKind);
} else {
return createKindPath(eppKind, Key.getKind(HistoryEntry.class), keyKind);
}
}
/**
* Creates the kind path for the given ofyKey}.
*
* <p>The kind path is a string including all kind names(delimited by slash) of a hierarchical
* {@link Key}, e.g., the kind path for BillingEvent.OneTime is "Domain/HistoryEntry/OneTime".
*/
@Nullable
public static String createKindPath(@Nullable Key<?> ofyKey) {
if (ofyKey == null) {
return null;
} else if (ofyKey.getParent() == null) {
return ofyKey.getKind();
} else {
return createKindPath(createKindPath(ofyKey.getParent()), ofyKey.getKind());
}
}
private static String createKindPath(String... kinds) {
return Joiner.on("/").join(kinds);
}
/** Creates a {@link VKey} from this instance. */
@Override
public VKey<K> createVKey() {
Class<K> vKeyType = new TypeInstantiator<K>(getClass()) {}.getExactType();
return VKey.createSql(vKeyType, createSqlKey());
}
public abstract Serializable createSqlKey();
}

View file

@ -103,12 +103,21 @@ public class RdapJsonFormatter {
private DateTime requestTime = null; private DateTime requestTime = null;
@Inject @Config("rdapTos") ImmutableList<String> rdapTos; @Inject
@Inject @Config("rdapTosStaticUrl") @Nullable String rdapTosStaticUrl; @Config("rdapTos")
ImmutableList<String> rdapTos;
@Inject
@Config("rdapTosStaticUrl")
@Nullable
String rdapTosStaticUrl;
@Inject @FullServletPath String fullServletPath; @Inject @FullServletPath String fullServletPath;
@Inject RdapAuthorization rdapAuthorization; @Inject RdapAuthorization rdapAuthorization;
@Inject Clock clock; @Inject Clock clock;
@Inject RdapJsonFormatter() {}
@Inject
RdapJsonFormatter() {}
/** /**
* What type of data to generate. * What type of data to generate.
@ -156,15 +165,15 @@ public class RdapJsonFormatter {
/** /**
* JPQL query template for finding the latest history entry per event type for an EPP entity. * JPQL query template for finding the latest history entry per event type for an EPP entity.
* *
* <p>User should replace '%entityName%', '%repoIdField%', and '%repoIdValue%' with valid values. * <p>User should replace '%entityName%' and '%repoIdValue%' with valid values. A {@code
* A DomainHistory query may look like below: {@code select e from DomainHistory e where * DomainHistory} query may look like below: {@code select e from DomainHistory e where
* domainRepoId = '17-Q9JYB4C' and modificationTime in (select max(modificationTime) from * domainRepoId = '17-Q9JYB4C' and modificationTime in (select max(modificationTime) from
* DomainHistory where domainRepoId = '17-Q9JYB4C' and type is not null group by type)} * DomainHistory where domainRepoId = '17-Q9JYB4C' and type is not null group by type)}
*/ */
private static final String GET_LAST_HISTORY_BY_TYPE_JPQL_TEMPLATE = private static final String GET_LAST_HISTORY_BY_TYPE_JPQL_TEMPLATE =
"select e from %entityName% e where %repoIdField% = '%repoIdValue%' and modificationTime in " "select e from %entityName% e where repoId = '%repoIdValue%' and modificationTime in "
+ " (select max(modificationTime) from %entityName% where " + " (select max(modificationTime) from %entityName% where "
+ " %repoIdField% = '%repoIdValue%' and type is not null group by type)"; + " repoId = '%repoIdValue%' and type is not null group by type)";
/** Map of EPP status values to the RDAP equivalents. */ /** Map of EPP status values to the RDAP equivalents. */
private static final ImmutableMap<StatusValue, RdapStatus> STATUS_TO_RDAP_STATUS_MAP = private static final ImmutableMap<StatusValue, RdapStatus> STATUS_TO_RDAP_STATUS_MAP =
@ -886,19 +895,17 @@ public class RdapJsonFormatter {
// 2.3.2.3 An event of *eventAction* type *transfer*, with the last date and time that the // 2.3.2.3 An event of *eventAction* type *transfer*, with the last date and time that the
// domain was transferred. The event of *eventAction* type *transfer* MUST be omitted if the // domain was transferred. The event of *eventAction* type *transfer* MUST be omitted if the
// domain name has not been transferred since it was created. // domain name has not been transferred since it was created.
VKey<? extends EppResource> resourceVkey = resource.createVKey(); VKey<? extends EppResource> resourceVkey = resource.createVKey();
Class<? extends HistoryEntry> historyClass = Class<? extends HistoryEntry> historyClass =
HistoryEntryDao.getHistoryClassFromParent(resourceVkey.getKind()); HistoryEntryDao.getHistoryClassFromParent(resourceVkey.getKind());
String entityName = historyClass.getAnnotation(Entity.class).name(); String entityName = historyClass.getAnnotation(Entity.class).name();
if (Strings.isNullOrEmpty(entityName)) { if (Strings.isNullOrEmpty(entityName)) {
entityName = historyClass.getSimpleName(); entityName = historyClass.getSimpleName();
} }
String repoIdFieldName = HistoryEntryDao.getRepoIdFieldNameFromHistoryClass(historyClass); String jpql =
String jpql = GET_LAST_HISTORY_BY_TYPE_JPQL_TEMPLATE
GET_LAST_HISTORY_BY_TYPE_JPQL_TEMPLATE .replace("%entityName%", entityName)
.replace("%entityName%", entityName) .replace("%repoIdValue%", resourceVkey.getSqlKey().toString());
.replace("%repoIdField%", repoIdFieldName)
.replace("%repoIdValue%", resourceVkey.getSqlKey().toString());
Iterable<HistoryEntry> historyEntries = Iterable<HistoryEntry> historyEntries =
replicaJpaTm() replicaJpaTm()
.transact( .transact(
@ -974,9 +981,7 @@ public class RdapJsonFormatter {
/** Creates an RDAP event object as defined by RFC 9083. */ /** Creates an RDAP event object as defined by RFC 9083. */
private static Event makeEvent( private static Event makeEvent(
EventAction eventAction, @Nullable String eventActor, DateTime eventDate) { EventAction eventAction, @Nullable String eventActor, DateTime eventDate) {
Event.Builder builder = Event.builder() Event.Builder builder = Event.builder().setEventAction(eventAction).setEventDate(eventDate);
.setEventAction(eventAction)
.setEventDate(eventDate);
if (eventActor != null) { if (eventActor != null) {
builder.setEventActor(eventActor); builder.setEventActor(eventActor);
} }
@ -1042,10 +1047,7 @@ public class RdapJsonFormatter {
addressArray.add( addressArray.add(
new Locale("en", nullToEmpty(address.getCountryCode())) new Locale("en", nullToEmpty(address.getCountryCode()))
.getDisplayCountry(new Locale("en"))); .getDisplayCountry(new Locale("en")));
vcardArrayBuilder.add(Vcard.create( vcardArrayBuilder.add(Vcard.create("adr", "text", addressArray));
"adr",
"text",
addressArray));
} }
/** Creates a vCard phone number entry. */ /** Creates a vCard phone number entry. */
@ -1074,8 +1076,7 @@ public class RdapJsonFormatter {
private static ImmutableSet<RdapStatus> makeStatusValueList( private static ImmutableSet<RdapStatus> makeStatusValueList(
ImmutableSet<StatusValue> statusValues, boolean isRedacted, boolean isDeleted) { ImmutableSet<StatusValue> statusValues, boolean isRedacted, boolean isDeleted) {
Stream<RdapStatus> stream = Stream<RdapStatus> stream =
statusValues statusValues.stream()
.stream()
.map(status -> STATUS_TO_RDAP_STATUS_MAP.getOrDefault(status, RdapStatus.OBSCURED)); .map(status -> STATUS_TO_RDAP_STATUS_MAP.getOrDefault(status, RdapStatus.OBSCURED));
if (isRedacted) { if (isRedacted) {
stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED)); stream = Streams.concat(stream, Stream.of(RdapStatus.REMOVED));
@ -1083,24 +1084,19 @@ public class RdapJsonFormatter {
if (isDeleted) { if (isDeleted) {
stream = stream =
Streams.concat( Streams.concat(
stream.filter(not(RdapStatus.ACTIVE::equals)), stream.filter(not(RdapStatus.ACTIVE::equals)), Stream.of(RdapStatus.INACTIVE));
Stream.of(RdapStatus.INACTIVE));
} }
return stream return stream
.sorted(Ordering.natural().onResultOf(RdapStatus::getDisplayName)) .sorted(Ordering.natural().onResultOf(RdapStatus::getDisplayName))
.collect(toImmutableSet()); .collect(toImmutableSet());
} }
/** /** Create a link relative to the RDAP server endpoint. */
* Create a link relative to the RDAP server endpoint.
*/
String makeRdapServletRelativeUrl(String part, String... moreParts) { String makeRdapServletRelativeUrl(String part, String... moreParts) {
return makeServerRelativeUrl(fullServletPath, part, moreParts); return makeServerRelativeUrl(fullServletPath, part, moreParts);
} }
/** /** Create a link relative to some base server */
* Create a link relative to some base server
*/
static String makeServerRelativeUrl(String baseServer, String part, String... moreParts) { static String makeServerRelativeUrl(String baseServer, String part, String... moreParts) {
String relativePath = Paths.get(part, moreParts).toString(); String relativePath = Paths.get(part, moreParts).toString();
if (baseServer.endsWith("/")) { if (baseServer.endsWith("/")) {
@ -1117,11 +1113,7 @@ public class RdapJsonFormatter {
*/ */
private Link makeSelfLink(String type, String name) { private Link makeSelfLink(String type, String name) {
String url = makeRdapServletRelativeUrl(type, name); String url = makeRdapServletRelativeUrl(type, name);
return Link.builder() return Link.builder().setRel("self").setHref(url).setType("application/rdap+json").build();
.setRel("self")
.setHref(url)
.setType("application/rdap+json")
.build();
} }
/** /**

View file

@ -47,7 +47,7 @@ import org.joda.time.Duration;
/** /**
* Utility functions for validating and applying {@link RegistryLock}s. * Utility functions for validating and applying {@link RegistryLock}s.
* *
* <p>For both locks and unlocks, a lock must be requested via the createRegistry*Requst methods * <p>For both locks and unlocks, a lock must be requested via the createRegistry*Request methods
* then verified through the verifyAndApply* methods. These methods will verify that the domain in * then verified through the verifyAndApply* methods. These methods will verify that the domain in
* question is in a lock/unlockable state and will return the lock object. * question is in a lock/unlockable state and will return the lock object.
*/ */
@ -57,7 +57,7 @@ public final class DomainLockUtils {
private final StringGenerator stringGenerator; private final StringGenerator stringGenerator;
private final String registryAdminRegistrarId; private final String registryAdminRegistrarId;
private CloudTasksUtils cloudTasksUtils; private final CloudTasksUtils cloudTasksUtils;
@Inject @Inject
public DomainLockUtils( public DomainLockUtils(
@ -152,7 +152,7 @@ public final class DomainLockUtils {
tm().transact(() -> removeLockStatuses(newLock, isAdmin, now)); tm().transact(() -> removeLockStatuses(newLock, isAdmin, now));
return newLock; return newLock;
}); });
// Submit relock outside of the transaction to make sure that it fully succeeded // Submit relock outside the transaction to make sure that it fully succeeded
submitRelockIfNecessary(lock); submitRelockIfNecessary(lock);
return lock; return lock;
} }
@ -200,7 +200,7 @@ public final class DomainLockUtils {
tm().transact(() -> removeLockStatuses(result, isAdmin, now)); tm().transact(() -> removeLockStatuses(result, isAdmin, now));
return result; return result;
}); });
// Submit relock outside of the transaction to make sure that it fully succeeded // Submit relock outside the transaction to make sure that it fully succeeded
submitRelockIfNecessary(lock); submitRelockIfNecessary(lock);
return lock; return lock;
} }

View file

@ -22,8 +22,8 @@ import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.googlecode.objectify.Key;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import java.util.Collection; import java.util.Collection;
@ -62,13 +62,12 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
if (loadedTokens.containsKey(token)) { if (loadedTokens.containsKey(token)) {
AllocationToken loadedToken = loadedTokens.get(token); AllocationToken loadedToken = loadedTokens.get(token);
System.out.println(loadedToken.toString()); System.out.println(loadedToken.toString());
if (!loadedToken.getRedemptionHistoryEntry().isPresent()) { if (!loadedToken.getRedemptionHistoryId().isPresent()) {
System.out.printf("Token %s was not redeemed.\n", token); System.out.printf("Token %s was not redeemed.\n", token);
} else { } else {
Key<Domain> domainOfyKey = VKey<Domain> domainKey =
loadedToken.getRedemptionHistoryEntry().get().getOfyKey().getParent(); VKey.createSql(Domain.class, loadedToken.getRedemptionHistoryId().get().getRepoId());
Domain domain = Domain domain = domains.get(domainKey);
domains.get(VKey.create(Domain.class, domainOfyKey.getName(), domainOfyKey));
if (domain == null) { if (domain == null) {
System.out.printf("ERROR: Token %s was redeemed but domain can't be loaded.\n", token); System.out.printf("ERROR: Token %s was redeemed but domain can't be loaded.\n", token);
} else { } else {
@ -89,12 +88,11 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
Collection<AllocationToken> tokens) { Collection<AllocationToken> tokens) {
ImmutableList<VKey<Domain>> domainKeys = ImmutableList<VKey<Domain>> domainKeys =
tokens.stream() tokens.stream()
.map(AllocationToken::getRedemptionHistoryEntry) .map(AllocationToken::getRedemptionHistoryId)
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.map(key -> tm().loadByKey(key)) .map(hi -> tm().loadByKey(VKey.createSql(DomainHistory.class, hi)))
.map(he -> (Key<Domain>) he.getParent()) .map(dh -> VKey.createSql(Domain.class, dh.getRepoId()))
.map(key -> VKey.create(Domain.class, key.getName(), key))
.collect(toImmutableList()); .collect(toImmutableList());
ImmutableMap.Builder<VKey<Domain>, Domain> domainsBuilder = new ImmutableMap.Builder<>(); ImmutableMap.Builder<VKey<Domain>, Domain> domainsBuilder = new ImmutableMap.Builder<>();
for (List<VKey<Domain>> keys : Lists.partition(domainKeys, BATCH_SIZE)) { for (List<VKey<Domain>> keys : Lists.partition(domainKeys, BATCH_SIZE)) {

View file

@ -70,8 +70,8 @@ final class GetHistoryEntriesCommand implements CommandWithRemoteApi {
"Client: %s\nTime: %s\nClient TRID: %s\nServer TRID: %s\n%s\n", "Client: %s\nTime: %s\nClient TRID: %s\nServer TRID: %s\n%s\n",
entry.getRegistrarId(), entry.getRegistrarId(),
entry.getModificationTime(), entry.getModificationTime(),
(entry.getTrid() == null) ? null : entry.getTrid().getClientTransactionId().orElse(null), entry.getTrid() == null ? null : entry.getTrid().getClientTransactionId().orElse(null),
(entry.getTrid() == null) ? null : entry.getTrid().getServerTransactionId(), entry.getTrid() == null ? null : entry.getTrid().getServerTransactionId(),
entry.getXmlBytes() == null entry.getXmlBytes() == null
? String.format("[no XML stored for %s]\n", entry.getType()) ? String.format("[no XML stored for %s]\n", entry.getType())
: XmlTransformer.prettyPrint(entry.getXmlBytes())); : XmlTransformer.prettyPrint(entry.getXmlBytes()));

View file

@ -132,7 +132,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
if (!domainsExpiringTooSoon.isEmpty()) { if (!domainsExpiringTooSoon.isEmpty()) {
System.err.printf("Domains expiring too soon: %s\n\n", domainsExpiringTooSoon); System.err.printf("Domains expiring too soon: %s\n\n", domainsExpiringTooSoon);
} }
checkArgument(!foundInvalidDomains, "Aborting because some domains cannot be unrewed"); checkArgument(!foundInvalidDomains, "Aborting because some domains cannot be unrenewed");
} }
@Override @Override
@ -218,7 +218,7 @@ class UnrenewDomainCommand extends ConfirmingCommand implements CommandWithRemot
// End the old autorenew billing event and poll message now. // End the old autorenew billing event and poll message now.
Recurring existingRecurring = tm().loadByKey(domain.getAutorenewBillingEvent()); Recurring existingRecurring = tm().loadByKey(domain.getAutorenewBillingEvent());
updateAutorenewRecurrenceEndTime( updateAutorenewRecurrenceEndTime(
domain, existingRecurring, now, domainHistory.getDomainHistoryId()); domain, existingRecurring, now, domainHistory.getHistoryEntryId());
Domain newDomain = Domain newDomain =
domain domain
.asBuilder() .asBuilder()

View file

@ -103,7 +103,7 @@ public class CreateCancellationsForOneTimesCommand extends ConfirmingCommand
new Cancellation.Builder() new Cancellation.Builder()
.setOneTimeEventKey(oneTime.createVKey()) .setOneTimeEventKey(oneTime.createVKey())
.setBillingTime(oneTime.getBillingTime()) .setBillingTime(oneTime.getBillingTime())
.setDomainHistoryId(oneTime.getDomainHistoryId()) .setDomainHistoryId(oneTime.getHistoryEntryId())
.setRegistrarId(oneTime.getRegistrarId()) .setRegistrarId(oneTime.getRegistrarId())
.setEventTime(oneTime.getEventTime()) .setEventTime(oneTime.getEventTime())
.setReason(BillingEvent.Reason.ERROR) .setReason(BillingEvent.Reason.ERROR)

View file

@ -37,7 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
import google.registry.flows.custom.DomainPricingCustomLogic; import google.registry.flows.custom.DomainPricingCustomLogic;
import google.registry.flows.domain.DomainPricingLogic; import google.registry.flows.domain.DomainPricingLogic;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
@ -126,7 +125,7 @@ public class ExpandRecurringBillingEventsActionTest {
action.response = new FakeResponse(); action.response = new FakeResponse();
action.run(); action.run();
// Need to save the current test time before running the action, which increments the clock. // Need to save the current test time before running the action, which increments the clock.
// The execution time (e. g. transaction time) is captured when the action starts running so // The execution time (e.g. transaction time) is captured when the action starts running so
// the passage of time afterward does not affect the timestamp stored in the billing events. // the passage of time afterward does not affect the timestamp stored in the billing events.
currentTestTime = clock.nowUtc(); currentTestTime = clock.nowUtc();
} }
@ -139,13 +138,13 @@ public class ExpandRecurringBillingEventsActionTest {
private void assertHistoryEntryMatches( private void assertHistoryEntryMatches(
Domain domain, Domain domain,
HistoryEntry actual, DomainHistory actual,
String registrarId, String registrarId,
DateTime billingTime, DateTime billingTime,
boolean shouldHaveTxRecord) { boolean shouldHaveTxRecord) {
assertThat(actual.getBySuperuser()).isFalse(); assertThat(actual.getBySuperuser()).isFalse();
assertThat(actual.getRegistrarId()).isEqualTo(registrarId); assertThat(actual.getRegistrarId()).isEqualTo(registrarId);
assertThat(actual.getParent()).isEqualTo(Key.create(domain)); assertThat(actual.getRepoId()).isEqualTo(domain.getRepoId());
assertThat(actual.getPeriod()).isEqualTo(Period.create(1, YEARS)); assertThat(actual.getPeriod()).isEqualTo(Period.create(1, YEARS));
assertThat(actual.getReason()) assertThat(actual.getReason())
.isEqualTo("Domain autorenewal by ExpandRecurringBillingEventsAction"); .isEqualTo("Domain autorenewal by ExpandRecurringBillingEventsAction");
@ -298,7 +297,7 @@ public class ExpandRecurringBillingEventsActionTest {
runAction(); runAction();
List<DomainHistory> persistedEntries = List<DomainHistory> persistedEntries =
getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class); getHistoryEntriesOfType(domain, DOMAIN_AUTORENEW, DomainHistory.class);
for (HistoryEntry persistedEntry : persistedEntries) { for (DomainHistory persistedEntry : persistedEntries) {
assertHistoryEntryMatches( assertHistoryEntryMatches(
domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true); domain, persistedEntry, "TheRegistrar", DateTime.parse("2000-02-19T00:00:00Z"), true);
} }

View file

@ -286,13 +286,12 @@ class WipeOutContactHistoryPiiActionTest {
void wipeOutContactHistoryData_wipesOutNoEntity() { void wipeOutContactHistoryData_wipesOutNoEntity() {
jpaTm() jpaTm()
.transact( .transact(
() -> { () ->
assertThat( assertThat(
action.wipeOutContactHistoryData( action.wipeOutContactHistoryData(
action.getNextContactHistoryEntitiesWithPiiBatch( action.getNextContactHistoryEntitiesWithPiiBatch(
clock.nowUtc().minusMonths(MIN_MONTHS_BEFORE_WIPE_OUT)))) clock.nowUtc().minusMonths(MIN_MONTHS_BEFORE_WIPE_OUT))))
.isEqualTo(0); .isEqualTo(0));
});
} }
@Test @Test
@ -317,9 +316,9 @@ class WipeOutContactHistoryPiiActionTest {
/** persists a number of ContactHistory entities for load and query testing. */ /** persists a number of ContactHistory entities for load and query testing. */
ImmutableList<ContactHistory> persistLotsOfContactHistoryEntities( ImmutableList<ContactHistory> persistLotsOfContactHistoryEntities(
int numOfEntities, int minusMonths, int minusDays, Contact contact) { int numOfEntities, int minusMonths, int minusDays, Contact contact) {
ImmutableList.Builder<ContactHistory> expectedEntitesBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<ContactHistory> expectedEntitiesBuilder = new ImmutableList.Builder<>();
for (int i = 0; i < numOfEntities; i++) { for (int i = 0; i < numOfEntities; i++) {
expectedEntitesBuilder.add( expectedEntitiesBuilder.add(
persistResource( persistResource(
new ContactHistory() new ContactHistory()
.asBuilder() .asBuilder()
@ -329,7 +328,7 @@ class WipeOutContactHistoryPiiActionTest {
.setContact(persistResource(contact)) .setContact(persistResource(contact))
.build())); .build()));
} }
return expectedEntitesBuilder.build(); return expectedEntitiesBuilder.build();
} }
boolean areAllPiiFieldsWiped(ContactBase contactBase) { boolean areAllPiiFieldsWiped(ContactBase contactBase) {

View file

@ -133,13 +133,13 @@ public class RdePipelineTest {
private final ImmutableList<DepositFragment> brdaFragments = private final ImmutableList<DepositFragment> brdaFragments =
ImmutableList.of( ImmutableList.of(
DepositFragment.create(RdeResourceType.DOMAIN, "<rdeDomain:domain/>\n", ""), DepositFragment.create(DOMAIN, "<rdeDomain:domain/>\n", ""),
DepositFragment.create(RdeResourceType.REGISTRAR, "<rdeRegistrar:registrar/>\n", "")); DepositFragment.create(REGISTRAR, "<rdeRegistrar:registrar/>\n", ""));
private final ImmutableList<DepositFragment> rdeFragments = private final ImmutableList<DepositFragment> rdeFragments =
ImmutableList.of( ImmutableList.of(
DepositFragment.create(RdeResourceType.DOMAIN, "<rdeDomain:domain/>\n", ""), DepositFragment.create(DOMAIN, "<rdeDomain:domain/>\n", ""),
DepositFragment.create(RdeResourceType.REGISTRAR, "<rdeRegistrar:registrar/>\n", ""), DepositFragment.create(REGISTRAR, "<rdeRegistrar:registrar/>\n", ""),
DepositFragment.create(CONTACT, "<rdeContact:contact/>\n", ""), DepositFragment.create(CONTACT, "<rdeContact:contact/>\n", ""),
DepositFragment.create(HOST, "<rdeHost:host/>\n", "")); DepositFragment.create(HOST, "<rdeHost:host/>\n", ""));
@ -183,7 +183,6 @@ public class RdePipelineTest {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setContact(contact) .setContact(contact)
.setContactRepoId(contact.getRepoId())
.build()); .build());
} }
@ -207,7 +206,6 @@ public class RdePipelineTest {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setDomain(domain) .setDomain(domain)
.setDomainRepoId(domain.getRepoId())
.setDomainTransactionRecords(ImmutableSet.of(transactionRecord)) .setDomainTransactionRecords(ImmutableSet.of(transactionRecord))
.setOtherRegistrarId("otherClient") .setOtherRegistrarId("otherClient")
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))
@ -226,7 +224,6 @@ public class RdePipelineTest {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setHost(hostBase) .setHost(hostBase)
.setHostRepoId(hostBase.getRepoId())
.build()); .build());
} }
@ -389,7 +386,7 @@ public class RdePipelineTest {
// The same registrars are attached to all the pending deposits. // The same registrars are attached to all the pending deposits.
.containsExactly("New Registrar", "The Registrar", "external_monitoring"); .containsExactly("New Registrar", "The Registrar", "external_monitoring");
// Domain fragments. // Domain fragments.
if (kv.getKey().tld().equals("soy")) { if ("soy".equals(kv.getKey().tld())) {
assertThat( assertThat(
getFragmentForType(kv, DOMAIN) getFragmentForType(kv, DOMAIN)
.map(getXmlElement(DOMAIN_NAME_PATTERN)) .map(getXmlElement(DOMAIN_NAME_PATTERN))
@ -404,7 +401,7 @@ public class RdePipelineTest {
} }
if (kv.getKey().mode().equals(FULL)) { if (kv.getKey().mode().equals(FULL)) {
// Contact fragments for hello.soy. // Contact fragments for hello.soy.
if (kv.getKey().tld().equals("soy")) { if ("soy".equals(kv.getKey().tld())) {
assertThat( assertThat(
getFragmentForType(kv, CONTACT) getFragmentForType(kv, CONTACT)
.map(getXmlElement(CONTACT_ID_PATTERN)) .map(getXmlElement(CONTACT_ID_PATTERN))
@ -528,7 +525,7 @@ public class RdePipelineTest {
decryptGhostrydeGcsFile(prefix + "soy_2000-01-01_thin_S1_" + revision + ".xml.ghostryde"); decryptGhostrydeGcsFile(prefix + "soy_2000-01-01_thin_S1_" + revision + ".xml.ghostryde");
assertThat(brdaOutputFile) assertThat(brdaOutputFile)
.isEqualTo( .isEqualTo(
readResourceUtf8(this.getClass(), "reducer_brda.xml") readResourceUtf8(getClass(), "reducer_brda.xml")
.replace("%RESEND%", manual ? "" : " resend=\"1\"")); .replace("%RESEND%", manual ? "" : " resend=\"1\""));
compareLength(brdaOutputFile, prefix + "soy_2000-01-01_thin_S1_" + revision + ".xml.length"); compareLength(brdaOutputFile, prefix + "soy_2000-01-01_thin_S1_" + revision + ".xml.length");
@ -577,7 +574,7 @@ public class RdePipelineTest {
} }
private static Function<DepositFragment, String> getXmlElement(String pattern) { private static Function<DepositFragment, String> getXmlElement(String pattern) {
return (fragment) -> { return fragment -> {
Matcher matcher = Pattern.compile(pattern).matcher(fragment.xml()); Matcher matcher = Pattern.compile(pattern).matcher(fragment.xml());
checkState(matcher.find(), "Missing %s in xml.", pattern); checkState(matcher.find(), "Missing %s in xml.", pattern);
return matcher.group(1); return matcher.group(1);

View file

@ -42,7 +42,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException; import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
@ -78,6 +77,7 @@ import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState; import google.registry.model.tld.Registry.TldState;
import google.registry.model.tld.label.ReservedList; import google.registry.model.tld.label.ReservedList;
@ -192,12 +192,12 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
void testSuccess_oneExists_allocationTokenIsRedeemed() throws Exception { void testSuccess_oneExists_allocationTokenIsRedeemed() throws Exception {
setEppInput("domain_check_allocationtoken.xml"); setEppInput("domain_check_allocationtoken.xml");
Domain domain = persistActiveDomain("example1.tld"); Domain domain = persistActiveDomain("example1.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
doCheckTest( doCheckTest(
create(false, "example1.tld", "In use"), create(false, "example1.tld", "In use"),

View file

@ -72,7 +72,6 @@ import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.truth.Truth8; import com.google.common.truth.Truth8;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig; import google.registry.config.RegistryConfig;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.EppException.UnimplementedExtensionException;
@ -172,6 +171,7 @@ import google.registry.model.registrar.Registrar.State;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState; import google.registry.model.tld.Registry.TldState;
import google.registry.model.tld.Registry.TldType; import google.registry.model.tld.Registry.TldType;
@ -530,12 +530,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts(); persistContactsAndHosts();
Domain domain = persistActiveDomain("foo.tld"); Domain domain = persistActiveDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 505L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = EppException thrown =
@ -556,8 +556,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlow(); runFlow();
assertSuccessfulCreate("tld", ImmutableSet.of(), token); assertSuccessfulCreate("tld", ImmutableSet.of(), token);
HistoryEntry historyEntry = getHistoryEntries(reloadResourceByForeignKey()).get(0); HistoryEntry historyEntry = getHistoryEntries(reloadResourceByForeignKey()).get(0);
assertThat(tm().transact(() -> tm().loadByEntity(token)).getRedemptionHistoryEntry()) assertThat(tm().transact(() -> tm().loadByEntity(token)).getRedemptionHistoryId())
.hasValue(HistoryEntry.createVKey(Key.create(historyEntry))); .hasValue(historyEntry.getHistoryEntryId());
} }
// DomainTransactionRecord is not propagated. // DomainTransactionRecord is not propagated.
@ -1355,10 +1355,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
AllocationToken reloadedToken = AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token))); tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isTrue(); assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry()) assertThat(reloadedToken.getRedemptionHistoryId())
.hasValue( .hasValue(getHistoryEntries(reloadResourceByForeignKey()).get(0).getHistoryEntryId());
HistoryEntry.createVKey(
Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0))));
} }
private void assertAllocationTokenWasNotRedeemed(String token) { private void assertAllocationTokenWasNotRedeemed(String token) {
@ -2570,7 +2568,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlow(); runFlow();
assertIcannReportingActivityFieldLogged("srs-dom-create"); assertIcannReportingActivityFieldLogged("srs-dom-create");
assertTldsFieldLogged("tld"); assertTldsFieldLogged("tld");
// Ensure we log the client ID for srs-dom-create so we can also use it for attempted-adds. // Ensure we log the client ID for srs-dom-create, so we can also use it for attempted-adds.
assertClientIdFieldLogged("TheRegistrar"); assertClientIdFieldLogged("TheRegistrar");
} }
@ -2584,7 +2582,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build()); .build());
runFlow(); runFlow();
Domain domain = reloadResourceByForeignKey(); Domain domain = reloadResourceByForeignKey();
HistoryEntry historyEntry = getHistoryEntries(domain).get(0); DomainHistory historyEntry = (DomainHistory) getHistoryEntries(domain).get(0);
assertThat(historyEntry.getDomainTransactionRecords()) assertThat(historyEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
DomainTransactionRecord.create( DomainTransactionRecord.create(
@ -2600,7 +2598,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistResource(Registry.get("tld").asBuilder().setTldType(TldType.TEST).build()); persistResource(Registry.get("tld").asBuilder().setTldType(TldType.TEST).build());
runFlow(); runFlow();
Domain domain = reloadResourceByForeignKey(); Domain domain = reloadResourceByForeignKey();
HistoryEntry historyEntry = getHistoryEntries(domain).get(0); DomainHistory historyEntry = (DomainHistory) getHistoryEntries(domain).get(0);
// No transaction records should be stored for test TLDs // No transaction records should be stored for test TLDs
assertThat(historyEntry.getDomainTransactionRecords()).isEmpty(); assertThat(historyEntry.getDomainTransactionRecords()).isEmpty();
} }

View file

@ -975,7 +975,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
"tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_1_YR, 1))) "tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_1_YR, 1)))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// No transaction records should be recorded for test TLDs // No transaction records should be recorded for test TLDs
assertThat(persistedEntry.getDomainTransactionRecords()).isEmpty(); assertThat(persistedEntry.getDomainTransactionRecords()).isEmpty();
} }
@ -997,7 +997,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
"tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_1_YR, 1))) "tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_1_YR, 1)))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the non-grace period delete // Transaction record should just be the non-grace period delete
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -1023,7 +1023,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
"tld", TIME_BEFORE_FLOW.plusDays(1), RESTORED_DOMAINS, 1))) "tld", TIME_BEFORE_FLOW.plusDays(1), RESTORED_DOMAINS, 1)))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the non-grace period delete // Transaction record should just be the non-grace period delete
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -1051,7 +1051,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
.setDomainTransactionRecords(ImmutableSet.of(renewRecord, notCancellableRecord)) .setDomainTransactionRecords(ImmutableSet.of(renewRecord, notCancellableRecord))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// We should only see the non-grace period delete record and the renew cancellation record // We should only see the non-grace period delete record and the renew cancellation record
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -1073,7 +1073,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
setUpGracePeriodDurations(); setUpGracePeriodDurations();
clock.advanceOneMilli(); clock.advanceOneMilli();
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the grace period delete // Transaction record should just be the grace period delete
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -1115,7 +1115,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
.setDomainTransactionRecords(ImmutableSet.of(existingRecord)) .setDomainTransactionRecords(ImmutableSet.of(existingRecord))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE); DomainHistory persistedEntry = (DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should be the grace period delete, and the more recent cancellation record // Transaction record should be the grace period delete, and the more recent cancellation record
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(

View file

@ -119,7 +119,7 @@ public class DomainPricingLogicTest {
.setId(2L) .setId(2L)
.setReason(Reason.RENEW) .setReason(Reason.RENEW)
.setRenewalPriceBehavior(renewalPriceBehavior) .setRenewalPriceBehavior(renewalPriceBehavior)
.setRenewalPrice(renewalPrice.isPresent() ? renewalPrice.get() : null) .setRenewalPrice(renewalPrice.orElse(null))
.setRecurrenceEndTime(END_OF_TIME) .setRecurrenceEndTime(END_OF_TIME)
.setTargetId(domain.getDomainName()) .setTargetId(domain.getDomainName())
.build()); .build());

View file

@ -50,7 +50,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.truth.Truth8; import com.google.common.truth.Truth8;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppRequestSource; import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.flows.FlowUtils.NotLoggedInException;
@ -95,6 +94,7 @@ import google.registry.model.registrar.Registrar.State;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper; import google.registry.testing.DatabaseHelper;
@ -609,8 +609,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
loadFile( loadFile(
"domain_renew_response.xml", "domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z"))); ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryEntry()) assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryId()).isPresent();
.isPresent();
} }
@Test @Test
@ -745,12 +744,12 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123")); ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain(); persistDomain();
Domain domain = persistActiveDomain("foo.tld"); Domain domain = persistActiveDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 505L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = EppException thrown =
@ -1188,7 +1187,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
.build()); .build());
runFlow(); runFlow();
Domain domain = reloadResourceByForeignKey(); Domain domain = reloadResourceByForeignKey();
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RENEW); DomainHistory historyEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RENEW);
assertThat(historyEntry.getDomainTransactionRecords()) assertThat(historyEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
DomainTransactionRecord.create( DomainTransactionRecord.create(

View file

@ -773,8 +773,8 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
persistPendingDeleteDomain(); persistPendingDeleteDomain();
runFlow(); runFlow();
Domain domain = reloadResourceByForeignKey(); Domain domain = reloadResourceByForeignKey();
HistoryEntry historyEntryDomainRestore = DomainHistory historyEntryDomainRestore =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RESTORE); (DomainHistory) getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RESTORE);
assertThat(historyEntryDomainRestore.getDomainTransactionRecords()) assertThat(historyEntryDomainRestore.getDomainTransactionRecords())
.containsExactly( .containsExactly(
DomainTransactionRecord.create( DomainTransactionRecord.create(

View file

@ -45,7 +45,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
@ -81,6 +80,7 @@ import google.registry.model.poll.PendingActionNotificationResponse;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.label.PremiumList; import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao; import google.registry.model.tld.label.PremiumListDao;
@ -207,7 +207,9 @@ class DomainTransferApproveFlowTest
DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST, DOMAIN_TRANSFER_APPROVE); DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST, DOMAIN_TRANSFER_APPROVE);
final HistoryEntry historyEntryTransferApproved = final HistoryEntry historyEntryTransferApproved =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE); getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE);
assertAboutHistoryEntries().that(historyEntryTransferApproved).hasOtherClientId("NewRegistrar"); assertAboutHistoryEntries()
.that(historyEntryTransferApproved)
.hasOtherRegistrarId("NewRegistrar");
assertTransferApproved(domain, originalTransferData); assertTransferApproved(domain, originalTransferData);
assertAboutDomains().that(domain).hasRegistrationExpirationTime(expectedExpirationTime); assertAboutDomains().that(domain).hasRegistrationExpirationTime(expectedExpirationTime);
assertThat(loadByKey(domain.getAutorenewBillingEvent()).getEventTime()) assertThat(loadByKey(domain.getAutorenewBillingEvent()).getEventTime())
@ -697,7 +699,8 @@ class DomainTransferApproveFlowTest
setUpGracePeriodDurations(); setUpGracePeriodDurations();
clock.advanceOneMilli(); clock.advanceOneMilli();
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE);
// We should only produce a transfer success record for (now + transfer grace period) // We should only produce a transfer success record for (now + transfer grace period)
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -724,7 +727,8 @@ class DomainTransferApproveFlowTest
ImmutableSet.of(previousSuccessRecord, notCancellableRecord)) ImmutableSet.of(previousSuccessRecord, notCancellableRecord))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_APPROVE);
// We should only produce cancellation records for the original reporting date (now + 1 day) and // We should only produce cancellation records for the original reporting date (now + 1 day) and
// success records for the new reporting date (now + transferGracePeriod=3 days) // success records for the new reporting date (now + transferGracePeriod=3 days)
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
@ -884,12 +888,12 @@ class DomainTransferApproveFlowTest
@Test @Test
void testFailure_allocationTokenAlreadyRedeemed() throws Exception { void testFailure_allocationTokenAlreadyRedeemed() throws Exception {
Domain domain = DatabaseHelper.newDomain("foo.tld"); Domain domain = DatabaseHelper.newDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 505L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
setEppInput("domain_transfer_approve_allocation_token.xml"); setEppInput("domain_transfer_approve_allocation_token.xml");
EppException thrown = EppException thrown =

View file

@ -145,7 +145,7 @@ class DomainTransferCancelFlowTest
.that(historyEntryTransferCancel) .that(historyEntryTransferCancel)
.hasRegistrarId("NewRegistrar") .hasRegistrarId("NewRegistrar")
.and() .and()
.hasOtherClientId("TheRegistrar"); .hasOtherRegistrarId("TheRegistrar");
// The only billing event left should be the original autorenew event, now reopened. // The only billing event left should be the original autorenew event, now reopened.
assertBillingEvents( assertBillingEvents(
getLosingClientAutorenewEvent().asBuilder().setRecurrenceEndTime(END_OF_TIME).build()); getLosingClientAutorenewEvent().asBuilder().setRecurrenceEndTime(END_OF_TIME).build());
@ -381,7 +381,8 @@ class DomainTransferCancelFlowTest
void testIcannTransactionRecord_noRecordsToCancel() throws Exception { void testIcannTransactionRecord_noRecordsToCancel() throws Exception {
clock.advanceOneMilli(); clock.advanceOneMilli();
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_CANCEL); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_CANCEL);
// No cancellation records should be produced // No cancellation records should be produced
assertThat(persistedEntry.getDomainTransactionRecords()).isEmpty(); assertThat(persistedEntry.getDomainTransactionRecords()).isEmpty();
} }
@ -410,7 +411,8 @@ class DomainTransferCancelFlowTest
ImmutableSet.of(previousSuccessRecord, notCancellableRecord)) ImmutableSet.of(previousSuccessRecord, notCancellableRecord))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_CANCEL); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_CANCEL);
// We should only produce a cancellation record for the original transfer success // We should only produce a cancellation record for the original transfer success
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(previousSuccessRecord.asBuilder().setReportAmount(-1).build()); .containsExactly(previousSuccessRecord.asBuilder().setReportAmount(-1).build());

View file

@ -111,7 +111,9 @@ class DomainTransferRejectFlowTest
.hasLastEppUpdateClientId("TheRegistrar"); .hasLastEppUpdateClientId("TheRegistrar");
final HistoryEntry historyEntryTransferRejected = final HistoryEntry historyEntryTransferRejected =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT); getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT);
assertAboutHistoryEntries().that(historyEntryTransferRejected).hasOtherClientId("NewRegistrar"); assertAboutHistoryEntries()
.that(historyEntryTransferRejected)
.hasOtherRegistrarId("NewRegistrar");
assertLastHistoryContainsResource(domain); assertLastHistoryContainsResource(domain);
// The only billing event left should be the original autorenew event, now reopened. // The only billing event left should be the original autorenew event, now reopened.
assertBillingEvents( assertBillingEvents(
@ -352,7 +354,8 @@ class DomainTransferRejectFlowTest
void testIcannTransactionRecord_noRecordsToCancel() throws Exception { void testIcannTransactionRecord_noRecordsToCancel() throws Exception {
setUpGracePeriodDurations(); setUpGracePeriodDurations();
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT);
// We should only produce transfer nacked records, reported now // We should only produce transfer nacked records, reported now
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(DomainTransactionRecord.create("tld", clock.nowUtc(), TRANSFER_NACKED, 1)); .containsExactly(DomainTransactionRecord.create("tld", clock.nowUtc(), TRANSFER_NACKED, 1));
@ -376,7 +379,8 @@ class DomainTransferRejectFlowTest
ImmutableSet.of(previousSuccessRecord, notCancellableRecord)) ImmutableSet.of(previousSuccessRecord, notCancellableRecord))
.build()); .build());
runFlow(); runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REJECT);
// We should only produce cancellation records for the original success records and nack records // We should only produce cancellation records for the original success records and nack records
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(

View file

@ -61,7 +61,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import google.registry.batch.ResaveEntityAction; import google.registry.batch.ResaveEntityAction;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppRequestSource; import google.registry.flows.EppRequestSource;
@ -114,6 +113,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.Registrar.State;
import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.model.tld.label.PremiumList; import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao; import google.registry.model.tld.label.PremiumListDao;
@ -500,7 +500,7 @@ class DomainTransferRequestFlowTest
.that(historyEntryTransferRequest) .that(historyEntryTransferRequest)
.hasPeriodYears(1) .hasPeriodYears(1)
.and() .and()
.hasOtherClientId("TheRegistrar"); .hasOtherRegistrarId("TheRegistrar");
// Verify correct fields were set. // Verify correct fields were set.
assertTransferRequested( assertTransferRequested(
domain, implicitTransferTime, Period.create(1, Unit.YEARS), expectedExpirationTime); domain, implicitTransferTime, Period.create(1, Unit.YEARS), expectedExpirationTime);
@ -608,7 +608,7 @@ class DomainTransferRequestFlowTest
.that(historyEntryTransferRequest) .that(historyEntryTransferRequest)
.hasPeriodYears(expectedPeriod.getValue()) .hasPeriodYears(expectedPeriod.getValue())
.and() .and()
.hasOtherClientId("TheRegistrar"); .hasOtherRegistrarId("TheRegistrar");
// Verify correct fields were set. // Verify correct fields were set.
assertTransferRequested(domain, implicitTransferTime, expectedPeriod, expectedExpirationTime); assertTransferRequested(domain, implicitTransferTime, expectedPeriod, expectedExpirationTime);
@ -1682,7 +1682,8 @@ class DomainTransferRequestFlowTest
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
runTest("domain_transfer_request.xml", UserPrivileges.NORMAL); runTest("domain_transfer_request.xml", UserPrivileges.NORMAL);
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST); DomainHistory persistedEntry =
(DomainHistory) getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST);
// We should produce a transfer success record // We should produce a transfer success record
assertThat(persistedEntry.getDomainTransactionRecords()) assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly( .containsExactly(
@ -1795,12 +1796,12 @@ class DomainTransferRequestFlowTest
void testFailure_allocationTokenAlreadyRedeemed() throws Exception { void testFailure_allocationTokenAlreadyRedeemed() throws Exception {
setupDomain("example", "tld"); setupDomain("example", "tld");
Domain domain = DatabaseHelper.newDomain("foo.tld"); Domain domain = DatabaseHelper.newDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 505L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123")); setEppInput("domain_transfer_request_allocation_token.xml", ImmutableMap.of("TOKEN", "abc123"));
EppException thrown = EppException thrown =

View file

@ -37,7 +37,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
@ -48,7 +47,7 @@ import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.domain.token.AllocationTokenExtension;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import google.registry.testing.DatabaseHelper; import google.registry.testing.DatabaseHelper;
@ -256,7 +255,7 @@ class AllocationTokenFlowUtilsTest {
@Test @Test
void test_validateTokenCreate_promoCancelled() { void test_validateTokenCreate_promoCancelled() {
// the promo would be valid but it was cancelled 12 hours ago // the promo would be valid, but it was cancelled 12 hours ago
persistResource( persistResource(
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1)) createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
.setTokenStatusTransitions( .setTokenStatusTransitions(
@ -271,7 +270,7 @@ class AllocationTokenFlowUtilsTest {
@Test @Test
void test_validateTokenExistingDomain_promoCancelled() { void test_validateTokenExistingDomain_promoCancelled() {
// the promo would be valid but it was cancelled 12 hours ago // the promo would be valid, but it was cancelled 12 hours ago
persistResource( persistResource(
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1)) createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
.setTokenStatusTransitions( .setTokenStatusTransitions(
@ -306,12 +305,12 @@ class AllocationTokenFlowUtilsTest {
@Test @Test
void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() { void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() {
Domain domain = persistActiveDomain("example.tld"); Domain domain = persistActiveDomain("example.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1051L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("tokeN") .setToken("tokeN")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.build()); .build());
assertThat( assertThat(
flowUtils flowUtils

View file

@ -41,7 +41,7 @@ public final class OteStatsTestHelper {
DateTime now = DateTime.now(DateTimeZone.UTC); DateTime now = DateTime.now(DateTimeZone.UTC);
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("xn--abc-873b2e7eb1k8a4lpjvv.tld").getRepoId()) .setDomain(persistActiveDomain("xn--abc-873b2e7eb1k8a4lpjvv.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_idn.xml")) .setXmlBytes(getBytes("domain_create_idn.xml"))
@ -49,7 +49,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_RESTORE) .setType(Type.DOMAIN_RESTORE)
.setXmlBytes(getBytes("domain_restore.xml")) .setXmlBytes(getBytes("domain_restore.xml"))
@ -57,7 +57,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new HostHistory.Builder() new HostHistory.Builder()
.setHostRepoId(persistDeletedHost("ns1.example.tld", now).getRepoId()) .setHost(persistDeletedHost("ns1.example.tld", now))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.HOST_DELETE) .setType(Type.HOST_DELETE)
.setXmlBytes(getBytes("host_delete.xml")) .setXmlBytes(getBytes("host_delete.xml"))
@ -86,7 +86,7 @@ public final class OteStatsTestHelper {
DateTime now = DateTime.now(DateTimeZone.UTC); DateTime now = DateTime.now(DateTimeZone.UTC);
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("exampleone.tld").getRepoId()) .setDomain(persistActiveDomain("exampleone.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_sunrise.xml")) .setXmlBytes(getBytes("domain_create_sunrise.xml"))
@ -94,7 +94,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example-one.tld").getRepoId()) .setDomain(persistActiveDomain("example-one.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_claim_notice.xml")) .setXmlBytes(getBytes("domain_create_claim_notice.xml"))
@ -102,7 +102,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_anchor_tenant_fee_standard.xml")) .setXmlBytes(getBytes("domain_create_anchor_tenant_fee_standard.xml"))
@ -110,7 +110,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_CREATE) .setType(Type.DOMAIN_CREATE)
.setXmlBytes(getBytes("domain_create_dsdata.xml")) .setXmlBytes(getBytes("domain_create_dsdata.xml"))
@ -118,7 +118,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistDeletedDomain("example.tld", now).getRepoId()) .setDomain(persistDeletedDomain("example.tld", now))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_DELETE) .setType(Type.DOMAIN_DELETE)
.setXmlBytes(getBytes("domain_delete.xml")) .setXmlBytes(getBytes("domain_delete.xml"))
@ -126,7 +126,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_APPROVE) .setType(Type.DOMAIN_TRANSFER_APPROVE)
.setXmlBytes(getBytes("domain_transfer_approve.xml")) .setXmlBytes(getBytes("domain_transfer_approve.xml"))
@ -134,7 +134,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_CANCEL) .setType(Type.DOMAIN_TRANSFER_CANCEL)
.setXmlBytes(getBytes("domain_transfer_cancel.xml")) .setXmlBytes(getBytes("domain_transfer_cancel.xml"))
@ -142,7 +142,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_REJECT) .setType(Type.DOMAIN_TRANSFER_REJECT)
.setXmlBytes(getBytes("domain_transfer_reject.xml")) .setXmlBytes(getBytes("domain_transfer_reject.xml"))
@ -150,7 +150,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_TRANSFER_REQUEST) .setType(Type.DOMAIN_TRANSFER_REQUEST)
.setXmlBytes(getBytes("domain_transfer_request.xml")) .setXmlBytes(getBytes("domain_transfer_request.xml"))
@ -158,7 +158,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(persistActiveDomain("example.tld").getRepoId()) .setDomain(persistActiveDomain("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.DOMAIN_UPDATE) .setType(Type.DOMAIN_UPDATE)
.setXmlBytes(getBytes("domain_update_with_secdns.xml")) .setXmlBytes(getBytes("domain_update_with_secdns.xml"))
@ -166,7 +166,7 @@ public final class OteStatsTestHelper {
.build()); .build());
persistResource( persistResource(
new HostHistory.Builder() new HostHistory.Builder()
.setHostRepoId(persistActiveHost("example.tld").getRepoId()) .setHost(persistActiveHost("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.HOST_CREATE) .setType(Type.HOST_CREATE)
.setXmlBytes(getBytes("host_create_complete.xml")) .setXmlBytes(getBytes("host_create_complete.xml"))
@ -178,7 +178,7 @@ public final class OteStatsTestHelper {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
persistResource( persistResource(
new HostHistory.Builder() new HostHistory.Builder()
.setHostRepoId(persistActiveHost("example.tld").getRepoId()) .setHost(persistActiveHost("example.tld"))
.setRegistrarId(oteAccount1) .setRegistrarId(oteAccount1)
.setType(Type.HOST_UPDATE) .setType(Type.HOST_UPDATE)
.setXmlBytes(getBytes("host_update.xml")) .setXmlBytes(getBytes("host_update.xml"))

View file

@ -14,6 +14,7 @@
package google.registry.model; package google.registry.model;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import google.registry.testing.GoldenFileTestHelper; import google.registry.testing.GoldenFileTestHelper;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -24,6 +25,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
* *
* <p>If the test breaks, the instructions below will be printed. * <p>If the test breaks, the instructions below will be printed.
*/ */
@DeleteAfterMigration
public class SchemaVersionTest { public class SchemaVersionTest {
@RegisterExtension @RegisterExtension

View file

@ -262,7 +262,7 @@ public class BillingEventTest extends EntityTestCase {
BillingEvent.Cancellation.forGracePeriod( BillingEvent.Cancellation.forGracePeriod(
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), oneTime), GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), oneTime),
domainHistory2.getModificationTime(), domainHistory2.getModificationTime(),
domainHistory2.getDomainHistoryId(), domainHistory2.getHistoryEntryId(),
"foo.tld"); "foo.tld");
// Set ID to be the same to ignore for the purposes of comparison. // Set ID to be the same to ignore for the purposes of comparison.
assertThat(newCancellation.asBuilder().setId(cancellationOneTime.getId()).build()) assertThat(newCancellation.asBuilder().setId(cancellationOneTime.getId()).build())
@ -280,7 +280,7 @@ public class BillingEventTest extends EntityTestCase {
"TheRegistrar", "TheRegistrar",
recurring.createVKey()), recurring.createVKey()),
domainHistory2.getModificationTime(), domainHistory2.getModificationTime(),
domainHistory2.getDomainHistoryId(), domainHistory2.getHistoryEntryId(),
"foo.tld"); "foo.tld");
// Set ID to be the same to ignore for the purposes of comparison. // Set ID to be the same to ignore for the purposes of comparison.
assertThat(newCancellation.asBuilder().setId(cancellationRecurring.getId()).build()) assertThat(newCancellation.asBuilder().setId(cancellationRecurring.getId()).build())
@ -300,7 +300,7 @@ public class BillingEventTest extends EntityTestCase {
now.plusDays(1), now.plusDays(1),
"a registrar"), "a registrar"),
domainHistory.getModificationTime(), domainHistory.getModificationTime(),
domainHistory.getDomainHistoryId(), domainHistory.getHistoryEntryId(),
"foo.tld")); "foo.tld"));
assertThat(thrown).hasMessageThat().contains("grace period without billing event"); assertThat(thrown).hasMessageThat().contains("grace period without billing event");
} }

View file

@ -21,7 +21,6 @@ import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.TestObject; import google.registry.testing.TestObject;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -42,7 +41,6 @@ public class ClassPathManagerTest {
assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class); assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class);
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class); assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class); assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class);
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
} }
@Test @Test
@ -80,7 +78,6 @@ public class ClassPathManagerTest {
assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class)) assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class))
.isEqualTo("GaeUserIdConverter"); .isEqualTo("GaeUserIdConverter");
assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain"); assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain");
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
} }
@Test @Test

View file

@ -29,16 +29,11 @@ import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
import static google.registry.testing.SqlHelper.saveRegistrar; import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.contact.Contact; import google.registry.model.contact.Contact;
import google.registry.model.domain.DesignatedContact.Type; import google.registry.model.domain.DesignatedContact.Type;
@ -50,16 +45,12 @@ import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host; import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData; import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.DomainTransferData;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.util.SerializeUtils; import google.registry.util.SerializeUtils;
import java.util.Arrays; import java.util.Arrays;
import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -80,14 +71,12 @@ public class DomainSqlTest {
.build(); .build();
private Domain domain; private Domain domain;
private DomainHistory historyEntry;
private VKey<Contact> contactKey; private VKey<Contact> contactKey;
private VKey<Contact> contact2Key; private VKey<Contact> contact2Key;
private VKey<Host> host1VKey; private VKey<Host> host1VKey;
private Host host; private Host host;
private Contact contact; private Contact contact;
private Contact contact2; private Contact contact2;
private ImmutableSet<GracePeriod> gracePeriods;
private AllocationToken allocationToken; private AllocationToken allocationToken;
@BeforeEach @BeforeEach
@ -411,131 +400,8 @@ public class DomainSqlTest {
insertInDb(contact, contact2, domain, host); insertInDb(contact, contact2, domain, host);
} }
@Test private <T> VKey<T> createKey(Class<T> clazz, String key) {
void persistDomainWithCompositeVKeys() { return VKey.createSql(clazz, key);
createTld("com");
historyEntry =
new DomainHistory.Builder()
.setId(100L)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC))
.setDomainRepoId("4-COM")
.setRegistrarId("registrar1")
// These are non-null, but I don't think some tests set them.
.setReason("felt like it")
.setRequestedByRegistrar(false)
.setXmlBytes(new byte[0])
.build();
BillingEvent.Recurring billEvent =
new BillingEvent.Recurring.Builder()
.setId(200L)
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setRegistrarId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistory(historyEntry)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setId(300L)
.setRegistrarId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setHistoryEntry(historyEntry)
.build();
PollMessage.OneTime deletePollMessage =
new PollMessage.OneTime.Builder()
.setId(400L)
.setRegistrarId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setHistoryEntry(historyEntry)
.build();
BillingEvent.OneTime oneTimeBillingEvent =
new BillingEvent.OneTime.Builder()
.setId(500L)
// Use SERVER_STATUS so we don't have to add a period.
.setReason(Reason.SERVER_STATUS)
.setTargetId("example.com")
.setRegistrarId("registrar1")
.setBillingTime(DateTime.now(UTC))
.setCost(Money.of(USD, 100))
.setEventTime(DateTime.now(UTC).plusYears(1))
.setDomainHistory(historyEntry)
.build();
DomainTransferData transferData =
new DomainTransferData.Builder()
.setServerApproveBillingEvent(oneTimeBillingEvent.createVKey())
.setServerApproveAutorenewEvent(billEvent.createVKey())
.setServerApproveAutorenewPollMessage(autorenewPollMessage.createVKey())
.build();
gracePeriods =
ImmutableSet.of(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
END_OF_TIME,
"registrar1",
oneTimeBillingEvent.createVKey()),
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
"4-COM",
END_OF_TIME,
"registrar1",
billEvent.createVKey()));
domain =
domain
.asBuilder()
.setAutorenewBillingEvent(billEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.setDeletePollMessage(deletePollMessage.createVKey())
.setTransferData(transferData)
.setGracePeriods(gracePeriods)
.build();
historyEntry = historyEntry.asBuilder().setDomain(domain).build();
insertInDb(
contact,
contact2,
host,
historyEntry,
autorenewPollMessage,
billEvent,
deletePollMessage,
oneTimeBillingEvent,
domain);
// Store the existing BillingRecurrence VKey. This happens after the event has been persisted.
Domain persisted = loadByKey(domain.createVKey());
// Verify that the domain data has been persisted.
// dsData still isn't persisted. gracePeriods appears to have the same values but for some
// reason is showing up as different.
assertEqualDomainExcept(persisted, "creationTime", "dsData", "gracePeriods");
// Verify that the DomainBase object from the history record sets the fields correctly.
DomainHistory persistedHistoryEntry = loadByKey(historyEntry.createVKey());
assertThat(persistedHistoryEntry.getDomainBase().get().getAutorenewPollMessage())
.isEqualTo(domain.getAutorenewPollMessage());
assertThat(persistedHistoryEntry.getDomainBase().get().getAutorenewBillingEvent())
.isEqualTo(domain.getAutorenewBillingEvent());
assertThat(persistedHistoryEntry.getDomainBase().get().getDeletePollMessage())
.isEqualTo(domain.getDeletePollMessage());
DomainTransferData persistedTransferData =
persistedHistoryEntry.getDomainBase().get().getTransferData();
DomainTransferData originalTransferData = domain.getTransferData();
assertThat(persistedTransferData.getServerApproveBillingEvent())
.isEqualTo(originalTransferData.getServerApproveBillingEvent());
assertThat(persistedTransferData.getServerApproveAutorenewEvent())
.isEqualTo(originalTransferData.getServerApproveAutorenewEvent());
assertThat(persistedTransferData.getServerApproveAutorenewPollMessage())
.isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage());
assertThat(persisted.getGracePeriods()).isEqualTo(gracePeriods);
}
private <T> VKey<T> createKey(Class<T> clazz, String name) {
return VKey.create(clazz, name, Key.create(clazz, name));
} }
private void assertEqualDomainExcept(Domain thatDomain, String... excepts) { private void assertEqualDomainExcept(Domain thatDomain, String... excepts) {
@ -548,7 +414,7 @@ public class DomainSqlTest {
.build(); .build();
// Note that the equality comparison forces a lazy load of all fields. // Note that the equality comparison forces a lazy load of all fields.
assertAboutImmutableObjects().that(thatDomain).isEqualExceptFields(domain, moreExcepts); assertAboutImmutableObjects().that(thatDomain).isEqualExceptFields(domain, moreExcepts);
// Transfer data cannot be directly compared due to serverApproveEtities inequalities // Transfer data cannot be directly compared due to serverApproveEntities inequalities
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(domain.getTransferData()) .that(domain.getTransferData())
.isEqualExceptFields(thatDomain.getTransferData(), "serverApproveEntities"); .isEqualExceptFields(thatDomain.getTransferData(), "serverApproveEntities");

View file

@ -109,7 +109,7 @@ public class DomainTest {
domainHistory = domainHistory =
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setDomainRepoId(domain.getRepoId()) .setDomain(domain)
.setModificationTime(fakeClock.nowUtc()) .setModificationTime(fakeClock.nowUtc())
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")
@ -129,11 +129,11 @@ public class DomainTest {
.createVKey(); .createVKey();
DomainHistory historyEntry = DomainHistory historyEntry =
new DomainHistory.Builder() new DomainHistory.Builder()
.setId(100L) .setRevisionId(100L)
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC)) .setModificationTime(DateTime.now(UTC))
.setDomainRepoId(domain.getRepoId()) .setDomain(domain)
.setRegistrarId(domain.getCurrentSponsorRegistrarId()) .setRegistrarId(domain.getCurrentSponsorRegistrarId())
// These are non-null, but I don't think some tests set them. // These are non-null, but I don't think some tests set them.
.setReason("felt like it") .setReason("felt like it")
@ -202,8 +202,8 @@ public class DomainTest {
.setLosingRegistrarId("NewRegistrar") .setLosingRegistrarId("NewRegistrar")
.setPendingTransferExpirationTime(fakeClock.nowUtc()) .setPendingTransferExpirationTime(fakeClock.nowUtc())
.setServerApproveEntities( .setServerApproveEntities(
historyEntry.getDomainRepoId(), historyEntry.getRepoId(),
historyEntry.getId(), historyEntry.getRevisionId(),
ImmutableSet.of(oneTimeBillKey, recurringBillKey, autorenewPollKey)) ImmutableSet.of(oneTimeBillKey, recurringBillKey, autorenewPollKey))
.setServerApproveBillingEvent(oneTimeBillKey) .setServerApproveBillingEvent(oneTimeBillKey)
.setServerApproveAutorenewEvent(recurringBillKey) .setServerApproveAutorenewEvent(recurringBillKey)
@ -438,7 +438,7 @@ public class DomainTest {
.setServerApproveBillingEvent(transferBillingEvent.createVKey()) .setServerApproveBillingEvent(transferBillingEvent.createVKey())
.setServerApproveEntities( .setServerApproveEntities(
domain.getRepoId(), domain.getRepoId(),
historyEntry.getId(), historyEntry.getRevisionId(),
ImmutableSet.of(transferBillingEvent.createVKey())) ImmutableSet.of(transferBillingEvent.createVKey()))
.build()) .build())
.addGracePeriod( .addGracePeriod(

View file

@ -21,8 +21,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
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.billing.BillingEvent.Recurring; import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
@ -53,7 +53,7 @@ public class GracePeriodTest {
.setBillingTime(now.plusDays(1)) .setBillingTime(now.plusDays(1))
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")
.setCost(Money.of(CurrencyUnit.USD, 42)) .setCost(Money.of(CurrencyUnit.USD, 42))
.setDomainHistoryId(new DomainHistoryId("domain", 12345)) .setDomainHistoryId(new HistoryEntryId("domain", 12345))
.setReason(Reason.CREATE) .setReason(Reason.CREATE)
.setPeriodYears(1) .setPeriodYears(1)
.setTargetId("foo.google") .setTargetId("foo.google")

View file

@ -33,7 +33,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.googlecode.objectify.Key;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.EntityTestCase; import google.registry.model.EntityTestCase;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
@ -41,7 +40,7 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior; import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.util.SerializeUtils; import google.registry.util.SerializeUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -82,12 +81,12 @@ public class AllocationTokenTest extends EntityTestCase {
assertThat(loadByEntity(unlimitedUseToken)).isEqualTo(unlimitedUseToken); assertThat(loadByEntity(unlimitedUseToken)).isEqualTo(unlimitedUseToken);
Domain domain = persistActiveDomain("example.foo"); Domain domain = persistActiveDomain("example.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1);
AllocationToken singleUseToken = AllocationToken singleUseToken =
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123Single") .setToken("abc123Single")
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.setDomainName("example.foo") .setDomainName("example.foo")
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z")) .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
@ -119,12 +118,12 @@ public class AllocationTokenTest extends EntityTestCase {
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted); assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
Domain domain = persistActiveDomain("example.foo"); Domain domain = persistActiveDomain("example.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1);
AllocationToken singleUseToken = AllocationToken singleUseToken =
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123Single") .setToken("abc123Single")
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryId(historyEntryId)
.setDomainName("example.foo") .setDomainName("example.foo")
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z")) .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
@ -306,12 +305,12 @@ public class AllocationTokenTest extends EntityTestCase {
@Test @Test
void testBuild_redemptionHistoryEntryOnlyInSingleUse() { void testBuild_redemptionHistoryEntryOnlyInSingleUse() {
Domain domain = persistActiveDomain("blahdomain.foo"); Domain domain = persistActiveDomain("blahdomain.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1);
AllocationToken.Builder builder = AllocationToken.Builder builder =
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("foobar") .setToken("foobar")
.setTokenType(TokenType.UNLIMITED_USE) .setTokenType(TokenType.UNLIMITED_USE)
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)); .setRedemptionHistoryId(historyEntryId);
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
assertThat(thrown) assertThat(thrown)
.hasMessageThat() .hasMessageThat()

View file

@ -54,7 +54,7 @@ public class ContactHistoryTest extends EntityTestCase {
() -> { () -> {
ContactHistory fromDatabase = jpaTm().loadByKey(contactHistory.createVKey()); ContactHistory fromDatabase = jpaTm().loadByKey(contactHistory.createVKey());
assertContactHistoriesEqual(fromDatabase, contactHistory); assertContactHistoriesEqual(fromDatabase, contactHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(contactHistory.getParentVKey()); assertThat(fromDatabase.getRepoId()).isEqualTo(contactHistory.getRepoId());
}); });
} }
@ -70,24 +70,6 @@ public class ContactHistoryTest extends EntityTestCase {
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase); assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
} }
@Test
void testLegacyPersistence_nullContactBase() {
Contact contact = newContactWithRoid("contactId", "contact1");
insertInDb(contact);
Contact contactFromDb = loadByEntity(contact);
ContactHistory contactHistory =
createContactHistory(contactFromDb).asBuilder().setContact(null).build();
insertInDb(contactHistory);
jpaTm()
.transact(
() -> {
ContactHistory fromDatabase = jpaTm().loadByKey(contactHistory.createVKey());
assertContactHistoriesEqual(fromDatabase, contactHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(contactHistory.getParentVKey());
});
}
@Test @Test
void testWipeOutPii_assertsAllPiiFieldsAreNull() { void testWipeOutPii_assertsAllPiiFieldsAreNull() {
ContactHistory originalEntity = ContactHistory originalEntity =
@ -153,16 +135,13 @@ public class ContactHistoryTest extends EntityTestCase {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setContact(contact) .setContact(contact)
.setContactRepoId(contact.getRepoId())
.build(); .build();
} }
static void assertContactHistoriesEqual(ContactHistory one, ContactHistory two) { static void assertContactHistoriesEqual(ContactHistory one, ContactHistory two) {
assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "resource");
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(one) .that(one.getContactBase().get())
.isEqualExceptFields(two, "contactBase", "contactRepoId"); .isEqualExceptFields(two.getContactBase().get());
assertAboutImmutableObjects()
.that(one.getContactBase().orElse(null))
.isEqualExceptFields(two.getContactBase().orElse(null), "repoId");
} }
} }

View file

@ -69,7 +69,7 @@ public class DomainHistoryTest extends EntityTestCase {
() -> { () -> {
DomainHistory fromDatabase = jpaTm().loadByKey(domainHistory.createVKey()); DomainHistory fromDatabase = jpaTm().loadByKey(domainHistory.createVKey());
assertDomainHistoriesEqual(fromDatabase, domainHistory); assertDomainHistoriesEqual(fromDatabase, domainHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(domainHistory.getParentVKey()); assertThat(fromDatabase.getRepoId()).isEqualTo(domainHistory.getRepoId());
}); });
} }
@ -83,23 +83,6 @@ public class DomainHistoryTest extends EntityTestCase {
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase); assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
} }
@Test
void testLegacyPersistence_nullResource() {
Domain domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
DomainHistory domainHistory = createDomainHistory(domain).asBuilder().setDomain(null).build();
insertInDb(domainHistory);
jpaTm()
.transact(
() -> {
DomainHistory fromDatabase = jpaTm().loadByKey(domainHistory.createVKey());
assertDomainHistoriesEqual(fromDatabase, domainHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(domainHistory.getParentVKey());
assertThat(fromDatabase.getNsHosts())
.containsExactlyElementsIn(domainHistory.getNsHosts());
});
}
static Domain createDomainWithContactsAndHosts() { static Domain createDomainWithContactsAndHosts() {
createTld("tld"); createTld("tld");
Host host = newHostWithRoid("ns1.example.com", "host1"); Host host = newHostWithRoid("ns1.example.com", "host1");
@ -134,7 +117,7 @@ public class DomainHistoryTest extends EntityTestCase {
} }
static void assertDomainHistoriesEqual(DomainHistory one, DomainHistory two) { static void assertDomainHistoriesEqual(DomainHistory one, DomainHistory two) {
assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "domainBase"); assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "resource");
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(one.getDomainBase().get()) .that(one.getDomainBase().get())
.isEqualExceptFields(two.getDomainBase().get(), "updateTimestamp"); .isEqualExceptFields(two.getDomainBase().get(), "updateTimestamp");
@ -159,7 +142,6 @@ public class DomainHistoryTest extends EntityTestCase {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setDomain(domain) .setDomain(domain)
.setDomainRepoId(domain.getRepoId())
.setDomainTransactionRecords(ImmutableSet.of(transactionRecord)) .setDomainTransactionRecords(ImmutableSet.of(transactionRecord))
.setOtherRegistrarId("otherClient") .setOtherRegistrarId("otherClient")
.setPeriod(Period.create(1, Period.Unit.YEARS)) .setPeriod(Period.create(1, Period.Unit.YEARS))

View file

@ -50,7 +50,7 @@ public class HostHistoryTest extends EntityTestCase {
() -> { () -> {
HostHistory fromDatabase = jpaTm().loadByKey(hostHistory.createVKey()); HostHistory fromDatabase = jpaTm().loadByKey(hostHistory.createVKey());
assertHostHistoriesEqual(fromDatabase, hostHistory); assertHostHistoriesEqual(fromDatabase, hostHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(hostHistory.getParentVKey()); assertThat(fromDatabase.getRepoId()).isEqualTo(hostHistory.getRepoId());
}); });
} }
@ -65,29 +65,11 @@ public class HostHistoryTest extends EntityTestCase {
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase); assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
} }
@Test private static void assertHostHistoriesEqual(HostHistory one, HostHistory two) {
void testLegacyPersistence_nullHostBase() { assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "resource");
Host host = newHostWithRoid("ns1.example.com", "host1");
insertInDb(host);
Host hostFromDb = loadByEntity(host);
HostHistory hostHistory = createHostHistory(hostFromDb).asBuilder().setHost(null).build();
insertInDb(hostHistory);
jpaTm()
.transact(
() -> {
HostHistory fromDatabase = jpaTm().loadByKey(hostHistory.createVKey());
assertHostHistoriesEqual(fromDatabase, hostHistory);
assertThat(fromDatabase.getParentVKey()).isEqualTo(hostHistory.getParentVKey());
});
}
private void assertHostHistoriesEqual(HostHistory one, HostHistory two) {
assertAboutImmutableObjects().that(one).isEqualExceptFields(two, "hostBase");
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(one.getHostBase().orElse(null)) .that(one.getHostBase().get())
.isEqualExceptFields(two.getHostBase().orElse(null), "repoId"); .isEqualExceptFields(two.getHostBase().get(), "repoId");
} }
private HostHistory createHostHistory(HostBase hostBase) { private HostHistory createHostHistory(HostBase hostBase) {
@ -101,7 +83,6 @@ public class HostHistoryTest extends EntityTestCase {
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(true) .setRequestedByRegistrar(true)
.setHost(hostBase) .setHost(hostBase)
.setHostRepoId(hostBase.getRepoId())
.build(); .build();
} }
} }

View file

@ -41,7 +41,6 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link PollMessage}. */ /** Unit tests for {@link PollMessage}. */
public class PollMessageTest extends EntityTestCase { public class PollMessageTest extends EntityTestCase {
private Domain domain;
private HistoryEntry historyEntry; private HistoryEntry historyEntry;
private PollMessage.OneTime oneTime; private PollMessage.OneTime oneTime;
private PollMessage.Autorenew autoRenew; private PollMessage.Autorenew autoRenew;
@ -54,7 +53,7 @@ public class PollMessageTest extends EntityTestCase {
void setUp() { void setUp() {
createTld("foobar"); createTld("foobar");
Contact contact = persistActiveContact("contact1234"); Contact contact = persistActiveContact("contact1234");
domain = persistResource(DatabaseHelper.newDomain("foo.foobar", contact)); Domain domain = persistResource(DatabaseHelper.newDomain("foo.foobar", contact));
historyEntry = historyEntry =
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
@ -68,8 +67,7 @@ public class PollMessageTest extends EntityTestCase {
.setBySuperuser(false) .setBySuperuser(false)
.setReason("reason") .setReason("reason")
.setRequestedByRegistrar(false) .setRequestedByRegistrar(false)
.build() .build());
.toChildHistoryEntity());
oneTime = oneTime =
new PollMessage.OneTime.Builder() new PollMessage.OneTime.Builder()
.setId(100L) .setId(100L)

View file

@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence; import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.newDomain;
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;
@ -76,7 +75,7 @@ class HistoryEntryDaoTest extends EntityTestCase {
@Test @Test
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", "domainBase")) .comparingElementsUsing(immutableObjectCorrespondence("nsHosts", "resource"))
.containsExactly(domainHistory); .containsExactly(domainHistory);
} }
@ -98,7 +97,7 @@ class HistoryEntryDaoTest extends EntityTestCase {
tm().transact( tm().transact(
() -> () ->
assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey())) assertThat(HistoryEntryDao.loadHistoryObjectsForResource(domain.createVKey()))
.comparingElementsUsing(immutableObjectCorrespondence("nsHosts", "domainBase")) .comparingElementsUsing(immutableObjectCorrespondence("nsHosts", "resource"))
.containsExactly(domainHistory)); .containsExactly(domainHistory));
} }

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.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
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 java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
@ -25,6 +26,7 @@ import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.model.EntityTestCase; import google.registry.model.EntityTestCase;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory; import google.registry.model.contact.ContactHistory;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
@ -36,15 +38,16 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** Unit tests for {@link HistoryEntry}. */ /** Unit tests for {@link HistoryEntry}. */
class HistoryEntryTest extends EntityTestCase { class HistoryEntryTest extends EntityTestCase {
private DomainHistory domainHistory; private DomainHistory domainHistory;
private Contact contact;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
createTld("foobar"); createTld("foobar");
Domain domain = persistActiveDomain("foo.foobar"); Domain domain = persistActiveDomain("foo.foobar");
contact = persistActiveContact("someone");
DomainTransactionRecord transactionRecord = DomainTransactionRecord transactionRecord =
new DomainTransactionRecord.Builder() new DomainTransactionRecord.Builder()
.setTld("foobar") .setTld("foobar")
@ -78,10 +81,26 @@ class HistoryEntryTest extends EntityTestCase {
DomainHistory fromDatabase = tm().loadByEntity(domainHistory); DomainHistory fromDatabase = tm().loadByEntity(domainHistory);
assertAboutImmutableObjects() assertAboutImmutableObjects()
.that(fromDatabase) .that(fromDatabase)
.isEqualExceptFields(domainHistory, "domainBase"); .isEqualExceptFields(domainHistory, "resource");
}); });
} }
@Test
void testBuilder_resourceMustBeSpecified() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
new ContactHistory.Builder()
.setRevisionId(5L)
.setModificationTime(DateTime.parse("1985-07-12T22:30:00Z"))
.setRegistrarId("TheRegistrar")
.setReason("Reason")
.setType(HistoryEntry.Type.CONTACT_CREATE)
.build());
assertThat(thrown).hasMessageThat().isEqualTo("EPP resource must be specified");
}
@Test @Test
void testBuilder_typeMustBeSpecified() { void testBuilder_typeMustBeSpecified() {
IllegalArgumentException thrown = IllegalArgumentException thrown =
@ -89,7 +108,8 @@ class HistoryEntryTest extends EntityTestCase {
IllegalArgumentException.class, IllegalArgumentException.class,
() -> () ->
new ContactHistory.Builder() new ContactHistory.Builder()
.setId(5L) .setContact(contact)
.setRevisionId(5L)
.setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z"))
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")
.setReason("Reason") .setReason("Reason")
@ -104,7 +124,8 @@ class HistoryEntryTest extends EntityTestCase {
IllegalArgumentException.class, IllegalArgumentException.class,
() -> () ->
new ContactHistory.Builder() new ContactHistory.Builder()
.setId(5L) .setContact(contact)
.setRevisionId(5L)
.setType(HistoryEntry.Type.CONTACT_CREATE) .setType(HistoryEntry.Type.CONTACT_CREATE)
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")
.setReason("Reason") .setReason("Reason")
@ -119,7 +140,8 @@ class HistoryEntryTest extends EntityTestCase {
IllegalArgumentException.class, IllegalArgumentException.class,
() -> () ->
new ContactHistory.Builder() new ContactHistory.Builder()
.setId(5L) .setRevisionId(5L)
.setContact(contact)
.setType(HistoryEntry.Type.CONTACT_CREATE) .setType(HistoryEntry.Type.CONTACT_CREATE)
.setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z"))
.setReason("Reason") .setReason("Reason")
@ -134,7 +156,8 @@ class HistoryEntryTest extends EntityTestCase {
IllegalArgumentException.class, IllegalArgumentException.class,
() -> () ->
new ContactHistory.Builder() new ContactHistory.Builder()
.setId(5L) .setContact(contact)
.setRevisionId(5L)
.setType(HistoryEntry.Type.SYNTHETIC) .setType(HistoryEntry.Type.SYNTHETIC)
.setModificationTime(DateTime.parse("1985-07-12T22:30:00Z")) .setModificationTime(DateTime.parse("1985-07-12T22:30:00Z"))
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")

View file

@ -1,36 +0,0 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
import static google.registry.model.translators.EppHistoryVKeyTranslatorFactory.kindPathToVKeyClass;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
/** Unit test for {@link EppHistoryVKeyTranslatorFactory}. */
class EppHistoryVKeyTranslatorFactoryTest {
@Test
void assertAllVKeyClassesHavingCreateFromOfyKeyMethod() {
kindPathToVKeyClass.forEach(
(kindPath, vKeyClass) -> {
try {
vKeyClass.getDeclaredMethod("create", com.googlecode.objectify.Key.class);
} catch (NoSuchMethodException e) {
fail("Missing static method create(com.googlecode.objectify.Key) on " + vKeyClass, e);
}
});
}
}

View file

@ -21,9 +21,6 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.common.ClassPathManager; import google.registry.model.common.ClassPathManager;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import google.registry.testing.TestObject; import google.registry.testing.TestObject;
@ -57,18 +54,6 @@ public class VKeyTranslatorFactoryTest {
assertThat(vkey.getSqlKey()).isEqualTo("ROID-1"); assertThat(vkey.getSqlKey()).isEqualTo("ROID-1");
} }
@Test
void testEntityWithAncestor() {
Key<Domain> domainKey = Key.create(Domain.class, "ROID-1");
Key<HistoryEntry> historyEntryKey = Key.create(domainKey, HistoryEntry.class, 10L);
VKey<HistoryEntry> vkey = VKeyTranslatorFactory.createVKey(historyEntryKey);
assertThat(vkey.getKind()).isEqualTo(DomainHistory.class);
assertThat(vkey.getOfyKey()).isEqualTo(historyEntryKey);
assertThat(vkey.getSqlKey()).isEqualTo(new DomainHistoryId("ROID-1", 10L));
}
@Test @Test
void testExtraEntityClass() { void testExtraEntityClass() {
TestObject testObject = TestObject.create("id", "field"); TestObject testObject = TestObject.create("id", "field");

View file

@ -1,89 +0,0 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit test for {@link DomainHistoryVKey}. */
class DomainHistoryVKeyTest {
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withCloudSql()
.withOfyTestEntities(TestEntity.class)
.withJpaUnitTestEntities(TestEntity.class)
.build();
@Test
void testRestoreSymmetricVKey() {
Key<HistoryEntry> ofyKey =
Key.create(Key.create(Domain.class, "domainRepoId"), HistoryEntry.class, 10L);
DomainHistoryVKey domainHistoryVKey = DomainHistoryVKey.create(ofyKey);
TestEntity original = new TestEntity(domainHistoryVKey);
tm().transact(() -> tm().insert(original));
TestEntity persisted = tm().transact(() -> tm().loadByKey(original.createVKey()));
assertThat(persisted).isEqualTo(original);
// Double check that the persisted.domainHistoryVKey is a symmetric VKey
assertThat(persisted.domainHistoryVKey.createSqlKey())
.isEqualTo(new DomainHistoryId("domainRepoId", 10L));
assertThat(persisted.domainHistoryVKey.createVKey())
.isEqualTo(VKey.createSql(HistoryEntry.class, new DomainHistoryId("domainRepoId", 10L)));
}
@Test
void testCreateSymmetricVKeyFromOfyKey() {
Key<HistoryEntry> ofyKey =
Key.create(Key.create(Domain.class, "domainRepoId"), HistoryEntry.class, 10L);
DomainHistoryVKey domainHistoryVKey = DomainHistoryVKey.create(ofyKey);
assertThat(domainHistoryVKey.createSqlKey())
.isEqualTo(new DomainHistoryId("domainRepoId", 10L));
assertThat(domainHistoryVKey.createVKey())
.isEqualTo(
VKey.create(HistoryEntry.class, new DomainHistoryId("domainRepoId", 10L), ofyKey));
}
@Entity
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends ImmutableObject {
@Id @javax.persistence.Id String id = "id";
DomainHistoryVKey domainHistoryVKey;
TestEntity() {}
TestEntity(DomainHistoryVKey domainHistoryVKey) {
this.domainHistoryVKey = domainHistoryVKey;
}
@Override
public VKey<TestEntity> createVKey() {
return VKey.createSql(TestEntity.class, id);
}
}
}

View file

@ -365,7 +365,7 @@ public class DomainToXjcConverterTest {
.createVKey()) .createVKey())
.setServerApproveEntities( .setServerApproveEntities(
domain.getRepoId(), domain.getRepoId(),
domainHistory.getId(), domainHistory.getRevisionId(),
ImmutableSet.of(billingEvent.createVKey())) ImmutableSet.of(billingEvent.createVKey()))
.setTransferRequestTime(DateTime.parse("1919-01-01T00:00:00Z")) .setTransferRequestTime(DateTime.parse("1919-01-01T00:00:00Z"))
.setTransferStatus(TransferStatus.PENDING) .setTransferStatus(TransferStatus.PENDING)

View file

@ -205,8 +205,8 @@ final class RdeFixtures {
.build()) .build())
.createVKey()) .createVKey())
.setServerApproveEntities( .setServerApproveEntities(
historyEntry.getDomainRepoId(), historyEntry.getRepoId(),
historyEntry.getId(), historyEntry.getRevisionId(),
ImmutableSet.of(billingEvent.createVKey())) ImmutableSet.of(billingEvent.createVKey()))
.setTransferRequestTime(DateTime.parse("1991-01-01T00:00:00Z")) .setTransferRequestTime(DateTime.parse("1991-01-01T00:00:00Z"))
.setTransferStatus(TransferStatus.PENDING) .setTransferStatus(TransferStatus.PENDING)

View file

@ -550,15 +550,14 @@ public final class DatabaseHelper {
public static Contact persistContactWithPendingTransfer( public static Contact persistContactWithPendingTransfer(
Contact contact, DateTime requestTime, DateTime expirationTime, DateTime now) { Contact contact, DateTime requestTime, DateTime expirationTime, DateTime now) {
HistoryEntry historyEntryContactTransfer = ContactHistory historyEntryContactTransfer =
persistResource( persistResource(
new ContactHistory.Builder() new ContactHistory.Builder()
.setType(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST) .setType(HistoryEntry.Type.CONTACT_TRANSFER_REQUEST)
.setContact(persistResource(contact)) .setContact(persistResource(contact))
.setModificationTime(now) .setModificationTime(now)
.setRegistrarId(contact.getCurrentSponsorRegistrarId()) .setRegistrarId(contact.getCurrentSponsorRegistrarId())
.build() .build());
.toChildHistoryEntity());
return persistResource( return persistResource(
contact contact
.asBuilder() .asBuilder()
@ -568,8 +567,8 @@ public final class DatabaseHelper {
createContactTransferDataBuilder(requestTime, expirationTime) createContactTransferDataBuilder(requestTime, expirationTime)
.setPendingTransferExpirationTime(now.plus(getContactAutomaticTransferLength())) .setPendingTransferExpirationTime(now.plus(getContactAutomaticTransferLength()))
.setServerApproveEntities( .setServerApproveEntities(
((ContactHistory) historyEntryContactTransfer).getContactRepoId(), historyEntryContactTransfer.getRepoId(),
historyEntryContactTransfer.getId(), historyEntryContactTransfer.getRevisionId(),
ImmutableSet.of( ImmutableSet.of(
// Pretend it's 3 days since the request // Pretend it's 3 days since the request
persistResource( persistResource(
@ -732,8 +731,8 @@ public final class DatabaseHelper {
.setServerApproveAutorenewPollMessage( .setServerApproveAutorenewPollMessage(
gainingClientAutorenewPollMessage.createVKey()) gainingClientAutorenewPollMessage.createVKey())
.setServerApproveEntities( .setServerApproveEntities(
historyEntryDomainTransfer.getDomainRepoId(), historyEntryDomainTransfer.getRepoId(),
historyEntryDomainTransfer.getId(), historyEntryDomainTransfer.getRevisionId(),
ImmutableSet.of( ImmutableSet.of(
transferBillingEvent.createVKey(), transferBillingEvent.createVKey(),
gainingClientAutorenewEvent.createVKey(), gainingClientAutorenewEvent.createVKey(),
@ -1106,15 +1105,13 @@ public final class DatabaseHelper {
tm().loadAllOf(PollMessage.class).stream() tm().loadAllOf(PollMessage.class).stream()
.filter( .filter(
pollMessage -> pollMessage ->
pollMessage pollMessage.getResourceId().equals(historyEntry.getRepoId())
.getResourceName() && pollMessage.getHistoryRevisionId()
.equals(historyEntry.getParent().getName()) == historyEntry.getRevisionId()
&& pollMessage.getHistoryRevisionId() == historyEntry.getId()
&& pollMessage && pollMessage
.getType() .getType()
.getResourceClass() .getResourceClass()
.getName() .equals(historyEntry.getResourceType()))
.equals(historyEntry.getParent().getKind()))
.collect(toImmutableList()))); .collect(toImmutableList())));
} }

View file

@ -30,6 +30,7 @@ import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.PostalInfo; import google.registry.model.contact.PostalInfo;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
@ -381,16 +382,19 @@ public final class FullFieldsTestEntityHelper {
Period period, Period period,
String reason, String reason,
DateTime modificationTime) { DateTime modificationTime) {
return HistoryEntry.createBuilderForResource(resource) HistoryEntry.Builder<?, ?> builder =
.setType(type) HistoryEntry.createBuilderForResource(resource)
.setPeriod(period) .setType(type)
.setXmlBytes("<xml></xml>".getBytes(UTF_8)) .setXmlBytes("<xml></xml>".getBytes(UTF_8))
.setModificationTime(modificationTime) .setModificationTime(modificationTime)
.setRegistrarId(resource.getPersistedCurrentSponsorRegistrarId()) .setRegistrarId(resource.getPersistedCurrentSponsorRegistrarId())
.setTrid(Trid.create("ABC-123", "server-trid")) .setTrid(Trid.create("ABC-123", "server-trid"))
.setBySuperuser(false) .setBySuperuser(false)
.setReason(reason) .setReason(reason)
.setRequestedByRegistrar(false) .setRequestedByRegistrar(false);
.build(); if (builder instanceof DomainHistory.Builder) {
((DomainHistory.Builder) builder).setPeriod(period);
}
return builder.build();
} }
} }

View file

@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertAbout;
import com.google.common.truth.FailureMetadata; import com.google.common.truth.FailureMetadata;
import com.google.common.truth.SimpleSubjectBuilder; import com.google.common.truth.SimpleSubjectBuilder;
import com.google.common.truth.Subject; import com.google.common.truth.Subject;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.TruthChainer.And; import google.registry.testing.TruthChainer.And;
@ -55,8 +56,12 @@ public class HistoryEntrySubject extends Subject {
return hasValue(registrarId, actual.getRegistrarId(), "getRegistrarId()"); return hasValue(registrarId, actual.getRegistrarId(), "getRegistrarId()");
} }
public And<HistoryEntrySubject> hasOtherClientId(String otherClientId) { public And<HistoryEntrySubject> hasOtherRegistrarId(String otherRegistrarId) {
return hasValue(otherClientId, actual.getOtherRegistrarId(), "getOtherRegistrarId()"); if (!(actual instanceof DomainHistory)) {
failWithActual(simpleFact("expected to be DomainHistory"));
}
return hasValue(
otherRegistrarId, ((DomainHistory) actual).getOtherRegistrarId(), "getOtherRegistrarId()");
} }
public And<HistoryEntrySubject> hasModificationTime(DateTime modificationTime) { public And<HistoryEntrySubject> hasModificationTime(DateTime modificationTime) {
@ -68,18 +73,25 @@ public class HistoryEntrySubject extends Subject {
} }
public And<HistoryEntrySubject> hasPeriod() { public And<HistoryEntrySubject> hasPeriod() {
if (actual.getPeriod() == null) { if (!(actual instanceof DomainHistory)) {
failWithActual(simpleFact("expected to be DomainHistory"));
}
if (((DomainHistory) actual).getPeriod() == null) {
failWithActual(simpleFact("expected to have a period")); failWithActual(simpleFact("expected to have a period"));
} }
return new And<>(this); return new And<>(this);
} }
public And<HistoryEntrySubject> hasPeriodYears(int years) { public And<HistoryEntrySubject> hasPeriodYears(int years) {
if (!(actual instanceof DomainHistory)) {
failWithActual(simpleFact("expected to be DomainHistory"));
}
Period actualPeriod = ((DomainHistory) actual).getPeriod();
return hasPeriod() return hasPeriod()
.and() .and()
.hasValue(Period.Unit.YEARS, actual.getPeriod().getUnit(), "getPeriod().getUnit()") .hasValue(Period.Unit.YEARS, actualPeriod.getUnit(), "getPeriod().getUnit()")
.and() .and()
.hasValue(years, actual.getPeriod().getValue(), "getPeriod().getValue()"); .hasValue(years, actualPeriod.getValue(), "getPeriod().getValue()");
} }
public And<HistoryEntrySubject> hasNoXml() { public And<HistoryEntrySubject> hasNoXml() {
@ -93,8 +105,7 @@ public class HistoryEntrySubject extends Subject {
return hasValue(reason, actual.getReason(), "getReason()"); return hasValue(reason, actual.getReason(), "getReason()");
} }
public And<HistoryEntrySubject> hasMetadataRequestedByRegistrar( public And<HistoryEntrySubject> hasMetadataRequestedByRegistrar(boolean requestedByRegistrar) {
boolean requestedByRegistrar) {
return hasValue( return hasValue(
requestedByRegistrar, actual.getRequestedByRegistrar(), "getRequestedByRegistrar()"); requestedByRegistrar, actual.getRequestedByRegistrar(), "getRequestedByRegistrar()");
} }

View file

@ -17,7 +17,7 @@ package google.registry.testing;
import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue;
/** /**
* Container for values passed to {@link AppEngineExtension} to set the logged in user for tests. * Container for values passed to {@link AppEngineExtension} to set the logged-in user for tests.
*/ */
@AutoValue @AutoValue
public abstract class UserInfo { public abstract class UserInfo {

View file

@ -24,7 +24,6 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory; import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew; import google.registry.model.poll.PollMessage.Autorenew;
import google.registry.model.poll.PollMessage.OneTime; import google.registry.model.poll.PollMessage.OneTime;
@ -54,10 +53,9 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
persistResource( persistResource(
new DomainHistory.Builder() new DomainHistory.Builder()
.setModificationTime(clock.nowUtc()) .setModificationTime(clock.nowUtc())
.setDomainRepoId(domain.getRepoId()) .setDomain(domain)
.setRegistrarId(domain.getCreationRegistrarId()) .setRegistrarId(domain.getCreationRegistrarId())
.setType(HistoryEntry.Type.DOMAIN_CREATE) .setType(HistoryEntry.Type.DOMAIN_CREATE)
.setId(2406L)
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
} }
@ -195,7 +193,7 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
return persistResource( return persistResource(
new PollMessage.OneTime.Builder() new PollMessage.OneTime.Builder()
.setId(id) .setId(id)
.setDomainHistoryId(new DomainHistoryId("FSDGS-TLD", domainHistory.getId())) .setHistoryEntry(domainHistory)
.setRegistrarId("TheRegistrar") .setRegistrarId("TheRegistrar")
.setEventTime(eventTime) .setEventTime(eventTime)
.setMsg(message) .setMsg(message)

View file

@ -25,11 +25,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import java.util.Arrays; import java.util.Arrays;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -174,8 +173,8 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
if (redeemed) { if (redeemed) {
String domainToPersist = domainName != null ? domainName : "example.foo"; String domainToPersist = domainName != null ? domainName : "example.foo";
Domain domain = persistActiveDomain(domainToPersist); Domain domain = persistActiveDomain(domainToPersist);
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L); HistoryEntryId historyEntryId = new HistoryEntryId(domain.getRepoId(), 1051L);
builder.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)); builder.setRedemptionHistoryId(historyEntryId);
} }
return persistResource(builder.build()); return persistResource(builder.build());
} }

View file

@ -42,8 +42,7 @@ import com.google.common.collect.Iterables;
import com.google.common.io.Files; import com.google.common.io.Files;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey;
import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.DeterministicStringGenerator.Rule; import google.registry.testing.DeterministicStringGenerator.Rule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
@ -446,12 +445,12 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
private AllocationToken createToken( private AllocationToken createToken(
String token, String token,
@Nullable VKey<? extends HistoryEntry> redemptionHistoryEntry, @Nullable HistoryEntryId redemptionHistoryEntryId,
@Nullable String domainName) { @Nullable String domainName) {
AllocationToken.Builder builder = AllocationToken.Builder builder =
new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE); new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE);
if (redemptionHistoryEntry != null) { if (redemptionHistoryEntryId != null) {
builder.setRedemptionHistoryEntry(redemptionHistoryEntry); builder.setRedemptionHistoryId(redemptionHistoryEntryId);
} }
builder.setDomainName(domainName); builder.setDomainName(domainName);
return builder.build(); return builder.build();

View file

@ -25,10 +25,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException; import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import google.registry.model.domain.Domain; import google.registry.model.domain.Domain;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.reporting.HistoryEntry;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -83,8 +81,8 @@ class GetAllocationTokenCommandTest extends CommandTestCase<GetAllocationTokenCo
.setToken("foo") .setToken("foo")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setDomainName("fqqdn.tld") .setDomainName("fqqdn.tld")
.setRedemptionHistoryEntry( .setRedemptionHistoryId(
HistoryEntry.createVKey(Key.create(createHistoryEntryForEppResource(domain)))) createHistoryEntryForEppResource(domain).getHistoryEntryId())
.build()); .build());
runCommand("foo"); runCommand("foo");
assertInStdout( assertInStdout(

View file

@ -212,7 +212,7 @@ public class UnrenewDomainCommandTest extends CommandTestCase<UnrenewDomainComma
"valid.tld")); "valid.tld"));
assertThat(thrown) assertThat(thrown)
.hasMessageThat() .hasMessageThat()
.isEqualTo("Aborting because some domains cannot be unrewed"); .isEqualTo("Aborting because some domains cannot be unrenewed");
assertInStderr( assertInStderr(
"Found domains that cannot be unrenewed for the following reasons:", "Found domains that cannot be unrenewed for the following reasons:",
"Domains that don't exist: [nonexistent.tld]", "Domains that don't exist: [nonexistent.tld]",

View file

@ -14,31 +14,6 @@ class google.registry.model.contact.Contact {
org.joda.time.DateTime lastEppUpdateTime; org.joda.time.DateTime lastEppUpdateTime;
org.joda.time.DateTime lastTransferTime; org.joda.time.DateTime lastTransferTime;
} }
class google.registry.model.contact.ContactBase {
@Id java.lang.String repoId;
java.lang.String contactId;
java.lang.String creationClientId;
java.lang.String currentSponsorClientId;
java.lang.String email;
java.lang.String lastEppUpdateClientId;
java.lang.String searchName;
org.joda.time.DateTime deletionTime;
org.joda.time.DateTime lastEppUpdateTime;
org.joda.time.DateTime lastTransferTime;
}
class google.registry.model.contact.ContactHistory {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent;
boolean bySuperuser;
byte[] xmlBytes;
google.registry.model.contact.ContactBase contactBase;
google.registry.model.reporting.HistoryEntry$Type type;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
java.lang.String reason;
org.joda.time.DateTime modificationTime;
}
class google.registry.model.domain.Domain { class google.registry.model.domain.Domain {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.persistence.VKey<google.registry.model.contact.Contact> adminContact; google.registry.persistence.VKey<google.registry.model.contact.Contact> adminContact;
@ -62,43 +37,6 @@ class google.registry.model.domain.Domain {
org.joda.time.DateTime lastTransferTime; org.joda.time.DateTime lastTransferTime;
org.joda.time.DateTime registrationExpirationTime; org.joda.time.DateTime registrationExpirationTime;
} }
class google.registry.model.domain.DomainBase {
@Id java.lang.String repoId;
google.registry.persistence.VKey<google.registry.model.contact.Contact> adminContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> billingContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> registrantContact;
google.registry.persistence.VKey<google.registry.model.contact.Contact> techContact;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$Autorenew> autorenewPollMessage;
google.registry.persistence.VKey<google.registry.model.poll.PollMessage$OneTime> deletePollMessage;
java.lang.String creationClientId;
java.lang.String currentSponsorClientId;
java.lang.String domainName;
java.lang.String idnTableName;
java.lang.String lastEppUpdateClientId;
java.lang.String smdId;
java.lang.String tld;
java.util.Set<google.registry.persistence.VKey<google.registry.model.host.Host>> nsHosts;
java.util.Set<java.lang.String> subordinateHosts;
org.joda.time.DateTime autorenewEndTime;
org.joda.time.DateTime deletionTime;
org.joda.time.DateTime lastEppUpdateTime;
org.joda.time.DateTime lastTransferTime;
org.joda.time.DateTime registrationExpirationTime;
}
class google.registry.model.domain.DomainHistory {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent;
boolean bySuperuser;
byte[] xmlBytes;
google.registry.model.domain.DomainBase domainBase;
google.registry.model.reporting.HistoryEntry$Type type;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
java.lang.String reason;
java.util.Set<google.registry.persistence.VKey<google.registry.model.host.Host>> nsHosts;
org.joda.time.DateTime modificationTime;
}
class google.registry.model.host.Host { class google.registry.model.host.Host {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.persistence.VKey<google.registry.model.domain.Domain> superordinateDomain; google.registry.persistence.VKey<google.registry.model.domain.Domain> superordinateDomain;
@ -112,70 +50,3 @@ class google.registry.model.host.Host {
org.joda.time.DateTime lastSuperordinateChange; org.joda.time.DateTime lastSuperordinateChange;
org.joda.time.DateTime lastTransferTime; org.joda.time.DateTime lastTransferTime;
} }
class google.registry.model.host.HostBase {
@Id java.lang.String repoId;
google.registry.persistence.VKey<google.registry.model.domain.Domain> superordinateDomain;
java.lang.String creationClientId;
java.lang.String currentSponsorClientId;
java.lang.String hostName;
java.lang.String lastEppUpdateClientId;
java.util.Set<java.net.InetAddress> inetAddresses;
org.joda.time.DateTime deletionTime;
org.joda.time.DateTime lastEppUpdateTime;
org.joda.time.DateTime lastSuperordinateChange;
org.joda.time.DateTime lastTransferTime;
}
class google.registry.model.host.HostHistory {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent;
boolean bySuperuser;
byte[] xmlBytes;
google.registry.model.host.HostBase hostBase;
google.registry.model.reporting.HistoryEntry$Type type;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
java.lang.String reason;
org.joda.time.DateTime modificationTime;
}
class google.registry.model.reporting.HistoryEntry {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<? extends google.registry.model.EppResource> parent;
boolean bySuperuser;
byte[] xmlBytes;
google.registry.model.reporting.HistoryEntry$Type type;
java.lang.Boolean requestedByRegistrar;
java.lang.String clientId;
java.lang.String otherClientId;
java.lang.String reason;
org.joda.time.DateTime modificationTime;
}
enum google.registry.model.reporting.HistoryEntry$Type {
CONTACT_CREATE;
CONTACT_DELETE;
CONTACT_DELETE_FAILURE;
CONTACT_PENDING_DELETE;
CONTACT_TRANSFER_APPROVE;
CONTACT_TRANSFER_CANCEL;
CONTACT_TRANSFER_REJECT;
CONTACT_TRANSFER_REQUEST;
CONTACT_UPDATE;
DOMAIN_ALLOCATE;
DOMAIN_AUTORENEW;
DOMAIN_CREATE;
DOMAIN_DELETE;
DOMAIN_RENEW;
DOMAIN_RESTORE;
DOMAIN_TRANSFER_APPROVE;
DOMAIN_TRANSFER_CANCEL;
DOMAIN_TRANSFER_REJECT;
DOMAIN_TRANSFER_REQUEST;
DOMAIN_UPDATE;
HOST_CREATE;
HOST_DELETE;
HOST_DELETE_FAILURE;
HOST_PENDING_DELETE;
HOST_UPDATE;
RDE_IMPORT;
SYNTHETIC;
}

View file

@ -22,8 +22,8 @@
discount_premiums boolean not null, discount_premiums boolean not null,
discount_years int4 not null, discount_years int4 not null,
domain_name text, domain_name text,
redemption_domain_history_id int8,
redemption_domain_repo_id text, redemption_domain_repo_id text,
redemption_domain_history_id int8,
registration_behavior text not null, registration_behavior text not null,
renewal_price_behavior text not null, renewal_price_behavior text not null,
token_status_transitions hstore, token_status_transitions hstore,
@ -333,6 +333,9 @@
history_server_transaction_id text, history_server_transaction_id text,
history_type text not null, history_type text not null,
history_xml_bytes bytea, history_xml_bytes bytea,
history_other_registrar_id text,
history_period_unit text,
history_period_value int4,
admin_contact text, admin_contact text,
auth_info_repo_id text, auth_info_repo_id text,
auth_info_value text, auth_info_value text,
@ -384,9 +387,6 @@
last_epp_update_time timestamptz, last_epp_update_time timestamptz,
statuses text[], statuses text[],
update_timestamp timestamptz, update_timestamp timestamptz,
history_other_registrar_id text,
history_period_unit text,
history_period_value int4,
primary key (domain_repo_id, history_revision_id) primary key (domain_repo_id, history_revision_id)
); );

View file

@ -463,7 +463,7 @@ automatic one-year renewal at the instant a domain would expire).
ICANN prohibits any registration from being longer than ten years so if the ICANN prohibits any registration from being longer than ten years so if the
request would result in a registration greater than ten years long it will fail. request would result in a registration greater than ten years long it will fail.
In practice this means it's impossible to request a ten year renewal, since that In practice this means it's impossible to request a ten-year renewal, since that
will always cause the new registration to be longer than 10 years unless it will always cause the new registration to be longer than 10 years unless it
comes in at the exact millisecond that the domain would have expired. comes in at the exact millisecond that the domain would have expired.
@ -523,14 +523,14 @@ cannot be restored. After that period anyone can re-register this name.
This flow is called a restore "request" because technically it is only supposed This flow is called a restore "request" because technically it is only supposed
to signal that the registrar requests the restore, which the registry can choose to signal that the registrar requests the restore, which the registry can choose
to process or not based on a restore report that is submitted through an out of to process or not based on a restore report that is submitted through an
band process and details the request. However, in practice this flow does the out-of-band process and details the request. However, in practice this flow does
restore immediately. This is allowable because all of the fields on a restore the restore immediately. This is allowable because all the fields on a restore
report are optional or have default values, and so by policy when the request report are optional or have default values, and so by policy when the request
comes in we consider it to have been accompanied by a default-initialized report comes in we consider it to have been accompanied by a default-initialized report
which we auto-approve. which we auto-approve.
Restores cost a fixed restore fee plus a one year renewal fee for the domain. Restores cost a fixed restore fee plus a one-year renewal fee for the domain.
The domain is restored to a single year expiration starting at the restore time, The domain is restored to a single year expiration starting at the restore time,
regardless of what the original expiration time was. regardless of what the original expiration time was.