Convert DomainBase's contacts to VKeys (#574)

* Convert DomainBase's contacts to VKeys

Convert usage of DomainBase contacts from Key to VKey.  This is the same
change as done for nameserver hosts, as it affects all external interfaces.
As with nameserver hosts, we preserve the existing representation so as not to
afffect the datastore representation.
This commit is contained in:
Michael Muller 2020-05-07 11:19:15 -04:00 committed by GitHub
parent 4b4be38d96
commit 1e1b00497b
34 changed files with 399 additions and 173 deletions

View file

@ -283,7 +283,9 @@ public class DeleteContactsAndHostsAction implements Runnable {
/** Determine whether the target resource is a linked resource on the domain. */ /** Determine whether the target resource is a linked resource on the domain. */
private boolean isLinked(DomainBase domain, Key<? extends EppResource> resourceKey) { private boolean isLinked(DomainBase domain, Key<? extends EppResource> resourceKey) {
if (resourceKey.getKind().equals(KIND_CONTACT)) { if (resourceKey.getKind().equals(KIND_CONTACT)) {
return domain.getReferencedContacts().contains(resourceKey); return domain
.getReferencedContacts()
.contains(VKey.createOfy(ContactResource.class, (Key<ContactResource>) resourceKey));
} else if (resourceKey.getKind().equals(KIND_HOST)) { } else if (resourceKey.getKind().equals(KIND_HOST)) {
return domain return domain
.getNameservers() .getNameservers()

View file

@ -48,6 +48,7 @@ import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -96,8 +97,9 @@ public final class ResourceFlowUtils {
queryForLinkedDomains(fki.getResourceKey(), now) queryForLinkedDomains(fki.getResourceKey(), now)
.limit(FAILFAST_CHECK_COUNT) .limit(FAILFAST_CHECK_COUNT)
.keys(); .keys();
VKey<R> resourceVKey = VKey.createOfy(resourceClass, fki.getResourceKey());
Predicate<DomainBase> predicate = Predicate<DomainBase> predicate =
domain -> getPotentialReferences.apply(domain).contains(fki.getResourceKey()); domain -> getPotentialReferences.apply(domain).contains(resourceVKey);
return ofy().load().keys(keys).values().stream().anyMatch(predicate) return ofy().load().keys(keys).values().stream().anyMatch(predicate)
? new ResourceToDeleteIsReferencedException() ? new ResourceToDeleteIsReferencedException()
: null; : null;
@ -184,9 +186,8 @@ public final class ResourceFlowUtils {
} }
// The roid should match one of the contacts. // The roid should match one of the contacts.
Optional<Key<ContactResource>> foundContact = Optional<Key<ContactResource>> foundContact =
domain domain.getReferencedContacts().stream()
.getReferencedContacts() .map(VKey::getOfyKey)
.stream()
.filter(key -> key.getName().equals(authRepoId)) .filter(key -> key.getName().equals(authRepoId))
.findFirst(); .findFirst();
if (!foundContact.isPresent()) { if (!foundContact.isPresent()) {

View file

@ -77,6 +77,7 @@ 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.contact.ContactResource;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.DomainCommand.Create; import google.registry.model.domain.DomainCommand.Create;
@ -352,7 +353,7 @@ public class DomainCreateFlow implements TransactionalFlow {
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null) .setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
.setSmdId(signedMarkId) .setSmdId(signedMarkId)
.setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null) .setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null)
.setRegistrant(command.getRegistrant()) .setRegistrant(VKey.createOfy(ContactResource.class, command.getRegistrant()))
.setAuthInfo(command.getAuthInfo()) .setAuthInfo(command.getAuthInfo())
.setFullyQualifiedDomainName(targetId) .setFullyQualifiedDomainName(targetId)
.setNameservers( .setNameservers(

View file

@ -35,6 +35,7 @@ import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRIS
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED; import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT; import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE; import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
@ -120,6 +121,7 @@ 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.tmch.ClaimsListShard; import google.registry.model.tmch.ClaimsListShard;
import google.registry.persistence.VKey;
import google.registry.tldconfig.idn.IdnLabelValidator; import google.registry.tldconfig.idn.IdnLabelValidator;
import google.registry.util.Idn; import google.registry.util.Idn;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -309,7 +311,10 @@ public class DomainFlowUtils {
Set<Key<HostResource>> nameservers) Set<Key<HostResource>> nameservers)
throws EppException { throws EppException {
ImmutableList.Builder<Key<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>(); ImmutableList.Builder<Key<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add); contacts.stream()
.map(DesignatedContact::getContactKey)
.map(VKey::getOfyKey)
.forEach(keysToLoad::add);
Optional.ofNullable(registrant).ifPresent(keysToLoad::add); Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
keysToLoad.addAll(nameservers); keysToLoad.addAll(nameservers);
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values()); verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
@ -355,7 +360,7 @@ public class DomainFlowUtils {
contacts.stream() contacts.stream()
.collect( .collect(
toImmutableSetMultimap( toImmutableSetMultimap(
DesignatedContact::getType, DesignatedContact::getContactKey)); DesignatedContact::getType, contact -> contact.getContactKey().getOfyKey()));
// 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)) {
@ -978,7 +983,7 @@ public class DomainFlowUtils {
for (DesignatedContact contact : contacts) { for (DesignatedContact contact : contacts) {
builder.add( builder.add(
ForeignKeyedDesignatedContact.create( ForeignKeyedDesignatedContact.create(
contact.getType(), ofy().load().key(contact.getContactKey()).now().getContactId())); contact.getType(), tm().load(contact.getContactKey()).getContactId()));
} }
return builder.build(); return builder.build();
} }

View file

@ -21,7 +21,6 @@ import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfP
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts; import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -102,16 +101,16 @@ public final class DomainInfoFlow implements Flow {
flowCustomLogic.afterValidation( flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder().setDomain(domain).build()); AfterValidationParameters.newBuilder().setDomain(domain).build());
// Prefetch all referenced resources. Calling values() blocks until loading is done. // Prefetch all referenced resources. Calling values() blocks until loading is done.
// We do nameservers separately since they've been converted to VKey.
tm().load(domain.getNameservers()); tm().load(domain.getNameservers());
ofy().load().values(domain.getReferencedContacts()).values(); tm().load(domain.getReferencedContacts());
// Registrars can only see a few fields on unauthorized domains. // Registrars can only see a few fields on unauthorized domains.
// This is a policy decision that is left up to us by the rfcs. // This is a policy decision that is left up to us by the rfcs.
DomainInfoData.Builder infoBuilder = DomainInfoData.newBuilder() DomainInfoData.Builder infoBuilder =
DomainInfoData.newBuilder()
.setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName()) .setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName())
.setRepoId(domain.getRepoId()) .setRepoId(domain.getRepoId())
.setCurrentSponsorClientId(domain.getCurrentSponsorClientId()) .setCurrentSponsorClientId(domain.getCurrentSponsorClientId())
.setRegistrant(ofy().load().key(domain.getRegistrant()).now().getContactId()); .setRegistrant(tm().load(domain.getRegistrant()).getContactId());
// If authInfo is non-null, then the caller is authorized to see the full information since we // If authInfo is non-null, then the caller is authorized to see the full information since we
// will have already verified the authInfo is valid. // will have already verified the authInfo is valid.
if (clientId.equals(domain.getCurrentSponsorClientId()) || authInfo.isPresent()) { if (clientId.equals(domain.getCurrentSponsorClientId()) || authInfo.isPresent()) {

View file

@ -60,6 +60,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainCommand.Update;
import google.registry.model.domain.DomainCommand.Update.AddRemove; import google.registry.model.domain.DomainCommand.Update.AddRemove;
@ -256,7 +257,12 @@ public final class DomainUpdateFlow implements TransactionalFlow {
.collect(toImmutableSet())) .collect(toImmutableSet()))
.addContacts(add.getContacts()) .addContacts(add.getContacts())
.removeContacts(remove.getContacts()) .removeContacts(remove.getContacts())
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant())) .setRegistrant(
firstNonNull(
change.getRegistrant() != null
? VKey.createOfy(ContactResource.class, change.getRegistrant())
: null,
domain.getRegistrant()))
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo())); .setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
return domainBuilder.build(); return domainBuilder.build();
} }
@ -269,7 +275,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
private void validateNewState(DomainBase newDomain) throws EppException { private void validateNewState(DomainBase newDomain) throws EppException {
validateNoDuplicateContacts(newDomain.getContacts()); validateNoDuplicateContacts(newDomain.getContacts());
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts()); validateRequiredContactsPresent(newDomain.getRegistrant().getOfyKey(), newDomain.getContacts());
validateDsData(newDomain.getDsData()); validateDsData(newDomain.getDsData());
validateNameserversCountForTld( validateNameserversCountForTld(
newDomain.getTld(), newDomain.getTld(),

View file

@ -14,7 +14,6 @@
package google.registry.flows.host; package google.registry.flows.host;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete; import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@ -82,17 +81,6 @@ public final class HostDeleteFlow implements TransactionalFlow {
@Inject EppResponse.Builder responseBuilder; @Inject EppResponse.Builder responseBuilder;
@Inject HostDeleteFlow() {} @Inject HostDeleteFlow() {}
/**
* Hack to convert DomainBase's nameserver VKey's to Ofy Key's.
*
* <p>We currently need this because {@code failfastForAsyncDelete()} checks to see if a name is
* in the ofy keys and is used for both nameservers and contacts. When we convert contacts to
* VKey's, we can remove this and do the conversion in {@code failfastForAsyncDelete()}.
*/
private static ImmutableSet<Key<HostResource>> getNameserverOfyKeys(DomainBase domain) {
return domain.getNameservers().stream().map(key -> key.getOfyKey()).collect(toImmutableSet());
}
@Override @Override
public final EppResponse run() throws EppException { public final EppResponse run() throws EppException {
extensionManager.register(MetadataExtension.class); extensionManager.register(MetadataExtension.class);
@ -100,7 +88,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
validateClientIsLoggedIn(clientId); validateClientIsLoggedIn(clientId);
DateTime now = tm().getTransactionTime(); DateTime now = tm().getTransactionTime();
validateHostName(targetId); validateHostName(targetId);
failfastForAsyncDelete(targetId, now, HostResource.class, HostDeleteFlow::getNameserverOfyKeys); failfastForAsyncDelete(targetId, now, HostResource.class, DomainBase::getNameservers);
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES); verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
if (!isSuperuser) { if (!isSuperuser) {

View file

@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime; import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
@ -30,6 +31,8 @@ import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.annotations.ReportedOn; import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.PostalInfo.Type; import google.registry.model.contact.PostalInfo.Type;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.persistence.VKey;
import google.registry.persistence.WithStringVKey;
import google.registry.schema.replay.DatastoreAndSqlEntity; import google.registry.schema.replay.DatastoreAndSqlEntity;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -60,6 +63,7 @@ import org.joda.time.DateTime;
@javax.persistence.Index(columnList = "searchName") @javax.persistence.Index(columnList = "searchName")
}) })
@ExternalMessagingName("contact") @ExternalMessagingName("contact")
@WithStringVKey
public class ContactResource extends EppResource public class ContactResource extends EppResource
implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData { implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData {
@ -193,6 +197,10 @@ public class ContactResource extends EppResource
}) })
Disclose disclose; Disclose disclose;
public VKey<ContactResource> createVKey() {
return VKey.createOfy(ContactResource.class, Key.create(this));
}
public String getContactId() { public String getContactId() {
return contactId; return contactId;
} }

View file

@ -18,9 +18,11 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.persistence.VKey;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlEnumValue;
@ -36,6 +38,9 @@ import javax.xml.bind.annotation.XmlEnumValue;
* situation with hosts where client-side renames would make that data stale. However, we sometimes * situation with hosts where client-side renames would make that data stale. However, we sometimes
* rename contacts internally ourselves, and it's easier to use the same model for both cases. * rename contacts internally ourselves, and it's easier to use the same model for both cases.
* *
* <p>This entity type is not persisted in Cloud SQL. The different roles are represented as
* separate fields in the Domain table.
*
* @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping * @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping
* - Contact and Client Identifiers</a> * - Contact and Client Identifiers</a>
*/ */
@ -58,22 +63,28 @@ public class DesignatedContact extends ImmutableObject {
REGISTRANT REGISTRANT
} }
public static DesignatedContact create(Type type, Key<ContactResource> contact) { public static DesignatedContact create(Type type, VKey<ContactResource> contact) {
DesignatedContact instance = new DesignatedContact(); DesignatedContact instance = new DesignatedContact();
instance.type = type; instance.type = type;
instance.contact = checkArgumentNotNull(contact, "Must specify contact key"); instance.contactVKey = checkArgumentNotNull(contact, "Must specify contact key");
instance.contact = contact.maybeGetOfyKey().orElse(null);
return instance; return instance;
} }
Type type; Type type;
@Index Key<ContactResource> contact; @Index Key<ContactResource> contact;
@Ignore VKey<ContactResource> contactVKey;
public Type getType() { public Type getType() {
return type; return type;
} }
public Key<ContactResource> getContactKey() { public VKey<ContactResource> getContactKey() {
return contact; return contactVKey;
}
public DesignatedContact reconstitute() {
return create(type, VKey.createOfy(ContactResource.class, contact));
} }
} }

