From 04f429c4d6b5ef048f33f79fd081ede4ddf976d2 Mon Sep 17 00:00:00 2001 From: Michael Muller Date: Thu, 7 May 2020 11:19:15 -0400 Subject: [PATCH] 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. --- .../batch/DeleteContactsAndHostsAction.java | 4 +- .../registry/flows/ResourceFlowUtils.java | 9 +- .../flows/domain/DomainCreateFlow.java | 3 +- .../flows/domain/DomainFlowUtils.java | 11 +- .../registry/flows/domain/DomainInfoFlow.java | 15 ++- .../flows/domain/DomainUpdateFlow.java | 10 +- .../registry/flows/host/HostDeleteFlow.java | 14 +-- .../model/contact/ContactResource.java | 8 ++ .../model/domain/DesignatedContact.java | 19 ++- .../registry/model/domain/DomainBase.java | 109 ++++++++++++++++-- .../registry/model/domain/DomainCommand.java | 9 +- .../registry/model/ofy/ObjectifyService.java | 2 + .../registry/rdap/RdapJsonFormatter.java | 10 +- .../rde/DomainBaseToXjcConverter.java | 10 +- .../registry/tools/UpdateDomainCommand.java | 8 +- .../registry/whois/DomainWhoisResponse.java | 8 +- .../main/resources/META-INF/persistence.xml | 1 + .../DeleteContactsAndHostsActionTest.java | 4 +- .../flows/domain/DomainDeleteFlowTest.java | 6 +- .../flows/domain/DomainInfoFlowTest.java | 6 +- .../flows/domain/DomainUpdateFlowTest.java | 53 +++++---- .../model/domain/DomainBaseSqlTest.java | 70 +++++++---- .../registry/model/domain/DomainBaseTest.java | 16 +-- .../rde/DomainBaseToXjcConverterTest.java | 18 +-- .../java/google/registry/rde/RdeFixtures.java | 17 ++- .../java/google/registry/server/Fixture.java | 13 +-- .../registry/testing/DatastoreHelper.java | 9 +- .../testing/FullFieldsTestEntityHelper.java | 11 +- .../registry/tmch/LordnTaskUtilsTest.java | 3 +- .../tools/UpdateDomainCommandTest.java | 9 +- .../whois/DomainWhoisResponseTest.java | 7 +- .../sql/flyway/V24__domain_base_contacts.sql | 38 ++++++ .../sql/schema/db-schema.sql.generated | 4 + .../resources/sql/schema/nomulus.golden.sql | 38 +++++- 34 files changed, 399 insertions(+), 173 deletions(-) create mode 100644 db/src/main/resources/sql/flyway/V24__domain_base_contacts.sql 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: - --