diff --git a/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java b/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java index 7b91c32b7..ed947c3b6 100644 --- a/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java +++ b/core/src/main/java/google/registry/batch/DeleteContactsAndHostsAction.java @@ -283,7 +283,9 @@ public class DeleteContactsAndHostsAction implements Runnable { /** Determine whether the target resource is a linked resource on the domain. */ private boolean isLinked(DomainBase domain, Key resourceKey) { if (resourceKey.getKind().equals(KIND_CONTACT)) { - return domain.getReferencedContacts().contains(resourceKey); + return domain + .getReferencedContacts() + .contains(VKey.createOfy(ContactResource.class, (Key) resourceKey)); } else if (resourceKey.getKind().equals(KIND_HOST)) { return domain .getNameservers() diff --git a/core/src/main/java/google/registry/flows/ResourceFlowUtils.java b/core/src/main/java/google/registry/flows/ResourceFlowUtils.java index 460a7c71b..11f7f4f7f 100644 --- a/core/src/main/java/google/registry/flows/ResourceFlowUtils.java +++ b/core/src/main/java/google/registry/flows/ResourceFlowUtils.java @@ -48,6 +48,7 @@ import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.index.ForeignKeyIndex; import google.registry.model.transfer.TransferStatus; +import google.registry.persistence.VKey; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -96,8 +97,9 @@ public final class ResourceFlowUtils { queryForLinkedDomains(fki.getResourceKey(), now) .limit(FAILFAST_CHECK_COUNT) .keys(); + VKey resourceVKey = VKey.createOfy(resourceClass, fki.getResourceKey()); Predicate predicate = - domain -> getPotentialReferences.apply(domain).contains(fki.getResourceKey()); + domain -> getPotentialReferences.apply(domain).contains(resourceVKey); return ofy().load().keys(keys).values().stream().anyMatch(predicate) ? new ResourceToDeleteIsReferencedException() : null; @@ -184,9 +186,8 @@ public final class ResourceFlowUtils { } // The roid should match one of the contacts. Optional> foundContact = - domain - .getReferencedContacts() - .stream() + domain.getReferencedContacts().stream() + .map(VKey::getOfyKey) .filter(key -> key.getName().equals(authRepoId)) .findFirst(); if (!foundContact.isPresent()) { diff --git a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java index 0f6f29d71..57e95f2b9 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java @@ -77,6 +77,7 @@ import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Flag; import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.billing.BillingEvent.Recurring; +import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand.Create; @@ -352,7 +353,7 @@ public class DomainCreateFlow implements TransactionalFlow { .setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null) .setSmdId(signedMarkId) .setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null) - .setRegistrant(command.getRegistrant()) + .setRegistrant(VKey.createOfy(ContactResource.class, command.getRegistrant())) .setAuthInfo(command.getAuthInfo()) .setFullyQualifiedDomainName(targetId) .setNameservers( diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index a991f43fa..feaaf95e7 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -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.RESERVED_FOR_ANCHOR_TENANT; 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.util.CollectionUtils.nullToEmpty; 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.HistoryEntry; import google.registry.model.tmch.ClaimsListShard; +import google.registry.persistence.VKey; import google.registry.tldconfig.idn.IdnLabelValidator; import google.registry.util.Idn; import java.math.BigDecimal; @@ -309,7 +311,10 @@ public class DomainFlowUtils { Set> nameservers) throws EppException { ImmutableList.Builder> 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); keysToLoad.addAll(nameservers); verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values()); @@ -355,7 +360,7 @@ public class DomainFlowUtils { contacts.stream() .collect( toImmutableSetMultimap( - DesignatedContact::getType, DesignatedContact::getContactKey)); + DesignatedContact::getType, contact -> contact.getContactKey().getOfyKey())); // If any contact type has multiple contacts: if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) { @@ -978,7 +983,7 @@ public class DomainFlowUtils { for (DesignatedContact contact : contacts) { builder.add( ForeignKeyedDesignatedContact.create( - contact.getType(), ofy().load().key(contact.getContactKey()).now().getContactId())); + contact.getType(), tm().load(contact.getContactKey()).getContactId())); } return builder.build(); } diff --git a/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java b/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java index c58e11ee6..ba1719400 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainInfoFlow.java @@ -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.loadForeignKeyedDesignatedContacts; import static google.registry.model.EppResourceUtils.loadByForeignKey; -import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import com.google.common.collect.ImmutableList; @@ -102,16 +101,16 @@ public final class DomainInfoFlow implements Flow { flowCustomLogic.afterValidation( AfterValidationParameters.newBuilder().setDomain(domain).build()); // 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()); - ofy().load().values(domain.getReferencedContacts()).values(); + tm().load(domain.getReferencedContacts()); // Registrars can only see a few fields on unauthorized domains. // This is a policy decision that is left up to us by the rfcs. - DomainInfoData.Builder infoBuilder = DomainInfoData.newBuilder() - .setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName()) - .setRepoId(domain.getRepoId()) - .setCurrentSponsorClientId(domain.getCurrentSponsorClientId()) - .setRegistrant(ofy().load().key(domain.getRegistrant()).now().getContactId()); + DomainInfoData.Builder infoBuilder = + DomainInfoData.newBuilder() + .setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName()) + .setRepoId(domain.getRepoId()) + .setCurrentSponsorClientId(domain.getCurrentSponsorClientId()) + .setRegistrant(tm().load(domain.getRegistrant()).getContactId()); // 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. if (clientId.equals(domain.getCurrentSponsorClientId()) || authInfo.isPresent()) { diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 9c0fd648f..127a1b8ff 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -60,6 +60,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; +import google.registry.model.contact.ContactResource; import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainCommand.Update.AddRemove; @@ -256,7 +257,12 @@ public final class DomainUpdateFlow implements TransactionalFlow { .collect(toImmutableSet())) .addContacts(add.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())); return domainBuilder.build(); } @@ -269,7 +275,7 @@ public final class DomainUpdateFlow implements TransactionalFlow { private void validateNewState(DomainBase newDomain) throws EppException { validateNoDuplicateContacts(newDomain.getContacts()); - validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts()); + validateRequiredContactsPresent(newDomain.getRegistrant().getOfyKey(), newDomain.getContacts()); validateDsData(newDomain.getDsData()); validateNameserversCountForTld( newDomain.getTld(), diff --git a/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java b/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java index b1f2a144a..94d89c00b 100644 --- a/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostDeleteFlow.java @@ -14,7 +14,6 @@ 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.ResourceFlowUtils.failfastForAsyncDelete; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; @@ -82,17 +81,6 @@ public final class HostDeleteFlow implements TransactionalFlow { @Inject EppResponse.Builder responseBuilder; @Inject HostDeleteFlow() {} - /** - * Hack to convert DomainBase's nameserver VKey's to Ofy Key's. - * - *

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> getNameserverOfyKeys(DomainBase domain) { - return domain.getNameservers().stream().map(key -> key.getOfyKey()).collect(toImmutableSet()); - } - @Override public final EppResponse run() throws EppException { extensionManager.register(MetadataExtension.class); @@ -100,7 +88,7 @@ public final class HostDeleteFlow implements TransactionalFlow { validateClientIsLoggedIn(clientId); DateTime now = tm().getTransactionTime(); validateHostName(targetId); - failfastForAsyncDelete(targetId, now, HostResource.class, HostDeleteFlow::getNameserverOfyKeys); + failfastForAsyncDelete(targetId, now, HostResource.class, DomainBase::getNameservers); HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now); verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES); if (!isSuperuser) { diff --git a/core/src/main/java/google/registry/model/contact/ContactResource.java b/core/src/main/java/google/registry/model/contact/ContactResource.java index ad526f9f7..cbdccd8bf 100644 --- a/core/src/main/java/google/registry/model/contact/ContactResource.java +++ b/core/src/main/java/google/registry/model/contact/ContactResource.java @@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime; import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.IgnoreSave; 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.contact.PostalInfo.Type; import google.registry.model.transfer.TransferData; +import google.registry.persistence.VKey; +import google.registry.persistence.WithStringVKey; import google.registry.schema.replay.DatastoreAndSqlEntity; import java.util.Objects; import java.util.Optional; @@ -60,6 +63,7 @@ import org.joda.time.DateTime; @javax.persistence.Index(columnList = "searchName") }) @ExternalMessagingName("contact") +@WithStringVKey public class ContactResource extends EppResource implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData { @@ -193,6 +197,10 @@ public class ContactResource extends EppResource }) Disclose disclose; + public VKey createVKey() { + return VKey.createOfy(ContactResource.class, Key.create(this)); + } + public String getContactId() { return contactId; } diff --git a/core/src/main/java/google/registry/model/domain/DesignatedContact.java b/core/src/main/java/google/registry/model/domain/DesignatedContact.java index 33d5e8dce..1868f9c21 100644 --- a/core/src/main/java/google/registry/model/domain/DesignatedContact.java +++ b/core/src/main/java/google/registry/model/domain/DesignatedContact.java @@ -18,9 +18,11 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Embed; +import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Index; import google.registry.model.ImmutableObject; import google.registry.model.contact.ContactResource; +import google.registry.persistence.VKey; import javax.persistence.Embeddable; 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 * rename contacts internally ourselves, and it's easier to use the same model for both cases. * + *

This entity type is not persisted in Cloud SQL. The different roles are represented as + * separate fields in the Domain table. + * * @see RFC 5731 - EPP Domain Name Mapping * - Contact and Client Identifiers */ @@ -58,22 +63,28 @@ public class DesignatedContact extends ImmutableObject { REGISTRANT } - public static DesignatedContact create(Type type, Key contact) { + public static DesignatedContact create(Type type, VKey contact) { DesignatedContact instance = new DesignatedContact(); 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; } Type type; @Index Key contact; + @Ignore VKey contactVKey; public Type getType() { return type; } - public Key getContactKey() { - return contact; + public VKey getContactKey() { + return contactVKey; + } + + public DesignatedContact reconstitute() { + return create(type, VKey.createOfy(ContactResource.class, contact)); } } diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java index 50fbc01f7..621e888e3 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -80,6 +80,7 @@ import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Embedded; import javax.persistence.JoinTable; +import javax.persistence.PostLoad; import javax.persistence.Transient; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -155,6 +156,17 @@ public class DomainBase extends EppResource */ @Transient Set allContacts; + /** + * Contacts as they are stored in cloud SQL. + * + *

This information is duplicated in allContacts, and must be kept in sync with it. + */ + @Ignore VKey adminContact; + + @Ignore VKey billingContact; + @Ignore VKey techContact; + @Ignore VKey registrantContact; + /** Authorization info (aka transfer secret) of the domain. */ @Embedded @AttributeOverrides({ @@ -261,6 +273,34 @@ public class DomainBase extends EppResource nullToEmptyImmutableCopy(nsHosts).stream() .map(hostKey -> VKey.createOfy(HostResource.class, hostKey)) .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 contactsBuilder = + new ImmutableSet.Builder(); + + 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 getSubordinateHosts() { @@ -515,13 +555,20 @@ public class DomainBase extends EppResource } /** A key to the registrant who registered this domain. */ - public Key getRegistrant() { - return nullToEmpty(allContacts) - .stream() - .filter(IS_REGISTRANT) - .findFirst() - .get() - .getContactKey(); + public VKey getRegistrant() { + return registrantContact; + } + + public VKey getAdminContact() { + return adminContact; + } + + public VKey getBillingContact() { + return billingContact; + } + + public VKey getTechContact() { + return techContact; } /** 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. */ - public ImmutableSet> getReferencedContacts() { + public ImmutableSet> getReferencedContacts() { return nullToEmptyImmutableCopy(allContacts) .stream() .map(DesignatedContact::getContactKey) @@ -549,6 +596,37 @@ public class DomainBase extends EppResource return tld; } + /** + * Sets the individual contact fields from {@code contacts}. + * + *

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 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. */ private static final Predicate IS_REGISTRANT = (DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type); @@ -593,7 +671,11 @@ public class DomainBase extends EppResource checkArgumentNotNull( 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); return super.build(); } @@ -611,11 +693,14 @@ public class DomainBase extends EppResource return thisCastToDerived(); } - public Builder setRegistrant(Key registrant) { + public Builder setRegistrant(VKey registrant) { // Replace the registrant contact inside allContacts. getInstance().allContacts = union( getInstance().getContacts(), DesignatedContact.create(Type.REGISTRANT, checkArgumentNotNull(registrant))); + + // Set the registrant field specifically. + getInstance().registrantContact = registrant; return thisCastToDerived(); } @@ -673,12 +758,16 @@ public class DomainBase extends EppResource public Builder setContacts(ImmutableSet contacts) { checkArgument(contacts.stream().noneMatch(IS_REGISTRANT), "Registrant cannot be a contact"); + // Replace the non-registrant contacts inside allContacts. getInstance().allContacts = Streams.concat( nullToEmpty(getInstance().allContacts).stream().filter(IS_REGISTRANT), contacts.stream()) .collect(toImmutableSet()); + + // Set the individual fields. + getInstance().setContactFields(contacts, false); return thisCastToDerived(); } diff --git a/core/src/main/java/google/registry/model/domain/DomainCommand.java b/core/src/main/java/google/registry/model/domain/DomainCommand.java index 49ec1b2f3..9215d866b 100644 --- a/core/src/main/java/google/registry/model/domain/DomainCommand.java +++ b/core/src/main/java/google/registry/model/domain/DomainCommand.java @@ -41,6 +41,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.host.HostResource; import google.registry.model.index.ForeignKeyIndex; +import google.registry.persistence.VKey; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; @@ -188,7 +189,7 @@ public class DomainCommand { now); for (DesignatedContact contact : contacts) { if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) { - clone.registrant = contact.getContactKey(); + clone.registrant = contact.getContactKey().getOfyKey(); clone.contacts = forceEmptyToNull(difference(contacts, contact)); break; } @@ -439,8 +440,10 @@ public class DomainCommand { loadByForeignKeysCached(foreignKeys.build(), ContactResource.class, now); ImmutableSet.Builder linkedContacts = new ImmutableSet.Builder<>(); for (ForeignKeyedDesignatedContact contact : contacts) { - linkedContacts.add(DesignatedContact.create( - contact.type, loadedContacts.get(contact.contactId))); + linkedContacts.add( + DesignatedContact.create( + contact.type, + VKey.createOfy(ContactResource.class, loadedContacts.get(contact.contactId)))); } return linkedContacts.build(); } diff --git a/core/src/main/java/google/registry/model/ofy/ObjectifyService.java b/core/src/main/java/google/registry/model/ofy/ObjectifyService.java index ab00bebca..56bc8fa52 100644 --- a/core/src/main/java/google/registry/model/ofy/ObjectifyService.java +++ b/core/src/main/java/google/registry/model/ofy/ObjectifyService.java @@ -36,6 +36,7 @@ import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFac import google.registry.config.RegistryEnvironment; import google.registry.model.EntityClasses; import google.registry.model.ImmutableObject; +import google.registry.model.contact.ContactResource; import google.registry.model.host.HostResource; import google.registry.model.translators.BloomFilterOfStringTranslatorFactory; import google.registry.model.translators.CidrAddressBlockTranslatorFactory; @@ -130,6 +131,7 @@ public class ObjectifyService { new InetAddressTranslatorFactory(), new MoneyStringTranslatorFactory(), new ReadableInstantUtcTranslatorFactory(), + new VKeyTranslatorFactory(ContactResource.class), new VKeyTranslatorFactory(HostResource.class), new UpdateAutoTimestampTranslatorFactory())) { factory().getTranslators().add(translatorFactory); diff --git a/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java b/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java index 362e13744..4932fb3c1 100644 --- a/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java +++ b/core/src/main/java/google/registry/rdap/RdapJsonFormatter.java @@ -52,6 +52,7 @@ import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarContact; import google.registry.model.reporting.HistoryEntry; +import google.registry.persistence.VKey; import google.registry.rdap.RdapDataStructures.Event; import google.registry.rdap.RdapDataStructures.EventAction; import google.registry.rdap.RdapDataStructures.Link; @@ -346,7 +347,12 @@ public class RdapJsonFormatter { ImmutableSet.copyOf(tm().load(domainBase.getNameservers())); // Load the registrant and other contacts and add them to the data. Map, 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 // have others. We also add the BILLING. // @@ -361,7 +367,7 @@ public class RdapJsonFormatter { .sorted(DESIGNATED_CONTACT_ORDERING) .collect( toImmutableSetMultimap( - DesignatedContact::getContactKey, DesignatedContact::getType)); + contact -> contact.getContactKey().getOfyKey(), DesignatedContact::getType)); for (Key contactKey : contactsToRoles.keySet()) { Set roles = diff --git a/core/src/main/java/google/registry/rde/DomainBaseToXjcConverter.java b/core/src/main/java/google/registry/rde/DomainBaseToXjcConverter.java index 61f3718f9..0942ebc61 100644 --- a/core/src/main/java/google/registry/rde/DomainBaseToXjcConverter.java +++ b/core/src/main/java/google/registry/rde/DomainBaseToXjcConverter.java @@ -15,13 +15,12 @@ package google.registry.rde; 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.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.Key; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DesignatedContact; 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.rde.RdeMode; import google.registry.model.transfer.TransferData; +import google.registry.persistence.VKey; import google.registry.util.Idn; import google.registry.xjc.domain.XjcDomainContactAttrType; import google.registry.xjc.domain.XjcDomainContactType; @@ -167,11 +167,11 @@ final class DomainBaseToXjcConverter { // o An OPTIONAL element that contain the identifier for // the human or organizational social information object associated // as the holder of the domain name object. - Key registrant = model.getRegistrant(); + VKey registrant = model.getRegistrant(); if (registrant == null) { logger.atWarning().log("Domain %s has no registrant contact.", domainName); } else { - ContactResource registrantContact = ofy().load().key(registrant).now(); + ContactResource registrantContact = tm().load(registrant); checkState( registrantContact != null, "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", model.getType(), domainName); - ContactResource contact = ofy().load().key(model.getContactKey()).now(); + ContactResource contact = tm().load(model.getContactKey()); checkState( contact != null, "Contact %s on domain %s does not exist", diff --git a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java index 8f2e55ae9..e134d2a54 100644 --- a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static google.registry.model.EppResourceUtils.loadByForeignKey; 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 org.joda.time.DateTimeZone.UTC; @@ -291,11 +291,9 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { ImmutableSet getContactsOfType( DomainBase domainBase, final DesignatedContact.Type contactType) { - return domainBase - .getContacts() - .stream() + return domainBase.getContacts().stream() .filter(contact -> contact.getType().equals(contactType)) - .map(contact -> ofy().load().key(contact.getContactKey()).now().getContactId()) + .map(contact -> tm().load(contact.getContactKey()).getContactId()) .collect(toImmutableSet()); } } diff --git a/core/src/main/java/google/registry/whois/DomainWhoisResponse.java b/core/src/main/java/google/registry/whois/DomainWhoisResponse.java index 99023fb68..a718c04fd 100644 --- a/core/src/main/java/google/registry/whois/DomainWhoisResponse.java +++ b/core/src/main/java/google/registry/whois/DomainWhoisResponse.java @@ -22,7 +22,6 @@ import static google.registry.xml.UtcDateTimeAdapter.getFormattedString; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.Key; import google.registry.model.EppResource; import google.registry.model.contact.ContactPhoneNumber; 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.RegistrarContact; import google.registry.model.translators.EnumToAttributeAdapter.EppEnum; +import google.registry.persistence.VKey; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -128,7 +128,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl { } /** Returns the contact of the given type. */ - private Optional> getContactReference(Type type) { + private Optional> getContactReference(Type type) { Optional contactOfType = domain.getContacts().stream().filter(d -> d.getType() == type).findFirst(); return contactOfType.map(DesignatedContact::getContactKey); @@ -149,14 +149,14 @@ final class DomainWhoisResponse extends WhoisResponseImpl { /** Emit the contact entry of the given type. */ DomainEmitter emitContact( - String contactType, Optional> contact, boolean preferUnicode) { + String contactType, Optional> contact, boolean preferUnicode) { if (!contact.isPresent()) { return this; } // 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 // someone's attention. - ContactResource contactResource = EppResource.loadCached(contact.get()); + ContactResource contactResource = EppResource.loadCached(contact.get().getOfyKey()); if (contactResource == null) { logger.atSevere().log( "(BUG) Broken reference found from domain %s to contact %s", diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index dd276474d..ff910329a 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -52,6 +52,7 @@ google.registry.model.host.VKeyConverter_HostResource + google.registry.model.contact.VKeyConverter_ContactResource NONE diff --git a/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java b/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java index c244b8ecb..d807cbeb7 100644 --- a/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java +++ b/core/src/test/java/google/registry/batch/DeleteContactsAndHostsActionTest.java @@ -190,7 +190,9 @@ public class DeleteContactsAndHostsActionTest .hasDeletionTime(END_OF_TIME); DomainBase domainReloaded = 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 = getOnlyHistoryEntryOfType(contactUpdated, HistoryEntry.Type.CONTACT_DELETE_FAILURE); assertPollMessageFor( diff --git a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java index 514905866..dcf054498 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java @@ -151,7 +151,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase contacts = contactsBuilder.build(); persistResource( @@ -319,8 +319,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase sh8013Key = Key.create(sh8013); + VKey sh8013Key = sh8013.createVKey(); persistResource( newDomainBase(getUniqueIdFromCommand()) .asBuilder() @@ -866,8 +865,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase { - assertThat(ofy().load().key(contact.getContactKey()).now().getContactId()) - .isEqualTo("mak21"); + assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("mak21"); }); - assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId()) + assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId()) .isEqualTo("mak21"); runFlow(); @@ -1249,10 +1249,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase { - assertThat(ofy().load().key(contact.getContactKey()).now().getContactId()) - .isEqualTo("sh8013"); + assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("sh8013"); }); - assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId()) + assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId()) .isEqualTo("sh8013"); } diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java index c41a051f5..60d8503a0 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseSqlTest.java @@ -22,7 +22,6 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.joda.time.DateTimeZone.UTC; import com.google.common.collect.ImmutableSet; -import com.googlecode.objectify.Key; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DesignatedContact.Type; 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.StatusValue; import google.registry.model.host.HostResource; +import google.registry.model.transfer.TransferData; import google.registry.persistence.VKey; import google.registry.persistence.transaction.JpaTestRules; import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension; @@ -56,15 +56,20 @@ public class DomainBaseSqlTest { new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension(); DomainBase domain; - Key contactKey; - Key contact2Key; + VKey contactKey; + VKey contact2Key; VKey host1VKey; HostResource host; + ContactResource contact; + ContactResource contact2; @BeforeEach public void setUp() { - contactKey = Key.create(ContactResource.class, "contact_id1"); - contact2Key = Key.create(ContactResource.class, "contact_id2"); + saveRegistrar("registrar1"); + saveRegistrar("registrar2"); + saveRegistrar("registrar3"); + contactKey = VKey.createSql(ContactResource.class, "contact_id1"); + contact2Key = VKey.createSql(ContactResource.class, "contact_id2"); host1VKey = VKey.createSql(HostResource.class, "host1"); @@ -104,23 +109,26 @@ public class DomainBaseSqlTest { .setCreationClientId("registrar1") .setPersistedCurrentSponsorClientId("registrar2") .build(); + contact = makeContact("contact_id1"); + contact2 = makeContact("contact_id2"); } @Test public void testDomainBasePersistence() { - saveRegistrar("registrar1"); - saveRegistrar("registrar2"); - saveRegistrar("registrar3"); - jpaTm() .transact( () -> { - // Persist the domain. - EntityManager em = jpaTm().getEntityManager(); - em.persist(domain); + // Persist the contacts. Note that these need to be persisted before the domain + // otherwise we get a foreign key constraint error. + jpaTm().saveNew(contact); + jpaTm().saveNew(contact2); - // Persist the host. - em.persist(host); + // Persist the domain. + 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() @@ -130,13 +138,11 @@ public class DomainBaseSqlTest { EntityManager em = jpaTm().getEntityManager(); 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 .asBuilder() .setRegistrant(contactKey) - .setContacts( - ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key))) .setDsData( ImmutableSet.of( DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2}))) @@ -151,16 +157,40 @@ public class DomainBaseSqlTest { } @Test - public void testForeignKeyConstraints() { + public void testHostForeignKeyConstraints() { assertThrowForeignKeyViolation( () -> { jpaTm() .transact( () -> { // Persist the domain without the associated host object. - EntityManager em = jpaTm().getEntityManager(); - em.persist(domain); + jpaTm().saveNew(contact); + 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(); + } } diff --git a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java index ac546ad22..b03b7e0dc 100644 --- a/core/src/test/java/google/registry/model/domain/DomainBaseTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainBaseTest.java @@ -80,20 +80,20 @@ public class DomainBaseTest extends EntityTestCase { .setRepoId("1-COM") .build()) .createKey(); - Key contact1Key = - Key.create( - persistResource( + VKey contact1Key = + persistResource( new ContactResource.Builder() .setContactId("contact_id1") .setRepoId("2-COM") - .build())); - Key contact2Key = - Key.create( - persistResource( + .build()) + .createVKey(); + VKey contact2Key = + persistResource( new ContactResource.Builder() .setContactId("contact_id2") .setRepoId("3-COM") - .build())); + .build()) + .createVKey(); Key historyEntryKey = Key.create(persistResource(new HistoryEntry.Builder().setParent(domainKey).build())); oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1); diff --git a/core/src/test/java/google/registry/rde/DomainBaseToXjcConverterTest.java b/core/src/test/java/google/registry/rde/DomainBaseToXjcConverterTest.java index 8e05ad2d7..941b4c4d3 100644 --- a/core/src/test/java/google/registry/rde/DomainBaseToXjcConverterTest.java +++ b/core/src/test/java/google/registry/rde/DomainBaseToXjcConverterTest.java @@ -240,22 +240,22 @@ public class DomainBaseToXjcConverterTest { ImmutableSet.of( DesignatedContact.create( DesignatedContact.Type.ADMIN, - Key.create( - makeContactResource( + makeContactResource( clock, "10-Q9JYB4C", "5372808-IRL", "be that word our sign in parting", - "BOFH@cat.みんな"))), + "BOFH@cat.みんな") + .createVKey()), DesignatedContact.create( DesignatedContact.Type.TECH, - Key.create( - makeContactResource( + makeContactResource( clock, "11-Q9JYB4C", "5372808-TRL", "bird or fiend!? i shrieked upstarting", - "bog@cat.みんな"))))) + "bog@cat.みんな") + .createVKey()))) .setCreationClientId("LawyerCat") .setCreationTimeForTest(DateTime.parse("1900-01-01T00:00:00Z")) .setPersistedCurrentSponsorClientId("GetTheeBack") @@ -273,9 +273,9 @@ public class DomainBaseToXjcConverterTest { makeHostResource(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef") .createKey())) .setRegistrant( - Key.create( - makeContactResource( - clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな"))) + makeContactResource( + clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな") + .createVKey()) .setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z")) .setGracePeriods( ImmutableSet.of( diff --git a/core/src/test/java/google/registry/rde/RdeFixtures.java b/core/src/test/java/google/registry/rde/RdeFixtures.java index 0ea2f5328..eb04237f3 100644 --- a/core/src/test/java/google/registry/rde/RdeFixtures.java +++ b/core/src/test/java/google/registry/rde/RdeFixtures.java @@ -63,9 +63,8 @@ final class RdeFixtures { .setFullyQualifiedDomainName("example." + tld) .setRepoId(generateNewDomainRoid(tld)) .setRegistrant( - Key.create( - makeContactResource( - clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな"))) + makeContactResource(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな") + .createVKey()) .build(); HistoryEntry historyEntry = persistResource(new HistoryEntry.Builder().setParent(domain).build()); @@ -90,20 +89,20 @@ final class RdeFixtures { ImmutableSet.of( DesignatedContact.create( DesignatedContact.Type.ADMIN, - Key.create( - makeContactResource( + makeContactResource( clock, "5372808-IRL", "be that word our sign in parting", - "BOFH@cat.みんな"))), + "BOFH@cat.みんな") + .createVKey()), DesignatedContact.create( DesignatedContact.Type.TECH, - Key.create( - makeContactResource( + makeContactResource( clock, "5372808-TRL", "bird or fiend!? i shrieked upstarting", - "bog@cat.みんな"))))) + "bog@cat.みんな") + .createVKey()))) .setCreationClientId("TheRegistrar") .setPersistedCurrentSponsorClientId("TheRegistrar") .setCreationTimeForTest(clock.nowUtc()) diff --git a/core/src/test/java/google/registry/server/Fixture.java b/core/src/test/java/google/registry/server/Fixture.java index d9004b556..b779e77a4 100644 --- a/core/src/test/java/google/registry/server/Fixture.java +++ b/core/src/test/java/google/registry/server/Fixture.java @@ -27,7 +27,6 @@ import static google.registry.testing.DatastoreHelper.persistResource; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.googlecode.objectify.Key; import google.registry.model.OteStatsTestHelper; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactResource; @@ -127,9 +126,9 @@ public enum Fixture { .asBuilder() .setContacts( ImmutableSet.of( - DesignatedContact.create(ADMIN, Key.create(robert)), - DesignatedContact.create(BILLING, Key.create(google)), - DesignatedContact.create(TECH, Key.create(justine)))) + DesignatedContact.create(ADMIN, robert.createVKey()), + DesignatedContact.create(BILLING, google.createVKey()), + DesignatedContact.create(TECH, justine.createVKey()))) .setNameservers( ImmutableSet.of( persistActiveHost("ns1.love.xn--q9jyb4c").createKey(), @@ -141,9 +140,9 @@ public enum Fixture { .asBuilder() .setContacts( ImmutableSet.of( - DesignatedContact.create(ADMIN, Key.create(robert)), - DesignatedContact.create(BILLING, Key.create(google)), - DesignatedContact.create(TECH, Key.create(justine)))) + DesignatedContact.create(ADMIN, robert.createVKey()), + DesignatedContact.create(BILLING, google.createVKey()), + DesignatedContact.create(TECH, justine.createVKey()))) .setNameservers( ImmutableSet.of( persistActiveHost("ns1.linode.com").createKey(), diff --git a/core/src/test/java/google/registry/testing/DatastoreHelper.java b/core/src/test/java/google/registry/testing/DatastoreHelper.java index 8f57e0488..9b1bc0938 100644 --- a/core/src/test/java/google/registry/testing/DatastoreHelper.java +++ b/core/src/test/java/google/registry/testing/DatastoreHelper.java @@ -93,6 +93,7 @@ import google.registry.model.registry.label.ReservedList; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; +import google.registry.persistence.VKey; import google.registry.tmch.LordnTaskUtils; import java.util.Arrays; import java.util.List; @@ -144,7 +145,7 @@ public class DatastoreHelper { public static DomainBase newDomainBase( String domainName, String repoId, ContactResource contact) { - Key contactKey = Key.create(contact); + VKey contactKey = contact.createVKey(); return new DomainBase.Builder() .setRepoId(repoId) .setFullyQualifiedDomainName(domainName) @@ -487,11 +488,11 @@ public class DatastoreHelper { .setCreationClientId("TheRegistrar") .setCreationTimeForTest(creationTime) .setRegistrationExpirationTime(expirationTime) - .setRegistrant(Key.create(contact)) + .setRegistrant(contact.createVKey()) .setContacts( ImmutableSet.of( - DesignatedContact.create(Type.ADMIN, Key.create(contact)), - DesignatedContact.create(Type.TECH, Key.create(contact)))) + DesignatedContact.create(Type.ADMIN, contact.createVKey()), + DesignatedContact.create(Type.TECH, contact.createVKey()))) .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR"))) .addGracePeriod( GracePeriod.create(GracePeriodStatus.ADD, now.plusDays(10), "foo", null)) diff --git a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java index 6df7c35c8..d806b010e 100644 --- a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java +++ b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java @@ -23,7 +23,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.net.InetAddresses; -import com.googlecode.objectify.Key; import google.registry.model.EppResource; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactPhoneNumber; @@ -360,17 +359,17 @@ public final class FullFieldsTestEntityHelper { StatusValue.SERVER_UPDATE_PROHIBITED)) .setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, "deadface"))); if (registrant != null) { - builder.setRegistrant(Key.create(registrant)); + builder.setRegistrant(registrant.createVKey()); } if ((admin != null) || (tech != null)) { ImmutableSet.Builder contactsBuilder = new ImmutableSet.Builder<>(); if (admin != null) { - contactsBuilder.add(DesignatedContact.create( - DesignatedContact.Type.ADMIN, Key.create(admin))); + contactsBuilder.add( + DesignatedContact.create(DesignatedContact.Type.ADMIN, admin.createVKey())); } if (tech != null) { - contactsBuilder.add(DesignatedContact.create( - DesignatedContact.Type.TECH, Key.create(tech))); + contactsBuilder.add( + DesignatedContact.create(DesignatedContact.Type.TECH, tech.createVKey())); } builder.setContacts(contactsBuilder.build()); } diff --git a/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java b/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java index c95c5c92c..4253a4463 100644 --- a/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java +++ b/core/src/test/java/google/registry/tmch/LordnTaskUtilsTest.java @@ -24,7 +24,6 @@ import static google.registry.testing.DatastoreHelper.persistDomainAndEnqueueLor import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued; import static org.junit.Assert.assertThrows; -import com.googlecode.objectify.Key; import google.registry.model.domain.DomainBase; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.ofy.Ofy; @@ -63,7 +62,7 @@ public class LordnTaskUtilsTest { private DomainBase.Builder newDomainBuilder() { return new DomainBase.Builder() .setFullyQualifiedDomainName("fleece.example") - .setRegistrant(Key.create(persistActiveContact("jd1234"))) + .setRegistrant(persistActiveContact("jd1234").createVKey()) .setSmdId("smdzzzz") .setCreationClientId("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/tools/UpdateDomainCommandTest.java b/core/src/test/java/google/registry/tools/UpdateDomainCommandTest.java index 26514d2b7..aa57ba938 100644 --- a/core/src/test/java/google/registry/tools/UpdateDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdateDomainCommandTest.java @@ -26,7 +26,6 @@ import static org.junit.Assert.assertThrows; import com.beust.jcommander.ParameterException; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.googlecode.objectify.Key; import google.registry.model.contact.ContactResource; import google.registry.model.domain.DesignatedContact; import google.registry.model.eppcommon.StatusValue; @@ -187,10 +186,10 @@ public class UpdateDomainCommandTest extends EppToolCommandTestCase adminResourceKey1 = Key.create(adminContact1); - Key adminResourceKey2 = Key.create(adminContact2); - Key techResourceKey1 = Key.create(techContact1); - Key techResourceKey2 = Key.create(techContact2); + VKey adminResourceKey1 = adminContact1.createVKey(); + VKey adminResourceKey2 = adminContact2.createVKey(); + VKey techResourceKey1 = techContact1.createVKey(); + VKey techResourceKey2 = techContact2.createVKey(); persistResource( newDomainBase("example.tld") diff --git a/core/src/test/java/google/registry/whois/DomainWhoisResponseTest.java b/core/src/test/java/google/registry/whois/DomainWhoisResponseTest.java index ad0d92b2a..2c7e2d339 100644 --- a/core/src/test/java/google/registry/whois/DomainWhoisResponseTest.java +++ b/core/src/test/java/google/registry/whois/DomainWhoisResponseTest.java @@ -23,7 +23,6 @@ import static google.registry.whois.WhoisTestData.loadFile; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.googlecode.objectify.Key; import google.registry.model.contact.ContactAddress; import google.registry.model.contact.ContactPhoneNumber; import google.registry.model.contact.ContactResource; @@ -223,9 +222,9 @@ public class DomainWhoisResponseTest { VKey hostResource1Key = hostResource1.createKey(); VKey hostResource2Key = hostResource2.createKey(); - Key registrantResourceKey = Key.create(registrant); - Key adminResourceKey = Key.create(adminContact); - Key techResourceKey = Key.create(techContact); + VKey registrantResourceKey = registrant.createVKey(); + VKey adminResourceKey = adminContact.createVKey(); + VKey techResourceKey = techContact.createVKey(); domainBase = persistResource( diff --git a/db/src/main/resources/sql/flyway/V24__domain_base_contacts.sql b/db/src/main/resources/sql/flyway/V24__domain_base_contacts.sql new file mode 100644 index 000000000..18ce69371 --- /dev/null +++ b/db/src/main/resources/sql/flyway/V24__domain_base_contacts.sql @@ -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"; diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index acf9cc65e..0cbb1a2ed 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -100,8 +100,10 @@ last_epp_update_client_id text, last_epp_update_time timestamptz, statuses text[], + admin_contact text, auth_info_repo_id text, auth_info_value text, + billing_contact text, fully_qualified_domain_name text, idn_table_name text, last_transfer_time timestamptz, @@ -109,9 +111,11 @@ launch_notice_expiration_time timestamptz, launch_notice_tcn_id text, launch_notice_validator_id text, + registrant_contact text, registration_expiration_time timestamptz, smd_id text, subordinate_hosts text[], + tech_contact text, tld text, primary key (repo_id) ); diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql index d77411065..eaf58f105 100644 --- a/db/src/main/resources/sql/schema/nomulus.golden.sql +++ b/db/src/main/resources/sql/schema/nomulus.golden.sql @@ -165,7 +165,11 @@ CREATE TABLE public."Domain" ( registration_expiration_time timestamp with time zone, smd_id 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); +-- +-- 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: - --