View file

@ -80,6 +80,7 @@ import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Embedded; import javax.persistence.Embedded;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.PostLoad;
import javax.persistence.Transient; import javax.persistence.Transient;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Interval; import org.joda.time.Interval;
@ -155,6 +156,17 @@ public class DomainBase extends EppResource
*/ */
@Transient Set<DesignatedContact> allContacts; @Transient Set<DesignatedContact> allContacts;
/**
* Contacts as they are stored in cloud SQL.
*
* <p>This information is duplicated in allContacts, and must be kept in sync with it.
*/
@Ignore VKey<ContactResource> adminContact;
@Ignore VKey<ContactResource> billingContact;
@Ignore VKey<ContactResource> techContact;
@Ignore VKey<ContactResource> registrantContact;
/** Authorization info (aka transfer secret) of the domain. */ /** Authorization info (aka transfer secret) of the domain. */
@Embedded @Embedded
@AttributeOverrides({ @AttributeOverrides({
@ -261,6 +273,34 @@ public class DomainBase extends EppResource
nullToEmptyImmutableCopy(nsHosts).stream() nullToEmptyImmutableCopy(nsHosts).stream()
.map(hostKey -> VKey.createOfy(HostResource.class, hostKey)) .map(hostKey -> VKey.createOfy(HostResource.class, hostKey))
.collect(toImmutableSet()); .collect(toImmutableSet());
// Reconstitute all of the contacts so that they have VKeys.
allContacts =
allContacts.stream().map(contact -> contact.reconstitute()).collect(toImmutableSet());
setContactFields(allContacts, true);
}
@PostLoad
void postLoad() {
// Reconstitute the contact list.
ImmutableSet.Builder<DesignatedContact> contactsBuilder =
new ImmutableSet.Builder<DesignatedContact>();
if (registrantContact != null) {
contactsBuilder.add(
DesignatedContact.create(DesignatedContact.Type.REGISTRANT, registrantContact));
}
if (billingContact != null) {
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.BILLING, billingContact));
}
if (techContact != null) {
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.TECH, techContact));
}
if (adminContact != null) {
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.ADMIN, adminContact));
}
allContacts = contactsBuilder.build();
} }
public ImmutableSet<String> getSubordinateHosts() { public ImmutableSet<String> getSubordinateHosts() {
@ -515,13 +555,20 @@ public class DomainBase extends EppResource
} }
/** A key to the registrant who registered this domain. */ /** A key to the registrant who registered this domain. */
public Key<ContactResource> getRegistrant() { public VKey<ContactResource> getRegistrant() {
return nullToEmpty(allContacts) return registrantContact;
.stream() }
.filter(IS_REGISTRANT)
.findFirst() public VKey<ContactResource> getAdminContact() {
.get() return adminContact;
.getContactKey(); }
public VKey<ContactResource> getBillingContact() {
return billingContact;
}
public VKey<ContactResource> getTechContact() {
return techContact;
} }
/** Associated contacts for the domain (other than registrant). */ /** Associated contacts for the domain (other than registrant). */
@ -537,7 +584,7 @@ public class DomainBase extends EppResource
} }
/** Returns all referenced contacts from this domain or application. */ /** Returns all referenced contacts from this domain or application. */
public ImmutableSet<Key<ContactResource>> getReferencedContacts() { public ImmutableSet<VKey<ContactResource>> getReferencedContacts() {
return nullToEmptyImmutableCopy(allContacts) return nullToEmptyImmutableCopy(allContacts)
.stream() .stream()
.map(DesignatedContact::getContactKey) .map(DesignatedContact::getContactKey)
@ -549,6 +596,37 @@ public class DomainBase extends EppResource
return tld; return tld;
} }
/**
* Sets the individual contact fields from {@code contacts}.
*
* <p>The registrant field is only set if {@code includeRegistrant} is true, as this field needs
* to be set in some circumstances but not in others.
*/
private void setContactFields(Set<DesignatedContact> contacts, boolean includeRegistrant) {
// Set the individual contact fields.
for (DesignatedContact contact : contacts) {
switch (contact.getType()) {
case BILLING:
billingContact = contact.getContactKey();
break;
case TECH:
techContact = contact.getContactKey();
break;
case ADMIN:
adminContact = contact.getContactKey();
break;
case REGISTRANT:
if (includeRegistrant) {
registrantContact = contact.getContactKey();
}
break;
default:
throw new IllegalArgumentException("Unknown contact resource type: " + contact.getType());
}
}
}
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */ /** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
private static final Predicate<DesignatedContact> IS_REGISTRANT = private static final Predicate<DesignatedContact> IS_REGISTRANT =
(DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type); (DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type);
@ -593,7 +671,11 @@ public class DomainBase extends EppResource
checkArgumentNotNull( checkArgumentNotNull(
emptyToNull(instance.fullyQualifiedDomainName), "Missing fullyQualifiedDomainName"); emptyToNull(instance.fullyQualifiedDomainName), "Missing fullyQualifiedDomainName");
checkArgument(instance.allContacts.stream().anyMatch(IS_REGISTRANT), "Missing registrant"); if (instance.getRegistrant() == null
&& instance.allContacts.stream().anyMatch(IS_REGISTRANT)) {
throw new IllegalArgumentException("registrant is null but is in allContacts");
}
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
instance.tld = getTldFromDomainName(instance.fullyQualifiedDomainName); instance.tld = getTldFromDomainName(instance.fullyQualifiedDomainName);
return super.build(); return super.build();
} }
@ -611,11 +693,14 @@ public class DomainBase extends EppResource
return thisCastToDerived(); return thisCastToDerived();
} }
public Builder setRegistrant(Key<ContactResource> registrant) { public Builder setRegistrant(VKey<ContactResource> registrant) {
// Replace the registrant contact inside allContacts. // Replace the registrant contact inside allContacts.
getInstance().allContacts = union( getInstance().allContacts = union(
getInstance().getContacts(), getInstance().getContacts(),
DesignatedContact.create(Type.REGISTRANT, checkArgumentNotNull(registrant))); DesignatedContact.create(Type.REGISTRANT, checkArgumentNotNull(registrant)));
// Set the registrant field specifically.
getInstance().registrantContact = registrant;
return thisCastToDerived(); return thisCastToDerived();
} }
@ -673,12 +758,16 @@ public class DomainBase extends EppResource
public Builder setContacts(ImmutableSet<DesignatedContact> contacts) { public Builder setContacts(ImmutableSet<DesignatedContact> contacts) {
checkArgument(contacts.stream().noneMatch(IS_REGISTRANT), "Registrant cannot be a contact"); checkArgument(contacts.stream().noneMatch(IS_REGISTRANT), "Registrant cannot be a contact");
// Replace the non-registrant contacts inside allContacts. // Replace the non-registrant contacts inside allContacts.
getInstance().allContacts = getInstance().allContacts =
Streams.concat( Streams.concat(
nullToEmpty(getInstance().allContacts).stream().filter(IS_REGISTRANT), nullToEmpty(getInstance().allContacts).stream().filter(IS_REGISTRANT),
contacts.stream()) contacts.stream())
.collect(toImmutableSet()); .collect(toImmutableSet());
// Set the individual fields.
getInstance().setContactFields(contacts, false);
return thisCastToDerived(); return thisCastToDerived();
} }

View file

@ -41,6 +41,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.persistence.VKey;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -188,7 +189,7 @@ public class DomainCommand {
now); now);
for (DesignatedContact contact : contacts) { for (DesignatedContact contact : contacts) {
if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) { if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) {
clone.registrant = contact.getContactKey(); clone.registrant = contact.getContactKey().getOfyKey();
clone.contacts = forceEmptyToNull(difference(contacts, contact)); clone.contacts = forceEmptyToNull(difference(contacts, contact));
break; break;
} }
@ -439,8 +440,10 @@ public class DomainCommand {
loadByForeignKeysCached(foreignKeys.build(), ContactResource.class, now); loadByForeignKeysCached(foreignKeys.build(), ContactResource.class, now);
ImmutableSet.Builder<DesignatedContact> linkedContacts = new ImmutableSet.Builder<>(); ImmutableSet.Builder<DesignatedContact> linkedContacts = new ImmutableSet.Builder<>();
for (ForeignKeyedDesignatedContact contact : contacts) { for (ForeignKeyedDesignatedContact contact : contacts) {
linkedContacts.add(DesignatedContact.create( linkedContacts.add(
contact.type, loadedContacts.get(contact.contactId))); DesignatedContact.create(
contact.type,
VKey.createOfy(ContactResource.class, loadedContacts.get(contact.contactId))));
} }
return linkedContacts.build(); return linkedContacts.build();
} }

View file

@ -36,6 +36,7 @@ import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFac
import google.registry.config.RegistryEnvironment; import google.registry.config.RegistryEnvironment;
import google.registry.model.EntityClasses; import google.registry.model.EntityClasses;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory; import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory; import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
@ -130,6 +131,7 @@ public class ObjectifyService {
new InetAddressTranslatorFactory(), new InetAddressTranslatorFactory(),
new MoneyStringTranslatorFactory(), new MoneyStringTranslatorFactory(),
new ReadableInstantUtcTranslatorFactory(), new ReadableInstantUtcTranslatorFactory(),
new VKeyTranslatorFactory<ContactResource>(ContactResource.class),
new VKeyTranslatorFactory<HostResource>(HostResource.class), new VKeyTranslatorFactory<HostResource>(HostResource.class),
new UpdateAutoTimestampTranslatorFactory())) { new UpdateAutoTimestampTranslatorFactory())) {
factory().getTranslators().add(translatorFactory); factory().getTranslators().add(translatorFactory);

View file

@ -52,6 +52,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarContact; import google.registry.model.registrar.RegistrarContact;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.rdap.RdapDataStructures.Event; import google.registry.rdap.RdapDataStructures.Event;
import google.registry.rdap.RdapDataStructures.EventAction; import google.registry.rdap.RdapDataStructures.EventAction;
import google.registry.rdap.RdapDataStructures.Link; import google.registry.rdap.RdapDataStructures.Link;
@ -346,7 +347,12 @@ public class RdapJsonFormatter {
ImmutableSet.copyOf(tm().load(domainBase.getNameservers())); ImmutableSet.copyOf(tm().load(domainBase.getNameservers()));
// Load the registrant and other contacts and add them to the data. // Load the registrant and other contacts and add them to the data.
Map<Key<ContactResource>, ContactResource> loadedContacts = Map<Key<ContactResource>, ContactResource> loadedContacts =
ofy().load().keys(domainBase.getReferencedContacts()); ofy()
.load()
.keys(
domainBase.getReferencedContacts().stream()
.map(VKey::getOfyKey)
.collect(toImmutableSet()));
// RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY // RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY
// have others. We also add the BILLING. // have others. We also add the BILLING.
// //
@ -361,7 +367,7 @@ public class RdapJsonFormatter {
.sorted(DESIGNATED_CONTACT_ORDERING) .sorted(DESIGNATED_CONTACT_ORDERING)
.collect( .collect(
toImmutableSetMultimap( toImmutableSetMultimap(
DesignatedContact::getContactKey, DesignatedContact::getType)); contact -> contact.getContactKey().getOfyKey(), DesignatedContact::getType));
for (Key<ContactResource> contactKey : contactsToRoles.keySet()) { for (Key<ContactResource> contactKey : contactsToRoles.keySet()) {
Set<RdapEntity.Role> roles = Set<RdapEntity.Role> roles =

View file

@ -15,13 +15,12 @@
package google.registry.rde; package google.registry.rde;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
@ -30,6 +29,7 @@ import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.rde.RdeMode; import google.registry.model.rde.RdeMode;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.persistence.VKey;
import google.registry.util.Idn; import google.registry.util.Idn;
import google.registry.xjc.domain.XjcDomainContactAttrType; import google.registry.xjc.domain.XjcDomainContactAttrType;
import google.registry.xjc.domain.XjcDomainContactType; import google.registry.xjc.domain.XjcDomainContactType;
@ -167,11 +167,11 @@ final class DomainBaseToXjcConverter {
// o An OPTIONAL <registrant> element that contain the identifier for // o An OPTIONAL <registrant> element that contain the identifier for
// the human or organizational social information object associated // the human or organizational social information object associated
// as the holder of the domain name object. // as the holder of the domain name object.
Key<ContactResource> registrant = model.getRegistrant(); VKey<ContactResource> registrant = model.getRegistrant();
if (registrant == null) { if (registrant == null) {
logger.atWarning().log("Domain %s has no registrant contact.", domainName); logger.atWarning().log("Domain %s has no registrant contact.", domainName);
} else { } else {
ContactResource registrantContact = ofy().load().key(registrant).now(); ContactResource registrantContact = tm().load(registrant);
checkState( checkState(
registrantContact != null, registrantContact != null,
"Registrant contact %s on domain %s does not exist", "Registrant contact %s on domain %s does not exist",
@ -304,7 +304,7 @@ final class DomainBaseToXjcConverter {
"Contact key for type %s is null on domain %s", "Contact key for type %s is null on domain %s",
model.getType(), model.getType(),
domainName); domainName);
ContactResource contact = ofy().load().key(model.getContactKey()).now(); ContactResource contact = tm().load(model.getContactKey());
checkState( checkState(
contact != null, contact != null,
"Contact %s on domain %s does not exist", "Contact %s on domain %s does not exist",

View file

@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
@ -291,11 +291,9 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
ImmutableSet<String> getContactsOfType( ImmutableSet<String> getContactsOfType(
DomainBase domainBase, final DesignatedContact.Type contactType) { DomainBase domainBase, final DesignatedContact.Type contactType) {
return domainBase return domainBase.getContacts().stream()
.getContacts()
.stream()
.filter(contact -> contact.getType().equals(contactType)) .filter(contact -> contact.getType().equals(contactType))
.map(contact -> ofy().load().key(contact.getContactKey()).now().getContactId()) .map(contact -> tm().load(contact.getContactKey()).getContactId())
.collect(toImmutableSet()); .collect(toImmutableSet());
} }
} }

View file

@ -22,7 +22,6 @@ import static google.registry.xml.UtcDateTimeAdapter.getFormattedString;
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.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.contact.ContactPhoneNumber; import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
@ -35,6 +34,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact; import google.registry.model.registrar.RegistrarContact;
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum; import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
import google.registry.persistence.VKey;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -128,7 +128,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
} }
/** Returns the contact of the given type. */ /** Returns the contact of the given type. */
private Optional<Key<ContactResource>> getContactReference(Type type) { private Optional<VKey<ContactResource>> getContactReference(Type type) {
Optional<DesignatedContact> contactOfType = Optional<DesignatedContact> contactOfType =
domain.getContacts().stream().filter(d -> d.getType() == type).findFirst(); domain.getContacts().stream().filter(d -> d.getType() == type).findFirst();
return contactOfType.map(DesignatedContact::getContactKey); return contactOfType.map(DesignatedContact::getContactKey);
@ -149,14 +149,14 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
/** Emit the contact entry of the given type. */ /** Emit the contact entry of the given type. */
DomainEmitter emitContact( DomainEmitter emitContact(
String contactType, Optional<Key<ContactResource>> contact, boolean preferUnicode) { String contactType, Optional<VKey<ContactResource>> contact, boolean preferUnicode) {
if (!contact.isPresent()) { if (!contact.isPresent()) {
return this; return this;
} }
// If we refer to a contact that doesn't exist, that's a bug. It means referential integrity // If we refer to a contact that doesn't exist, that's a bug. It means referential integrity
// has somehow been broken. We skip the rest of this contact, but log it to hopefully bring it // has somehow been broken. We skip the rest of this contact, but log it to hopefully bring it
// someone's attention. // someone's attention.
ContactResource contactResource = EppResource.loadCached(contact.get()); ContactResource contactResource = EppResource.loadCached(contact.get().getOfyKey());
if (contactResource == null) { if (contactResource == null) {
logger.atSevere().log( logger.atSevere().log(
"(BUG) Broken reference found from domain %s to contact %s", "(BUG) Broken reference found from domain %s to contact %s",

View file

@ -52,6 +52,7 @@
<!-- Generated converters for VKey --> <!-- Generated converters for VKey -->
<class>google.registry.model.host.VKeyConverter_HostResource</class> <class>google.registry.model.host.VKeyConverter_HostResource</class>
<class>google.registry.model.contact.VKeyConverter_ContactResource</class>
<!-- TODO(weiminyu): check out application-layer validation. --> <!-- TODO(weiminyu): check out application-layer validation. -->
<validation-mode>NONE</validation-mode> <validation-mode>NONE</validation-mode>

View file

@ -190,7 +190,9 @@ public class DeleteContactsAndHostsActionTest
.hasDeletionTime(END_OF_TIME); .hasDeletionTime(END_OF_TIME);
DomainBase domainReloaded = DomainBase domainReloaded =
loadByForeignKey(DomainBase.class, "example.tld", clock.nowUtc()).get(); loadByForeignKey(DomainBase.class, "example.tld", clock.nowUtc()).get();
assertThat(domainReloaded.getReferencedContacts()).contains(Key.create(contactUpdated)); // We have to check for the objectify key here, specifically, as the SQL side of the key does
// not get persisted.
assertThat(domainReloaded.getReferencedContacts()).contains(contactUpdated.createVKey());
HistoryEntry historyEntry = HistoryEntry historyEntry =
getOnlyHistoryEntryOfType(contactUpdated, HistoryEntry.Type.CONTACT_DELETE_FAILURE); getOnlyHistoryEntryOfType(contactUpdated, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
assertPollMessageFor( assertPollMessageFor(

View file

@ -151,7 +151,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
newDomainBase(getUniqueIdFromCommand()) newDomainBase(getUniqueIdFromCommand())
.asBuilder() .asBuilder()
.setCreationTimeForTest(TIME_BEFORE_FLOW) .setCreationTimeForTest(TIME_BEFORE_FLOW)
.setRegistrant(Key.create(contact)) .setRegistrant(contact.createVKey())
.setRegistrationExpirationTime(expirationTime) .setRegistrationExpirationTime(expirationTime)
.build(); .build();
earlierHistoryEntry = earlierHistoryEntry =
@ -700,7 +700,9 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
newDomainBase("example1.tld") newDomainBase("example1.tld")
.asBuilder() .asBuilder()
.setRegistrant( .setRegistrant(
Key.create(loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get())) loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())
.get()
.createVKey())
.setNameservers(ImmutableSet.of(host.createKey())) .setNameservers(ImmutableSet.of(host.createKey()))
.setDeletionTime(START_OF_TIME) .setDeletionTime(START_OF_TIME)
.build()); .build());

View file

@ -112,11 +112,11 @@ public class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Dom
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z")) .setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z")) .setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z")) .setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
.setRegistrant(Key.create(registrant)) .setRegistrant(registrant.createVKey())
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, Key.create(contact)), DesignatedContact.create(Type.ADMIN, contact.createVKey()),
DesignatedContact.create(Type.TECH, Key.create(contact)))) DesignatedContact.create(Type.TECH, contact.createVKey())))
.setNameservers( .setNameservers(
inactive ? null : ImmutableSet.of(host1.createKey(), host2.createKey())) inactive ? null : ImmutableSet.of(host1.createKey(), host2.createKey()))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR"))) .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))

View file

@ -19,8 +19,8 @@ import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD; import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatastoreHelper.assertBillingEvents; import static google.registry.testing.DatastoreHelper.assertBillingEvents;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
@ -135,10 +135,10 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.TECH, Key.create(mak21Contact)), DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
DesignatedContact.create(Type.ADMIN, Key.create(mak21Contact)), DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
DesignatedContact.create(Type.BILLING, Key.create(mak21Contact)))) DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
.setRegistrant(Key.create(mak21Contact)) .setRegistrant(mak21Contact.createVKey())
.setNameservers(ImmutableSet.of(host.createKey())) .setNameservers(ImmutableSet.of(host.createKey()))
.build()); .build());
historyEntryDomainCreate = historyEntryDomainCreate =
@ -160,8 +160,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact)), DesignatedContact.create(Type.TECH, sh8013Contact.createVKey()),
DesignatedContact.create(Type.ADMIN, Key.create(unusedContact)))) DesignatedContact.create(Type.ADMIN, unusedContact.createVKey())))
.setNameservers(ImmutableSet.of(host.createKey())) .setNameservers(ImmutableSet.of(host.createKey()))
.build()); .build());
historyEntryDomainCreate = historyEntryDomainCreate =
@ -298,7 +298,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
contactsBuilder.add( contactsBuilder.add(
DesignatedContact.create( DesignatedContact.create(
DesignatedContact.Type.values()[i % 4], DesignatedContact.Type.values()[i % 4],
Key.create(persistActiveContact(String.format("max_test_%d", i))))); persistActiveContact(String.format("max_test_%d", i)).createVKey()));
} }
ImmutableList<DesignatedContact> contacts = contactsBuilder.build(); ImmutableList<DesignatedContact> contacts = contactsBuilder.build();
persistResource( persistResource(
@ -319,8 +319,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
assertThat(domain.getNameservers()).hasSize(13); assertThat(domain.getNameservers()).hasSize(13);
// getContacts does not return contacts of type REGISTRANT, so check these separately. // getContacts does not return contacts of type REGISTRANT, so check these separately.
assertThat(domain.getContacts()).hasSize(3); assertThat(domain.getContacts()).hasSize(3);
assertThat(ofy().load().key(domain.getRegistrant()).now().getContactId()) assertThat(tm().load(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
.isEqualTo("max_test_7");
assertNoBillingEvents(); assertNoBillingEvents();
assertDnsTasksEnqueued("example.tld"); assertDnsTasksEnqueued("example.tld");
} }
@ -403,7 +402,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
persistResource( persistResource(
newDomainBase(getUniqueIdFromCommand()) newDomainBase(getUniqueIdFromCommand())
.asBuilder() .asBuilder()
.setRegistrant(Key.create(sh8013)) .setRegistrant(sh8013.createVKey())
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
runFlowAssertResponse(loadFile("generic_success_response.xml")); runFlowAssertResponse(loadFile("generic_success_response.xml"));
@ -415,7 +414,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
persistReferencedEntities(); persistReferencedEntities();
ContactResource sh8013 = ContactResource sh8013 =
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get(); loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get();
Key<ContactResource> sh8013Key = Key.create(sh8013); VKey<ContactResource> sh8013Key = sh8013.createVKey();
persistResource( persistResource(
newDomainBase(getUniqueIdFromCommand()) newDomainBase(getUniqueIdFromCommand())
.asBuilder() .asBuilder()
@ -866,8 +865,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.setContacts( .setContacts(
DesignatedContact.create( DesignatedContact.create(
Type.TECH, Type.TECH,
Key.create( loadByForeignKey(ContactResource.class, "foo", clock.nowUtc())
loadByForeignKey(ContactResource.class, "foo", clock.nowUtc()).get()))) .get()
.createVKey()))
.build()); .build());
EppException thrown = assertThrows(DuplicateContactForRoleException.class, this::runFlow); EppException thrown = assertThrows(DuplicateContactForRoleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -1077,8 +1077,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.setContacts( .setContacts(
DesignatedContact.create( DesignatedContact.create(
Type.TECH, Type.TECH,
Key.create( loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get()))) .get()
.createVKey()))
.build()); .build());
EppException thrown = assertThrows(AddRemoveSameValueException.class, this::runFlow); EppException thrown = assertThrows(AddRemoveSameValueException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -1093,8 +1094,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)), DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact)))) DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
.build()); .build());
EppException thrown = assertThrows(MissingAdminContactException.class, this::runFlow); EppException thrown = assertThrows(MissingAdminContactException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -1109,8 +1110,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)), DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact)))) DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
.build()); .build());
EppException thrown = assertThrows(MissingTechnicalContactException.class, this::runFlow); EppException thrown = assertThrows(MissingTechnicalContactException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml(); assertAboutEppExceptions().that(thrown).marshalsToXml();
@ -1223,7 +1224,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo")) .setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
.build()); .build());
runFlow(); runFlow();
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId()) assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
.isEqualTo("sh8013"); .isEqualTo("sh8013");
} }
@ -1237,10 +1238,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.getContacts() .getContacts()
.forEach( .forEach(
contact -> { contact -> {
assertThat(ofy().load().key(contact.getContactKey()).now().getContactId()) assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("mak21");
.isEqualTo("mak21");
}); });
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId()) assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
.isEqualTo("mak21"); .isEqualTo("mak21");
runFlow(); runFlow();
@ -1249,10 +1249,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.getContacts() .getContacts()
.forEach( .forEach(
contact -> { contact -> {
assertThat(ofy().load().key(contact.getContactKey()).now().getContactId()) assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("sh8013");
.isEqualTo("sh8013");
}); });
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId()) assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
.isEqualTo("sh8013"); .isEqualTo("sh8013");
} }

View file

@ -22,7 +22,6 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME;
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.googlecode.objectify.Key;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type; import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchNotice;
@ -30,6 +29,7 @@ import google.registry.model.domain.secdns.DelegationSignerData;
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.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.transfer.TransferData;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestRules; import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension; import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
@ -56,15 +56,20 @@ public class DomainBaseSqlTest {
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension(); new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
DomainBase domain; DomainBase domain;
Key<ContactResource> contactKey; VKey<ContactResource> contactKey;
Key<ContactResource> contact2Key; VKey<ContactResource> contact2Key;
VKey<HostResource> host1VKey; VKey<HostResource> host1VKey;
HostResource host; HostResource host;
ContactResource contact;
ContactResource contact2;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
contactKey = Key.create(ContactResource.class, "contact_id1"); saveRegistrar("registrar1");
contact2Key = Key.create(ContactResource.class, "contact_id2"); saveRegistrar("registrar2");
saveRegistrar("registrar3");
contactKey = VKey.createSql(ContactResource.class, "contact_id1");
contact2Key = VKey.createSql(ContactResource.class, "contact_id2");
host1VKey = VKey.createSql(HostResource.class, "host1"); host1VKey = VKey.createSql(HostResource.class, "host1");
@ -104,23 +109,26 @@ public class DomainBaseSqlTest {
.setCreationClientId("registrar1") .setCreationClientId("registrar1")
.setPersistedCurrentSponsorClientId("registrar2") .setPersistedCurrentSponsorClientId("registrar2")
.build(); .build();
contact = makeContact("contact_id1");
contact2 = makeContact("contact_id2");
} }
@Test @Test
public void testDomainBasePersistence() { public void testDomainBasePersistence() {
saveRegistrar("registrar1");
saveRegistrar("registrar2");
saveRegistrar("registrar3");
jpaTm() jpaTm()
.transact( .transact(
() -> { () -> {
// Persist the domain. // Persist the contacts. Note that these need to be persisted before the domain
EntityManager em = jpaTm().getEntityManager(); // otherwise we get a foreign key constraint error.
em.persist(domain); jpaTm().saveNew(contact);
jpaTm().saveNew(contact2);
// Persist the host. // Persist the domain.
em.persist(host); jpaTm().saveNew(domain);
// Persist the host. This does _not_ need to be persisted before the domain,
// presumably because its relationship is stored in a join table.
jpaTm().saveNew(host);
}); });
jpaTm() jpaTm()
@ -130,13 +138,11 @@ public class DomainBaseSqlTest {
EntityManager em = jpaTm().getEntityManager(); EntityManager em = jpaTm().getEntityManager();
DomainBase result = em.find(DomainBase.class, "4-COM"); DomainBase result = em.find(DomainBase.class, "4-COM");
// Fix contacts, grace period and DS data, since we can't persist them yet. // Fix grace period and DS data, since we can't persist them yet.
result = result =
result result
.asBuilder() .asBuilder()
.setRegistrant(contactKey) .setRegistrant(contactKey)
.setContacts(
ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setDsData( .setDsData(
ImmutableSet.of( ImmutableSet.of(
DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2}))) DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
@ -151,16 +157,40 @@ public class DomainBaseSqlTest {
} }
@Test @Test
public void testForeignKeyConstraints() { public void testHostForeignKeyConstraints() {
assertThrowForeignKeyViolation( assertThrowForeignKeyViolation(
() -> { () -> {
jpaTm() jpaTm()
.transact( .transact(
() -> { () -> {
// Persist the domain without the associated host object. // Persist the domain without the associated host object.
EntityManager em = jpaTm().getEntityManager(); jpaTm().saveNew(contact);
em.persist(domain); jpaTm().saveNew(contact2);
jpaTm().saveNew(domain);
}); });
}); });
} }
@Test
public void testContactForeignKeyConstraints() {
assertThrowForeignKeyViolation(
() -> {
jpaTm()
.transact(
() -> {
// Persist the domain without the associated contact objects.
jpaTm().saveNew(domain);
jpaTm().saveNew(host);
});
});
}
public static ContactResource makeContact(String repoId) {
return new ContactResource.Builder()
.setRepoId(repoId)
.setCreationClientId("registrar1")
.setTransferData(new TransferData.Builder().build())
.setPersistedCurrentSponsorClientId("registrar1")
.build();
}
} }

View file

@ -80,20 +80,20 @@ public class DomainBaseTest extends EntityTestCase {
.setRepoId("1-COM") .setRepoId("1-COM")
.build()) .build())
.createKey(); .createKey();
Key<ContactResource> contact1Key = VKey<ContactResource> contact1Key =
Key.create(
persistResource( persistResource(
new ContactResource.Builder() new ContactResource.Builder()
.setContactId("contact_id1") .setContactId("contact_id1")
.setRepoId("2-COM") .setRepoId("2-COM")
.build())); .build())
Key<ContactResource> contact2Key = .createVKey();
Key.create( VKey<ContactResource> contact2Key =
persistResource( persistResource(
new ContactResource.Builder() new ContactResource.Builder()
.setContactId("contact_id2") .setContactId("contact_id2")
.setRepoId("3-COM") .setRepoId("3-COM")
.build())); .build())
.createVKey();
Key<HistoryEntry> historyEntryKey = Key<HistoryEntry> historyEntryKey =
Key.create(persistResource(new HistoryEntry.Builder().setParent(domainKey).build())); Key.create(persistResource(new HistoryEntry.Builder().setParent(domainKey).build()));
oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1); oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1);

View file

@ -240,22 +240,22 @@ public class DomainBaseToXjcConverterTest {
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create( DesignatedContact.create(
DesignatedContact.Type.ADMIN, DesignatedContact.Type.ADMIN,
Key.create(
makeContactResource( makeContactResource(
clock, clock,
"10-Q9JYB4C", "10-Q9JYB4C",
"5372808-IRL", "5372808-IRL",
"be that word our sign in parting", "be that word our sign in parting",
"BOFH@cat.みんな"))), "BOFH@cat.みんな")
.createVKey()),
DesignatedContact.create( DesignatedContact.create(
DesignatedContact.Type.TECH, DesignatedContact.Type.TECH,
Key.create(
makeContactResource( makeContactResource(
clock, clock,
"11-Q9JYB4C", "11-Q9JYB4C",
"5372808-TRL", "5372808-TRL",
"bird or fiend!? i shrieked upstarting", "bird or fiend!? i shrieked upstarting",
"bog@cat.みんな"))))) "bog@cat.みんな")
.createVKey())))
.setCreationClientId("LawyerCat") .setCreationClientId("LawyerCat")
.setCreationTimeForTest(DateTime.parse("1900-01-01T00:00:00Z")) .setCreationTimeForTest(DateTime.parse("1900-01-01T00:00:00Z"))
.setPersistedCurrentSponsorClientId("GetTheeBack") .setPersistedCurrentSponsorClientId("GetTheeBack")
@ -273,9 +273,9 @@ public class DomainBaseToXjcConverterTest {
makeHostResource(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef") makeHostResource(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef")
.createKey())) .createKey()))
.setRegistrant( .setRegistrant(
Key.create(
makeContactResource( makeContactResource(
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな"))) clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
.createVKey())
.setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z")) .setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z"))
.setGracePeriods( .setGracePeriods(
ImmutableSet.of( ImmutableSet.of(

View file

@ -63,9 +63,8 @@ final class RdeFixtures {
.setFullyQualifiedDomainName("example." + tld) .setFullyQualifiedDomainName("example." + tld)
.setRepoId(generateNewDomainRoid(tld)) .setRepoId(generateNewDomainRoid(tld))
.setRegistrant( .setRegistrant(
Key.create( makeContactResource(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
makeContactResource( .createVKey())
clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")))
.build(); .build();
HistoryEntry historyEntry = HistoryEntry historyEntry =
persistResource(new HistoryEntry.Builder().setParent(domain).build()); persistResource(new HistoryEntry.Builder().setParent(domain).build());
@ -90,20 +89,20 @@ final class RdeFixtures {
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create( DesignatedContact.create(
DesignatedContact.Type.ADMIN, DesignatedContact.Type.ADMIN,
Key.create(
makeContactResource( makeContactResource(
clock, clock,
"5372808-IRL", "5372808-IRL",
"be that word our sign in parting", "be that word our sign in parting",
"BOFH@cat.みんな"))), "BOFH@cat.みんな")
.createVKey()),
DesignatedContact.create( DesignatedContact.create(
DesignatedContact.Type.TECH, DesignatedContact.Type.TECH,
Key.create(
makeContactResource( makeContactResource(
clock, clock,
"5372808-TRL", "5372808-TRL",
"bird or fiend!? i shrieked upstarting", "bird or fiend!? i shrieked upstarting",
"bog@cat.みんな"))))) "bog@cat.みんな")
.createVKey())))
.setCreationClientId("TheRegistrar") .setCreationClientId("TheRegistrar")
.setPersistedCurrentSponsorClientId("TheRegistrar") .setPersistedCurrentSponsorClientId("TheRegistrar")
.setCreationTimeForTest(clock.nowUtc()) .setCreationTimeForTest(clock.nowUtc())

View file

@ -27,7 +27,6 @@ import static google.registry.testing.DatastoreHelper.persistResource;
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.OteStatsTestHelper; import google.registry.model.OteStatsTestHelper;
import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
@ -127,9 +126,9 @@ public enum Fixture {
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(ADMIN, Key.create(robert)), DesignatedContact.create(ADMIN, robert.createVKey()),
DesignatedContact.create(BILLING, Key.create(google)), DesignatedContact.create(BILLING, google.createVKey()),
DesignatedContact.create(TECH, Key.create(justine)))) DesignatedContact.create(TECH, justine.createVKey())))
.setNameservers( .setNameservers(
ImmutableSet.of( ImmutableSet.of(
persistActiveHost("ns1.love.xn--q9jyb4c").createKey(), persistActiveHost("ns1.love.xn--q9jyb4c").createKey(),
@ -141,9 +140,9 @@ public enum Fixture {
.asBuilder() .asBuilder()
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(ADMIN, Key.create(robert)), DesignatedContact.create(ADMIN, robert.createVKey()),
DesignatedContact.create(BILLING, Key.create(google)), DesignatedContact.create(BILLING, google.createVKey()),
DesignatedContact.create(TECH, Key.create(justine)))) DesignatedContact.create(TECH, justine.createVKey())))
.setNameservers( .setNameservers(
ImmutableSet.of( ImmutableSet.of(
persistActiveHost("ns1.linode.com").createKey(), persistActiveHost("ns1.linode.com").createKey(),

View file

@ -93,6 +93,7 @@ import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.tmch.LordnTaskUtils; import google.registry.tmch.LordnTaskUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -144,7 +145,7 @@ public class DatastoreHelper {
public static DomainBase newDomainBase( public static DomainBase newDomainBase(
String domainName, String repoId, ContactResource contact) { String domainName, String repoId, ContactResource contact) {
Key<ContactResource> contactKey = Key.create(contact); VKey<ContactResource> contactKey = contact.createVKey();
return new DomainBase.Builder() return new DomainBase.Builder()
.setRepoId(repoId) .setRepoId(repoId)
.setFullyQualifiedDomainName(domainName) .setFullyQualifiedDomainName(domainName)
@ -487,11 +488,11 @@ public class DatastoreHelper {
.setCreationClientId("TheRegistrar") .setCreationClientId("TheRegistrar")
.setCreationTimeForTest(creationTime) .setCreationTimeForTest(creationTime)
.setRegistrationExpirationTime(expirationTime) .setRegistrationExpirationTime(expirationTime)
.setRegistrant(Key.create(contact)) .setRegistrant(contact.createVKey())
.setContacts( .setContacts(
ImmutableSet.of( ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, Key.create(contact)), DesignatedContact.create(Type.ADMIN, contact.createVKey()),
DesignatedContact.create(Type.TECH, Key.create(contact)))) DesignatedContact.create(Type.TECH, contact.createVKey())))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR"))) .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")))
.addGracePeriod( .addGracePeriod(
GracePeriod.create(GracePeriodStatus.ADD, now.plusDays(10), "foo", null)) GracePeriod.create(GracePeriodStatus.ADD, now.plusDays(10), "foo", null))

View file

@ -23,7 +23,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
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.InetAddresses; import com.google.common.net.InetAddresses;
import com.googlecode.objectify.Key;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactPhoneNumber; import google.registry.model.contact.ContactPhoneNumber;
@ -360,17 +359,17 @@ public final class FullFieldsTestEntityHelper {
StatusValue.SERVER_UPDATE_PROHIBITED)) StatusValue.SERVER_UPDATE_PROHIBITED))
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, "deadface"))); .setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, "deadface")));
if (registrant != null) { if (registrant != null) {
builder.setRegistrant(Key.create(registrant)); builder.setRegistrant(registrant.createVKey());
} }
if ((admin != null) || (tech != null)) { if ((admin != null) || (tech != null)) {
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
if (admin != null) { if (admin != null) {
contactsBuilder.add(DesignatedContact.create( contactsBuilder.add(
DesignatedContact.Type.ADMIN, Key.create(admin))); DesignatedContact.create(DesignatedContact.Type.ADMIN, admin.createVKey()));
} }
if (tech != null) { if (tech != null) {
contactsBuilder.add(DesignatedContact.create( contactsBuilder.add(
DesignatedContact.Type.TECH, Key.create(tech))); DesignatedContact.create(DesignatedContact.Type.TECH, tech.createVKey()));
} }
builder.setContacts(contactsBuilder.build()); builder.setContacts(contactsBuilder.build());
} }

View file

@ -24,7 +24,6 @@ import static google.registry.testing.DatastoreHelper.persistDomainAndEnqueueLor
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued; import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.ofy.Ofy; import google.registry.model.ofy.Ofy;
@ -63,7 +62,7 @@ public class LordnTaskUtilsTest {
private DomainBase.Builder newDomainBuilder() { private DomainBase.Builder newDomainBuilder() {
return new DomainBase.Builder() return new DomainBase.Builder()
.setFullyQualifiedDomainName("fleece.example") .setFullyQualifiedDomainName("fleece.example")
.setRegistrant(Key.create(persistActiveContact("jd1234"))) .setRegistrant(persistActiveContact("jd1234").createVKey())
.setSmdId("smdzzzz") .setSmdId("smdzzzz")
.setCreationClientId("TheRegistrar"); .setCreationClientId("TheRegistrar");
} }

View file

@ -26,7 +26,6 @@ import static org.junit.Assert.assertThrows;
import com.beust.jcommander.ParameterException; import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
@ -187,10 +186,10 @@ public class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomain
ContactResource adminContact2 = persistResource(newContactResource("crr-admin2")); ContactResource adminContact2 = persistResource(newContactResource("crr-admin2"));
ContactResource techContact1 = persistResource(newContactResource("crr-tech1")); ContactResource techContact1 = persistResource(newContactResource("crr-tech1"));
ContactResource techContact2 = persistResource(newContactResource("crr-tech2")); ContactResource techContact2 = persistResource(newContactResource("crr-tech2"));
Key<ContactResource> adminResourceKey1 = Key.create(adminContact1); VKey<ContactResource> adminResourceKey1 = adminContact1.createVKey();
Key<ContactResource> adminResourceKey2 = Key.create(adminContact2); VKey<ContactResource> adminResourceKey2 = adminContact2.createVKey();
Key<ContactResource> techResourceKey1 = Key.create(techContact1); VKey<ContactResource> techResourceKey1 = techContact1.createVKey();
Key<ContactResource> techResourceKey2 = Key.create(techContact2); VKey<ContactResource> techResourceKey2 = techContact2.createVKey();
persistResource( persistResource(
newDomainBase("example.tld") newDomainBase("example.tld")

View file

@ -23,7 +23,6 @@ import static google.registry.whois.WhoisTestData.loadFile;
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.contact.ContactAddress; import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactPhoneNumber; import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
@ -223,9 +222,9 @@ public class DomainWhoisResponseTest {
VKey<HostResource> hostResource1Key = hostResource1.createKey(); VKey<HostResource> hostResource1Key = hostResource1.createKey();
VKey<HostResource> hostResource2Key = hostResource2.createKey(); VKey<HostResource> hostResource2Key = hostResource2.createKey();
Key<ContactResource> registrantResourceKey = Key.create(registrant); VKey<ContactResource> registrantResourceKey = registrant.createVKey();
Key<ContactResource> adminResourceKey = Key.create(adminContact); VKey<ContactResource> adminResourceKey = adminContact.createVKey();
Key<ContactResource> techResourceKey = Key.create(techContact); VKey<ContactResource> techResourceKey = techContact.createVKey();
domainBase = domainBase =
persistResource( persistResource(

View file

@ -0,0 +1,38 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "Domain" ADD COLUMN admin_contact text;
ALTER TABLE "Domain" ADD COLUMN billing_contact text;
ALTER TABLE "Domain" ADD COLUMN registrant_contact text;
ALTER TABLE "Domain" ADD COLUMN tech_contact text;
alter table if exists "Domain"
add constraint fk_domain_admin_contact
foreign key (admin_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_billing_contact
foreign key (billing_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_registrant_contact
foreign key (registrant_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_tech_contact
foreign key (tech_contact)
references "Contact";

View file

@ -100,8 +100,10 @@
last_epp_update_client_id text, last_epp_update_client_id text,
last_epp_update_time timestamptz, last_epp_update_time timestamptz,
statuses text[], statuses text[],
admin_contact text,
auth_info_repo_id text, auth_info_repo_id text,
auth_info_value text, auth_info_value text,
billing_contact text,
fully_qualified_domain_name text, fully_qualified_domain_name text,
idn_table_name text, idn_table_name text,
last_transfer_time timestamptz, last_transfer_time timestamptz,
@ -109,9 +111,11 @@
launch_notice_expiration_time timestamptz, launch_notice_expiration_time timestamptz,
launch_notice_tcn_id text, launch_notice_tcn_id text,
launch_notice_validator_id text, launch_notice_validator_id text,
registrant_contact text,
registration_expiration_time timestamptz, registration_expiration_time timestamptz,
smd_id text, smd_id text,
subordinate_hosts text[], subordinate_hosts text[],
tech_contact text,
tld text, tld text,
primary key (repo_id) primary key (repo_id)
); );

View file

@ -165,7 +165,11 @@ CREATE TABLE public."Domain" (
registration_expiration_time timestamp with time zone, registration_expiration_time timestamp with time zone,
smd_id text, smd_id text,
subordinate_hosts text[], subordinate_hosts text[],
tld text tld text,
admin_contact text,
billing_contact text,
registrant_contact text,
tech_contact text
); );
@ -747,6 +751,38 @@ ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT fk93c185fx7chn68uv7nl6uv2s0 FOREIGN KEY (current_sponsor_client_id) REFERENCES public."Registrar"(client_id); ADD CONSTRAINT fk93c185fx7chn68uv7nl6uv2s0 FOREIGN KEY (current_sponsor_client_id) REFERENCES public."Registrar"(client_id);
--
-- Name: Domain fk_domain_admin_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_admin_contact FOREIGN KEY (admin_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_billing_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_billing_contact FOREIGN KEY (billing_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_registrant_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_registrant_contact FOREIGN KEY (registrant_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_tech_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_tech_contact FOREIGN KEY (tech_contact) REFERENCES public."Contact"(repo_id);
-- --
-- Name: DomainHost fk_domainhost_host_valid; Type: FK CONSTRAINT; Schema: public; Owner: - -- Name: DomainHost fk_domainhost_host_valid; Type: FK CONSTRAINT; Schema: public; Owner: -
-- --