diff --git a/java/google/registry/flows/contact/ContactInfoFlow.java b/java/google/registry/flows/contact/ContactInfoFlow.java index f7abe94d4..20ccc0777 100644 --- a/java/google/registry/flows/contact/ContactInfoFlow.java +++ b/java/google/registry/flows/contact/ContactInfoFlow.java @@ -25,6 +25,7 @@ import google.registry.flows.ExtensionManager; import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; +import google.registry.model.contact.ContactInfoData; import google.registry.model.contact.ContactResource; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppoutput.EppResponse; @@ -59,9 +60,27 @@ public final class ContactInfoFlow implements Flow { validateClientIsLoggedIn(clientId); ContactResource contact = loadAndVerifyExistence(ContactResource.class, targetId, now); verifyOptionalAuthInfo(authInfo, contact); - if (!clientId.equals(contact.getCurrentSponsorClientId()) && !authInfo.isPresent()) { - contact = contact.asBuilder().setAuthInfo(null).build(); - } - return responseBuilder.setResData(cloneResourceWithLinkedStatus(contact, now)).build(); + contact = (ContactResource) cloneResourceWithLinkedStatus(contact, now); + boolean includeAuthInfo = + clientId.equals(contact.getCurrentSponsorClientId()) || authInfo.isPresent(); + return responseBuilder + .setResData(ContactInfoData.newBuilder() + .setContactId(contact.getContactId()) + .setRepoId(contact.getRepoId()) + .setStatusValues(contact.getStatusValues()) + .setPostalInfos(contact.getPostalInfosAsList()) + .setVoiceNumber(contact.getVoiceNumber()) + .setFaxNumber(contact.getFaxNumber()) + .setEmailAddress(contact.getEmailAddress()) + .setCurrentSponsorClientId(contact.getCurrentSponsorClientId()) + .setCreationClientId(contact.getCreationClientId()) + .setCreationTime(contact.getCreationTime()) + .setLastEppUpdateClientId(contact.getLastEppUpdateClientId()) + .setLastEppUpdateTime(contact.getLastEppUpdateTime()) + .setLastTransferTime(contact.getLastTransferTime()) + .setAuthInfo(includeAuthInfo ? contact.getAuthInfo() : null) + .setDisclose(contact.getDisclose()) + .build()) + .build(); } } diff --git a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java index 92898244c..7a02d850e 100644 --- a/java/google/registry/flows/domain/DomainApplicationInfoFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationInfoFlow.java @@ -20,8 +20,11 @@ import static google.registry.flows.ResourceFlowUtils.verifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; +import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts; +import static google.registry.flows.domain.DomainFlowUtils.prefetchReferencedResources; import static google.registry.flows.domain.DomainFlowUtils.verifyApplicationDomainMatchesTargetId; import static google.registry.model.EppResourceUtils.loadDomainApplication; +import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -35,6 +38,7 @@ import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand.Info; +import google.registry.model.domain.DomainInfoData; import google.registry.model.domain.launch.LaunchInfoExtension; import google.registry.model.domain.launch.LaunchInfoResponseExtension; import google.registry.model.eppcommon.AuthInfo; @@ -95,8 +99,23 @@ public final class DomainApplicationInfoFlow implements Flow { } // We don't support authInfo for applications, so if it's another registrar always fail. verifyResourceOwnership(clientId, application); + application = getResourceInfo(application); + prefetchReferencedResources(application); return responseBuilder - .setResData(getResourceInfo(application)) + .setResData(DomainInfoData.newBuilder() + .setFullyQualifiedDomainName(application.getFullyQualifiedDomainName()) + .setRepoId(application.getRepoId()) + .setStatusValues(application.getStatusValues()) + .setRegistrant(ofy().load().key(application.getRegistrant()).now().getContactId()) + .setContacts(loadForeignKeyedDesignatedContacts(application.getContacts())) + .setNameservers(application.loadNameserverFullyQualifiedHostNames()) + .setCurrentSponsorClientId(application.getCurrentSponsorClientId()) + .setCreationClientId(application.getCreationClientId()) + .setCreationTime(application.getCreationTime()) + .setLastEppUpdateClientId(application.getLastEppUpdateClientId()) + .setLastEppUpdateTime(application.getLastEppUpdateTime()) + .setAuthInfo(application.getAuthInfo()) + .build()) .setExtensions(getDomainResponseExtensions(application, launchInfo, now)) .build(); } diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index c01866eaa..755b956c1 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -70,6 +70,7 @@ import google.registry.model.domain.DomainCommand.CreateOrUpdate; import google.registry.model.domain.DomainCommand.InvalidReferencesException; import google.registry.model.domain.DomainCommand.Update; import google.registry.model.domain.DomainResource; +import google.registry.model.domain.ForeignKeyedDesignatedContact; import google.registry.model.domain.LrpTokenEntity; import google.registry.model.domain.Period; import google.registry.model.domain.fee.Credit; @@ -896,6 +897,23 @@ public class DomainFlowUtils { .build(); } + /** Bulk-load all referenced resources on a domain so they are in the session cache. */ + static void prefetchReferencedResources(DomainBase domain) { + // Calling values() on the result blocks until loading is done. + ofy().load().values(union(domain.getNameservers(), domain.getReferencedContacts())).values(); + } + + static ImmutableSet loadForeignKeyedDesignatedContacts( + ImmutableSet contacts) { + ImmutableSet.Builder builder = new ImmutableSet.Builder<>(); + for (DesignatedContact contact : contacts) { + builder.add(ForeignKeyedDesignatedContact.create( + contact.getType(), + ofy().load().key(contact.getContactKey()).now().getContactId())); + } + return builder.build(); + } + /** Resource linked to this domain does not exist. */ static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException { public LinkedResourcesDoNotExistException(Class type, ImmutableSet resourceIds) { diff --git a/java/google/registry/flows/domain/DomainInfoFlow.java b/java/google/registry/flows/domain/DomainInfoFlow.java index e349474dd..dc9e5f079 100644 --- a/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/java/google/registry/flows/domain/DomainInfoFlow.java @@ -19,6 +19,9 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfPresent; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; +import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts; +import static google.registry.flows.domain.DomainFlowUtils.prefetchReferencedResources; +import static google.registry.model.ofy.ObjectifyService.ofy; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -35,6 +38,7 @@ import google.registry.flows.custom.DomainInfoFlowCustomLogic.BeforeResponsePara import google.registry.flows.custom.DomainInfoFlowCustomLogic.BeforeResponseReturnData; import google.registry.model.domain.DomainCommand.Info; import google.registry.model.domain.DomainCommand.Info.HostsRequest; +import google.registry.model.domain.DomainInfoData; import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource.Builder; import google.registry.model.domain.fee06.FeeInfoCommandExtensionV06; @@ -97,8 +101,26 @@ public final class DomainInfoFlow implements Flow { .setResData(getResourceInfo(domain)) .setResponseExtensions(getDomainResponseExtensions(domain, now)) .build()); + domain = responseData.resData(); + prefetchReferencedResources(domain); return responseBuilder - .setResData(responseData.resData()) + .setResData(DomainInfoData.newBuilder() + .setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName()) + .setRepoId(domain.getRepoId()) + .setStatusValues(domain.getStatusValues()) + .setRegistrant(ofy().load().key(domain.getRegistrant()).now().getContactId()) + .setContacts(loadForeignKeyedDesignatedContacts(domain.getContacts())) + .setNameservers(domain.loadNameserverFullyQualifiedHostNames()) + .setSubordinateHosts(domain.getSubordinateHosts()) + .setCurrentSponsorClientId(domain.getCurrentSponsorClientId()) + .setCreationClientId(domain.getCreationClientId()) + .setCreationTime(domain.getCreationTime()) + .setLastEppUpdateClientId(domain.getLastEppUpdateClientId()) + .setLastEppUpdateTime(domain.getLastEppUpdateTime()) + .setRegistrationExpirationTime(domain.getRegistrationExpirationTime()) + .setLastTransferTime(domain.getLastTransferTime()) + .setAuthInfo(domain.getAuthInfo()) + .build()) .setExtensions(responseData.responseExtensions()) .build(); } diff --git a/java/google/registry/flows/host/HostInfoFlow.java b/java/google/registry/flows/host/HostInfoFlow.java index 94a3c5a1a..b730acd81 100644 --- a/java/google/registry/flows/host/HostInfoFlow.java +++ b/java/google/registry/flows/host/HostInfoFlow.java @@ -25,6 +25,7 @@ import google.registry.flows.Flow; import google.registry.flows.FlowModule.ClientId; import google.registry.flows.FlowModule.TargetId; import google.registry.model.eppoutput.EppResponse; +import google.registry.model.host.HostInfoData; import google.registry.model.host.HostResource; import google.registry.util.Clock; import javax.inject.Inject; @@ -53,10 +54,24 @@ public final class HostInfoFlow implements Flow { @Override public EppResponse run() throws EppException { extensionManager.validate(); // There are no legal extensions for this flow. - validateClientIsLoggedIn(clientId); + validateClientIsLoggedIn(clientId); validateHostName(targetId); DateTime now = clock.nowUtc(); HostResource host = loadAndVerifyExistence(HostResource.class, targetId, now); - return responseBuilder.setResData(cloneResourceWithLinkedStatus(host, now)).build(); + host = (HostResource) cloneResourceWithLinkedStatus(host, now); + return responseBuilder + .setResData(HostInfoData.newBuilder() + .setFullyQualifiedHostName(host.getFullyQualifiedHostName()) + .setRepoId(host.getRepoId()) + .setStatusValues(host.getStatusValues()) + .setInetAddresses(host.getInetAddresses()) + .setCurrentSponsorClientId(host.getCurrentSponsorClientId()) + .setCreationClientId(host.getCreationClientId()) + .setCreationTime(host.getCreationTime()) + .setLastEppUpdateClientId(host.getLastEppUpdateClientId()) + .setLastEppUpdateTime(host.getLastEppUpdateTime()) + .setLastTransferTime(host.getLastTransferTime()) + .build()) + .build(); } } diff --git a/java/google/registry/model/BUILD b/java/google/registry/model/BUILD index cf5b985c5..108e825e9 100644 --- a/java/google/registry/model/BUILD +++ b/java/google/registry/model/BUILD @@ -18,6 +18,7 @@ java_library( "//java/google/registry/xml", "//third_party/java/objectify:objectify-v4_1", "@com_google_appengine_api_1_0_sdk", + "@com_google_auto_value", "@com_google_code_findbugs_jsr305", "@com_google_dagger", "@com_google_guava", diff --git a/java/google/registry/model/EppResource.java b/java/google/registry/model/EppResource.java index 9814a6b8c..54dba26d3 100644 --- a/java/google/registry/model/EppResource.java +++ b/java/google/registry/model/EppResource.java @@ -29,17 +29,13 @@ import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Index; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.EppResponse.ResponseData; import google.registry.model.ofy.CommitLogManifest; import google.registry.model.transfer.TransferData; import java.util.Set; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlTransient; import org.joda.time.DateTime; /** An EPP entity object (i.e. a domain, application, contact, or host). */ -@XmlTransient -public abstract class EppResource extends BackupGroupRoot implements Buildable, ResponseData { +public abstract class EppResource extends BackupGroupRoot implements Buildable { /** * Unique identifier in the registry for this resource. @@ -48,16 +44,13 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, * @see RFC 5730 */ @Id - @XmlElement(name = "roid") String repoId; /** The ID of the registrar that is currently sponsoring this resource. */ @Index - @XmlElement(name = "clID") String currentSponsorClientId; /** The ID of the registrar that created this resource. */ - @XmlElement(name = "crID") String creationClientId; /** @@ -67,14 +60,12 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, * edits; it only includes EPP-visible modifications such as {@literal }. Can be null if * the resource has never been modified. */ - @XmlElement(name = "upID") String lastEppUpdateClientId; /** The time when this resource was created. */ // Map the method to XML, not the field, because if we map the field (with an adaptor class) it // will never be omitted from the xml even if the timestamp inside creationTime is null and we // return null from the adaptor. (Instead it gets written as an empty tag.) - @XmlTransient CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); /** @@ -91,7 +82,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, * now. */ @Index - @XmlTransient DateTime deletionTime; @@ -102,7 +92,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, * edits; it only includes EPP-visible modifications such as {@literal }. Can be null if * the resource has never been modified. */ - @XmlElement(name = "upDate") DateTime lastEppUpdateTime; /** Status values associated with this resource. */ @@ -116,14 +105,12 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable, * * @see google.registry.model.translators.CommitLogRevisionsTranslatorFactory */ - @XmlTransient ImmutableSortedMap> revisions = ImmutableSortedMap.of(); public final String getRepoId() { return repoId; } - @XmlElement(name = "crDate") public final DateTime getCreationTime() { return creationTime.getTimestamp(); } diff --git a/java/google/registry/model/contact/ContactInfoData.java b/java/google/registry/model/contact/ContactInfoData.java new file mode 100644 index 000000000..b81f17343 --- /dev/null +++ b/java/google/registry/model/contact/ContactInfoData.java @@ -0,0 +1,132 @@ +// Copyright 2016 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.model.contact; + +import com.google.auto.value.AutoValue; +import com.google.auto.value.AutoValue.CopyAnnotations; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppoutput.EppResponse.ResponseData; +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.joda.time.DateTime; + + +/** The {@link ResponseData} returned for an EPP info flow on a contact. */ +@XmlRootElement(name = "infData") +@XmlType(propOrder = { + "contactId", + "repoId", + "statusValues", + "postalInfos", + "voiceNumber", + "faxNumber", + "emailAddress", + "currentSponsorClientId", + "creationClientId", + "creationTime", + "lastEppUpdateClientId", + "lastEppUpdateTime", + "lastTransferTime", + "authInfo", + "disclose" }) +@AutoValue +@CopyAnnotations +public abstract class ContactInfoData implements ResponseData { + + @XmlElement(name = "id") + abstract String getContactId(); + + @XmlElement(name = "roid") + abstract String getRepoId(); + + @XmlElement(name = "status") + abstract ImmutableSet getStatusValues(); + + @XmlElement(name = "postalInfo") + abstract ImmutableList getPostalInfos(); + + @XmlElement(name = "voice") + @Nullable + abstract ContactPhoneNumber getVoiceNumber(); + + @XmlElement(name = "fax") + @Nullable + abstract ContactPhoneNumber getFaxNumber(); + + @XmlElement(name = "email") + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + @Nullable + abstract String getEmailAddress(); + + @XmlElement(name = "clID") + abstract String getCurrentSponsorClientId(); + + @XmlElement(name = "crID") + abstract String getCreationClientId(); + + @XmlElement(name = "crDate") + abstract DateTime getCreationTime(); + + @XmlElement(name = "upID") + @Nullable + abstract String getLastEppUpdateClientId(); + + @XmlElement(name = "upDate") + @Nullable + abstract DateTime getLastEppUpdateTime(); + + @XmlElement(name = "trDate") + @Nullable + abstract DateTime getLastTransferTime(); + + @XmlElement(name = "authInfo") + @Nullable + abstract ContactAuthInfo getAuthInfo(); + + @XmlElement(name = "disclose") + @Nullable + abstract Disclose getDisclose(); + + /** Builder for {@link ContactInfoData}. */ + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setContactId(String contactId); + public abstract Builder setRepoId(String repoId); + public abstract Builder setStatusValues(ImmutableSet statusValues); + public abstract Builder setPostalInfos(ImmutableList postalInfos); + public abstract Builder setVoiceNumber(@Nullable ContactPhoneNumber voiceNumber); + public abstract Builder setFaxNumber(@Nullable ContactPhoneNumber faxNumber); + public abstract Builder setEmailAddress(@Nullable String emailAddress); + public abstract Builder setCurrentSponsorClientId(String currentSponsorClientId); + public abstract Builder setCreationClientId(String creationClientId); + public abstract Builder setCreationTime(DateTime creationTime); + public abstract Builder setLastEppUpdateClientId(@Nullable String lastEppUpdateClientId); + public abstract Builder setLastEppUpdateTime(@Nullable DateTime lastEppUpdateTime); + public abstract Builder setLastTransferTime(@Nullable DateTime lastTransferTime); + public abstract Builder setAuthInfo(@Nullable ContactAuthInfo authInfo); + public abstract Builder setDisclose(@Nullable Disclose disclose); + public abstract ContactInfoData build(); + } + + public static Builder newBuilder() { + return new AutoValue_ContactInfoData.Builder(); + } +} diff --git a/java/google/registry/model/contact/ContactResource.java b/java/google/registry/model/contact/ContactResource.java index 735ffe87e..32f5d3fff 100644 --- a/java/google/registry/model/contact/ContactResource.java +++ b/java/google/registry/model/contact/ContactResource.java @@ -21,6 +21,7 @@ import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; @@ -34,13 +35,7 @@ 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 java.util.List; import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.joda.time.DateTime; /** @@ -48,23 +43,6 @@ import org.joda.time.DateTime; * * @see RFC 5733 */ -@XmlRootElement(name = "infData") -@XmlType(propOrder = { - "contactId", - "repoId", - "status", - "postalInfosAsList", - "voice", - "fax", - "email", - "currentSponsorClientId", - "creationClientId", - "creationTime", - "lastEppUpdateClientId", - "lastEppUpdateTime", - "lastTransferTime", - "authInfo", - "disclose" }) @Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION) @ReportedOn @Entity @@ -79,7 +57,6 @@ public class ContactResource extends EppResource * from (creationTime, deletionTime) there can only be one contact in the datastore with this id. * However, there can be many contacts with the same id and non-overlapping lifetimes. */ - @XmlTransient String contactId; /** @@ -87,7 +64,6 @@ public class ContactResource extends EppResource * US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) - @XmlTransient PostalInfo localizedPostalInfo; /** @@ -95,7 +71,6 @@ public class ContactResource extends EppResource * {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) - @XmlTransient PostalInfo internationalizedPostalInfo; /** @@ -104,7 +79,6 @@ public class ContactResource extends EppResource * info; cleared by {@link Builder#wipeOut}. */ @Index - @XmlTransient String searchName; /** Contact’s voice number. Personal info; cleared by {@link Builder#wipeOut}. */ @@ -117,14 +91,12 @@ public class ContactResource extends EppResource /** Contact’s email address. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) - @XmlJavaTypeAdapter(CollapsedStringAdapter.class) String email; /** Authorization info (aka transfer secret) of the contact. */ ContactAuthInfo authInfo; /** Data about any pending or past transfers on this contact. */ - @XmlTransient TransferData transferData; /** @@ -132,17 +104,14 @@ public class ContactResource extends EppResource * *

Can be null if the resource has never been transferred. */ - @XmlElement(name = "trDate") DateTime lastTransferTime; // If any new fields are added which contain personal information, make sure they are cleared by // the wipeOut() function, so that data is not kept around for deleted contacts. /** Disclosure policy. */ - @IgnoreSave(IfNull.class) Disclose disclose; - @XmlElement(name = "id") public String getContactId() { return contactId; } @@ -199,7 +168,7 @@ public class ContactResource extends EppResource * transforms the persisted format to the XML format for marshalling. */ @XmlElement(name = "postalInfo") - public List getPostalInfosAsList() { + public ImmutableList getPostalInfosAsList() { return FluentIterable .from(Lists.newArrayList(localizedPostalInfo, internationalizedPostalInfo)) .filter(Predicates.notNull()) diff --git a/java/google/registry/model/domain/DomainApplication.java b/java/google/registry/model/domain/DomainApplication.java index 0f1bcfacd..29cd8334c 100644 --- a/java/google/registry/model/domain/DomainApplication.java +++ b/java/google/registry/model/domain/DomainApplication.java @@ -33,27 +33,10 @@ import google.registry.model.smd.EncodedSignedMark; import google.registry.xml.XmlTransformer; import java.io.ByteArrayInputStream; import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; import org.joda.money.Money; import org.joda.time.DateTime; /** An application to create a domain. */ -@XmlRootElement(name = "infData") -@XmlType(propOrder = { - "fullyQualifiedDomainName", - "repoId", - "status", - "marshalledRegistrant", - "marshalledContacts", - "marshalledNameservers", - "currentSponsorClientId", - "creationClientId", - "creationTime", - "lastEppUpdateClientId", - "lastEppUpdateTime", - "authInfo"}) @Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION) @EntitySubclass(index = true) @ExternalMessagingName("application") @@ -66,30 +49,24 @@ public class DomainApplication extends DomainBase { * *

This field may be null for applications that were created before the field was added. */ - @XmlTransient Trid creationTrid; /** * The phase which this application is registered for. We store this only so we can return it back * to the user on info commands. */ - @XmlTransient LaunchPhase phase; /** The requested registration period. */ - @XmlTransient Period period; /** The current status of this application. */ - @XmlTransient ApplicationStatus applicationStatus; /** The encoded signed marks which were asserted when this application was created. */ - @XmlTransient List encodedSignedMarks; /** The amount paid at auction for the right to register the domain. Used only for reporting. */ - @XmlTransient Money auctionPrice; // TODO(b/32447342): remove this once the period has been populated on all DomainApplications diff --git a/java/google/registry/model/domain/DomainBase.java b/java/google/registry/model/domain/DomainBase.java index 41a54252d..7f7a9414b 100644 --- a/java/google/registry/model/domain/DomainBase.java +++ b/java/google/registry/model/domain/DomainBase.java @@ -22,7 +22,6 @@ import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.union; import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.util.CollectionUtils.forceEmptyToNull; import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy; @@ -49,12 +48,8 @@ import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.host.HostResource; import java.util.Set; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlTransient; /** Shared base class for {@link DomainResource} and {@link DomainApplication}. */ -@XmlTransient @ReportedOn @Entity public abstract class DomainBase extends EppResource { @@ -69,17 +64,14 @@ public abstract class DomainBase extends EppResource { * @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase() */ @Index - @XmlElement(name = "name") String fullyQualifiedDomainName; /** The top level domain this is under, dernormalized from {@link #fullyQualifiedDomainName}. */ @Index - @XmlTransient String tld; /** References to hosts that are the nameservers for the domain. */ @Index - @XmlTransient Set> nsHosts; /** @@ -87,7 +79,6 @@ public abstract class DomainBase extends EppResource { * *

These are stored in one field so that we can query across all contacts at once. */ - @XmlTransient Set allContacts; /** Authorization info (aka transfer secret) of the domain. */ @@ -99,7 +90,6 @@ public abstract class DomainBase extends EppResource { *

This is {@literal @}XmlTransient because it needs to be returned under the "extension" tag * of an info response rather than inside the "infData" tag. */ - @XmlTransient Set dsData; /** @@ -107,7 +97,6 @@ public abstract class DomainBase extends EppResource { * {@literal @}XmlTransient because it's not returned in an info response. */ @IgnoreSave(IfNull.class) - @XmlTransient LaunchNotice launchNotice; /** @@ -116,52 +105,8 @@ public abstract class DomainBase extends EppResource { * @see google.registry.tldconfig.idn.IdnLabelValidator#findValidIdnTableForTld */ @IgnoreSave(IfNull.class) - @XmlTransient String idnTableName; - /** - * Synchronously load all referenced contacts and hosts into the Objectify session cache. - * - *

This saves an extra datastore roundtrip on marshalling, since contacts, hosts, and the - * registrant will all be in the session cache when their respective methods are called. - */ - private void preMarshal() { - // Calling values() blocks until loading is done. - ofy().load().values(union(getNameservers(), getReferencedContacts())).values(); - } - - /** JAXB java beans property to marshal nameserver hostnames. */ - @XmlElementWrapper(name = "ns") - @XmlElement(name = "hostObj") - private ImmutableSet getMarshalledNameservers() { - preMarshal(); - // If there are no nameservers we must return null, or an empty "ns" element will be marshalled. - return forceEmptyToNull(loadNameserverFullyQualifiedHostNames()); - } - - /** JAXB java beans property to marshal non-registrant contact ids. */ - @XmlElement(name = "contact") - private ImmutableSet getMarshalledContacts() { - preMarshal(); - return FluentIterable.from(getContacts()) - .transform( - new Function() { - @Override - public ForeignKeyedDesignatedContact apply(DesignatedContact designated) { - return ForeignKeyedDesignatedContact.create( - designated.getType(), - ofy().load().key(designated.getContactKey()).now().getContactId()); - }}) - .toSet(); - } - - /** JAXB java beans property to marshal nameserver hostnames. */ - @XmlElement(name = "registrant") - private String getMarshalledRegistrant() { - preMarshal(); - return ofy().load().key(getRegistrant()).now().getContactId(); - } - public String getFullyQualifiedDomainName() { return fullyQualifiedDomainName; } diff --git a/java/google/registry/model/domain/DomainInfoData.java b/java/google/registry/model/domain/DomainInfoData.java new file mode 100644 index 000000000..42c7bf9a5 --- /dev/null +++ b/java/google/registry/model/domain/DomainInfoData.java @@ -0,0 +1,146 @@ +// Copyright 2016 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.model.domain; + +import static google.registry.util.CollectionUtils.forceEmptyToNull; + +import com.google.auto.value.AutoValue; +import com.google.auto.value.AutoValue.CopyAnnotations; +import com.google.common.collect.ImmutableSet; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppoutput.EppResponse.ResponseData; +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import org.joda.time.DateTime; + +/** The {@link ResponseData} returned for an EPP info flow on a domain. */ +@XmlRootElement(name = "infData") +@XmlType(propOrder = { + "fullyQualifiedDomainName", + "repoId", + "statusValues", + "registrant", + "contacts", + "nameservers", + "subordinateHosts", + "currentSponsorClientId", + "creationClientId", + "creationTime", + "lastEppUpdateClientId", + "lastEppUpdateTime", + "registrationExpirationTime", + "lastTransferTime", + "authInfo"}) +@AutoValue +@CopyAnnotations +public abstract class DomainInfoData implements ResponseData { + + @XmlElement(name = "name") + abstract String getFullyQualifiedDomainName(); + + @XmlElement(name = "roid") + abstract String getRepoId(); + + @XmlElement(name = "status") + abstract ImmutableSet getStatusValues(); + + @XmlElement(name = "registrant") + abstract String getRegistrant(); + + @XmlElement(name = "contact") + abstract ImmutableSet getContacts(); + + @XmlElementWrapper(name = "ns") + @XmlElement(name = "hostObj") + @Nullable + abstract ImmutableSet getNameservers(); + + @XmlElement(name = "host") + @Nullable + abstract ImmutableSet getSubordinateHosts(); + + @XmlElement(name = "clID") + abstract String getCurrentSponsorClientId(); + + @XmlElement(name = "crID") + @Nullable + abstract String getCreationClientId(); + + @XmlElement(name = "crDate") + @Nullable + abstract DateTime getCreationTime(); + + @XmlElement(name = "upID") + @Nullable + abstract String getLastEppUpdateClientId(); + + @XmlElement(name = "upDate") + @Nullable + abstract DateTime getLastEppUpdateTime(); + + @XmlElement(name = "exDate") + @Nullable + abstract DateTime getRegistrationExpirationTime(); + + @XmlElement(name = "trDate") + @Nullable + abstract DateTime getLastTransferTime(); + + @XmlElement(name = "authInfo") + @Nullable + abstract DomainAuthInfo getAuthInfo(); + + /** Builder for {@link DomainInfoData}. */ + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setFullyQualifiedDomainName(String fullyQualifiedDomainName); + public abstract Builder setRepoId(String repoId); + public abstract Builder setStatusValues(ImmutableSet statusValues); + public abstract Builder setRegistrant(String registrant); + public abstract Builder setContacts(ImmutableSet contacts); + public abstract Builder setNameservers(@Nullable ImmutableSet nameservers); + public abstract Builder setSubordinateHosts(@Nullable ImmutableSet subordinateHosts); + public abstract Builder setCurrentSponsorClientId(String currentSponsorClientId); + public abstract Builder setCreationClientId(@Nullable String creationClientId); + public abstract Builder setCreationTime(@Nullable DateTime creationTime); + public abstract Builder setLastEppUpdateClientId(@Nullable String lastEppUpdateClientId); + public abstract Builder setLastEppUpdateTime(@Nullable DateTime lastEppUpdateTime); + public abstract Builder setRegistrationExpirationTime( + @Nullable DateTime registrationExpirationTime); + public abstract Builder setLastTransferTime(@Nullable DateTime lastTransferTime); + public abstract Builder setAuthInfo(@Nullable DomainAuthInfo authInfo); + + /** Internal accessor for use in {@link #build}. */ + @Nullable + abstract ImmutableSet getNameservers(); + + /** Generated build method. */ + abstract DomainInfoData autoBuild(); + + /** Public build method. */ + public DomainInfoData build() { + // If there are no nameservers use null, or an empty "ns" element will be marshalled. + setNameservers(forceEmptyToNull(getNameservers())); + return autoBuild(); + } + } + + public static Builder newBuilder() { + return new AutoValue_DomainInfoData.Builder(); + } +} diff --git a/java/google/registry/model/domain/DomainResource.java b/java/google/registry/model/domain/DomainResource.java index cc5f059e5..d4c099c27 100644 --- a/java/google/registry/model/domain/DomainResource.java +++ b/java/google/registry/model/domain/DomainResource.java @@ -44,10 +44,6 @@ import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import java.util.HashSet; import java.util.Set; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -56,23 +52,6 @@ import org.joda.time.Interval; * * @see RFC 5731 */ -@XmlRootElement(name = "infData") -@XmlType(propOrder = { - "fullyQualifiedDomainName", - "repoId", - "status", - "marshalledRegistrant", - "marshalledContacts", - "marshalledNameservers", - "subordinateHosts", - "currentSponsorClientId", - "creationClientId", - "creationTime", - "lastEppUpdateClientId", - "lastEppUpdateTime", - "registrationExpirationTime", - "lastTransferTime", - "authInfo"}) @Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION) @EntitySubclass(index = true) @ExternalMessagingName("domain") @@ -91,11 +70,9 @@ public class DomainResource extends DomainBase StatusValue.SERVER_HOLD); /** Fully qualified host names of this domain's active subordinate hosts. */ - @XmlElement(name = "host") Set subordinateHosts; /** When this domain's registration will expire. */ - @XmlElement(name = "exDate") DateTime registrationExpirationTime; /** @@ -105,7 +82,6 @@ public class DomainResource extends DomainBase * refer to a {@link PollMessage} timed to when the domain is fully deleted. If the domain is * restored, the message should be deleted. */ - @XmlTransient Key deletePollMessage; /** @@ -116,7 +92,6 @@ public class DomainResource extends DomainBase * {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one * should be created, and this field should be updated to point to the new one. */ - @XmlTransient Key autorenewBillingEvent; /** @@ -127,11 +102,9 @@ public class DomainResource extends DomainBase * {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one * should be created, and this field should be updated to point to the new one. */ - @XmlTransient Key autorenewPollMessage; /** The unexpired grace periods for this domain (some of which may not be active yet). */ - @XmlTransient Set gracePeriods; /** @@ -139,7 +112,6 @@ public class DomainResource extends DomainBase * Will only be populated for domains allocated from a sunrise application. */ @IgnoreSave(IfNull.class) - @XmlTransient String smdId; /** @@ -147,7 +119,6 @@ public class DomainResource extends DomainBase * for domains allocated from an application. */ @IgnoreSave(IfNull.class) - @XmlTransient DateTime applicationTime; /** @@ -155,11 +126,9 @@ public class DomainResource extends DomainBase * allocated from an application. */ @IgnoreSave(IfNull.class) - @XmlTransient Key application; /** Data about any pending or past transfers on this domain. */ - @XmlTransient TransferData transferData; /** @@ -167,7 +136,6 @@ public class DomainResource extends DomainBase * *

Can be null if the resource has never been transferred. */ - @XmlElement(name = "trDate") DateTime lastTransferTime; public ImmutableSet getSubordinateHosts() { diff --git a/java/google/registry/model/domain/ForeignKeyedDesignatedContact.java b/java/google/registry/model/domain/ForeignKeyedDesignatedContact.java index 288eaacef..f8a169f3c 100644 --- a/java/google/registry/model/domain/ForeignKeyedDesignatedContact.java +++ b/java/google/registry/model/domain/ForeignKeyedDesignatedContact.java @@ -25,14 +25,15 @@ import javax.xml.bind.annotation.XmlValue; * @see * RFC 5731 - EPP Domain Name Mapping - Contact and Client Identifiers */ -class ForeignKeyedDesignatedContact extends ImmutableObject { +public class ForeignKeyedDesignatedContact extends ImmutableObject { @XmlAttribute(required = true) DesignatedContact.Type type; @XmlValue String contactId; - static ForeignKeyedDesignatedContact create(DesignatedContact.Type type, String contactId) { + public static ForeignKeyedDesignatedContact create( + DesignatedContact.Type type, String contactId) { ForeignKeyedDesignatedContact instance = new ForeignKeyedDesignatedContact(); instance.type = type; instance.contactId = contactId; diff --git a/java/google/registry/model/eppoutput/EppResponse.java b/java/google/registry/model/eppoutput/EppResponse.java index 77a151aea..99557a3d9 100644 --- a/java/google/registry/model/eppoutput/EppResponse.java +++ b/java/google/registry/model/eppoutput/EppResponse.java @@ -21,10 +21,9 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import google.registry.model.Buildable; import google.registry.model.ImmutableObject; -import google.registry.model.contact.ContactResource; -import google.registry.model.domain.DomainApplication; +import google.registry.model.contact.ContactInfoData; +import google.registry.model.domain.DomainInfoData; import google.registry.model.domain.DomainRenewData; -import google.registry.model.domain.DomainResource; import google.registry.model.domain.fee06.FeeCheckResponseExtensionV06; import google.registry.model.domain.fee06.FeeCreateResponseExtensionV06; import google.registry.model.domain.fee06.FeeDeleteResponseExtensionV06; @@ -57,7 +56,7 @@ import google.registry.model.eppoutput.CreateData.ContactCreateData; import google.registry.model.eppoutput.CreateData.DomainCreateData; import google.registry.model.eppoutput.CreateData.HostCreateData; import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting; -import google.registry.model.host.HostResource; +import google.registry.model.host.HostInfoData; import google.registry.model.poll.MessageQueueInfo; import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; @@ -99,21 +98,20 @@ public class EppResponse extends ImmutableObject implements ResponseOrGreeting { /** Zero or more response "resData" results. */ @XmlElementRefs({ - @XmlElementRef(type = ContactResource.class), - @XmlElementRef(type = DomainApplication.class), - @XmlElementRef(type = DomainResource.class), - @XmlElementRef(type = HostResource.class), @XmlElementRef(type = ContactCheckData.class), @XmlElementRef(type = ContactCreateData.class), + @XmlElementRef(type = ContactInfoData.class), @XmlElementRef(type = ContactPendingActionNotificationResponse.class), @XmlElementRef(type = ContactTransferResponse.class), @XmlElementRef(type = DomainCheckData.class), @XmlElementRef(type = DomainCreateData.class), + @XmlElementRef(type = DomainInfoData.class), @XmlElementRef(type = DomainPendingActionNotificationResponse.class), @XmlElementRef(type = DomainRenewData.class), @XmlElementRef(type = DomainTransferResponse.class), @XmlElementRef(type = HostCheckData.class), - @XmlElementRef(type = HostCreateData.class)}) + @XmlElementRef(type = HostCreateData.class), + @XmlElementRef(type = HostInfoData.class)}) @XmlElementWrapper ImmutableList resData; diff --git a/java/google/registry/model/host/HostInfoData.java b/java/google/registry/model/host/HostInfoData.java new file mode 100644 index 000000000..9fe1cdb8a --- /dev/null +++ b/java/google/registry/model/host/HostInfoData.java @@ -0,0 +1,99 @@ +// Copyright 2016 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.model.host; + +import com.google.auto.value.AutoValue; +import com.google.auto.value.AutoValue.CopyAnnotations; +import com.google.common.collect.ImmutableSet; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppoutput.EppResponse.ResponseData; +import java.net.InetAddress; +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import org.joda.time.DateTime; + + +/** The {@link ResponseData} returned for an EPP info flow on a host. */ +@XmlRootElement(name = "infData") +@XmlType(propOrder = { + "fullyQualifiedHostName", + "repoId", + "statusValues", + "inetAddresses", + "currentSponsorClientId", + "creationClientId", + "creationTime", + "lastEppUpdateClientId", + "lastEppUpdateTime", + "lastTransferTime" }) +@AutoValue +@CopyAnnotations +public abstract class HostInfoData implements ResponseData { + + @XmlElement(name = "name") + abstract String getFullyQualifiedHostName(); + + @XmlElement(name = "roid") + abstract String getRepoId(); + + @XmlElement(name = "status") + abstract ImmutableSet getStatusValues(); + + @XmlElement(name = "addr") + abstract ImmutableSet getInetAddresses(); + + @XmlElement(name = "clID") + abstract String getCurrentSponsorClientId(); + + @XmlElement(name = "crID") + abstract String getCreationClientId(); + + @XmlElement(name = "crDate") + abstract DateTime getCreationTime(); + + @XmlElement(name = "upID") + @Nullable + abstract String getLastEppUpdateClientId(); + + @XmlElement(name = "upDate") + @Nullable + abstract DateTime getLastEppUpdateTime(); + + @XmlElement(name = "trDate") + @Nullable + abstract DateTime getLastTransferTime(); + + /** Builder for {@link HostInfoData}. */ + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setFullyQualifiedHostName(String fullyQualifiedHostName); + public abstract Builder setRepoId(String repoId); + public abstract Builder setStatusValues(ImmutableSet statusValues); + public abstract Builder setInetAddresses(ImmutableSet inetAddresses); + public abstract Builder setCurrentSponsorClientId(String currentSponsorClientId); + public abstract Builder setCreationClientId(String creationClientId); + public abstract Builder setCreationTime(DateTime creationTime); + public abstract Builder setLastEppUpdateClientId(@Nullable String lastEppUpdateClientId); + public abstract Builder setLastEppUpdateTime(@Nullable DateTime lastEppUpdateTime); + public abstract Builder setLastTransferTime(@Nullable DateTime lastTransferTime); + public abstract HostInfoData build(); + } + + public static Builder newBuilder() { + return new AutoValue_HostInfoData.Builder(); + } +} diff --git a/java/google/registry/model/host/HostResource.java b/java/google/registry/model/host/HostResource.java index ffdb964b6..df52e83c2 100644 --- a/java/google/registry/model/host/HostResource.java +++ b/java/google/registry/model/host/HostResource.java @@ -39,10 +39,6 @@ import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferStatus; import java.net.InetAddress; import java.util.Set; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; import org.joda.time.DateTime; /** @@ -53,18 +49,6 @@ import org.joda.time.DateTime; * * @see RFC 5732 */ -@XmlRootElement(name = "infData") -@XmlType(propOrder = { - "fullyQualifiedHostName", - "repoId", - "status", - "inetAddresses", - "currentSponsorClientId", - "creationClientId", - "creationTime", - "lastEppUpdateClientId", - "lastEppUpdateTime", - "lastTransferTime" }) @Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION) @ReportedOn @Entity @@ -79,18 +63,15 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource * However, there can be many hosts with the same name and non-overlapping lifetimes. */ @Index - @XmlTransient String fullyQualifiedHostName; /** IP Addresses for this host. Can be null if this is an external host. */ @Index - @XmlTransient Set inetAddresses; /** The superordinate domain of this host, or null if this is an external host. */ @Index @IgnoreSave(IfNull.class) - @XmlTransient @DoNotHydrate Key superordinateDomain; @@ -99,17 +80,14 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource * *

Can be null if the resource has never been transferred. */ - @XmlElement(name = "trDate") DateTime lastTransferTime; /** * The most recent time that the superordinate domain was changed, or null if this host is * external. */ - @XmlTransient DateTime lastSuperordinateChange; - @XmlElement(name = "name") public String getFullyQualifiedHostName() { return fullyQualifiedHostName; } @@ -118,7 +96,6 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource return superordinateDomain; } - @XmlElement(name = "addr") public ImmutableSet getInetAddresses() { return nullToEmptyImmutableCopy(inetAddresses); } diff --git a/javatests/google/registry/flows/domain/DomainApplicationInfoFlowTest.java b/javatests/google/registry/flows/domain/DomainApplicationInfoFlowTest.java index 8f533f21c..11415102d 100644 --- a/javatests/google/registry/flows/domain/DomainApplicationInfoFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainApplicationInfoFlowTest.java @@ -14,14 +14,24 @@ package google.registry.flows.domain; +import static com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig.getLocalMemcacheService; +import static com.google.common.base.Predicates.equalTo; import static com.google.common.io.BaseEncoding.base16; +import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistActiveHost; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions; +import static google.registry.util.DatastoreServiceUtils.KEY_TO_KIND_FUNCTION; +import com.google.appengine.api.memcache.MemcacheServicePb.MemcacheFlushRequest; +import com.google.appengine.tools.development.LocalRpcService; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -44,10 +54,12 @@ 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.ofy.RequestCapturingAsyncDatastoreService; import google.registry.model.registry.Registry.TldState; import google.registry.model.smd.EncodedSignedMark; import google.registry.testing.AppEngineRule; import google.registry.testing.EppLoader; +import java.util.List; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -317,4 +329,32 @@ public class DomainApplicationInfoFlowTest thrown.expect(ApplicationLaunchPhaseMismatchException.class); runFlow(); } + + /** Test that we load contacts and hosts as a batch rather than individually. */ + @Test + public void testBatchLoadingOfReferences() throws Exception { + persistTestEntities(HostsState.HOSTS_EXIST, MarksState.NO_MARKS_EXIST); + // Clear out memcache and session cache so that we count actual datastore calls. + ofy().clearSessionCache(); + getLocalMemcacheService().flushAll( + new LocalRpcService.Status(), MemcacheFlushRequest.newBuilder().build()); + int numPreviousReads = RequestCapturingAsyncDatastoreService.getReads().size(); + doSuccessfulTest("domain_info_sunrise_response.xml", HostsState.HOSTS_EXIST); + // Get all of the keys loaded in the flow, with each distinct load() call as a list of keys. + int numReadsWithContactsOrHosts = FluentIterable + .from(RequestCapturingAsyncDatastoreService.getReads()) + .skip(numPreviousReads) + .filter( + new Predicate>() { + @Override + public boolean apply(List keys) { + return FluentIterable.from(keys) + .transform(KEY_TO_KIND_FUNCTION) + .anyMatch(Predicates.or( + equalTo(Key.getKind(ContactResource.class)), + equalTo(Key.getKind(HostResource.class)))); + }}) + .size(); + assertThat(numReadsWithContactsOrHosts).isEqualTo(1); + } } diff --git a/javatests/google/registry/flows/domain/DomainInfoFlowTest.java b/javatests/google/registry/flows/domain/DomainInfoFlowTest.java index 7db81d4db..0c346be78 100644 --- a/javatests/google/registry/flows/domain/DomainInfoFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainInfoFlowTest.java @@ -14,7 +14,11 @@ package google.registry.flows.domain; +import static com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig.getLocalMemcacheService; +import static com.google.common.base.Predicates.equalTo; import static com.google.common.io.BaseEncoding.base16; +import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.newDomainResource; @@ -22,7 +26,13 @@ import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistActiveHost; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions; +import static google.registry.util.DatastoreServiceUtils.KEY_TO_KIND_FUNCTION; +import com.google.appengine.api.memcache.MemcacheServicePb.MemcacheFlushRequest; +import com.google.appengine.tools.development.LocalRpcService; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; @@ -46,7 +56,9 @@ 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.ofy.RequestCapturingAsyncDatastoreService; import google.registry.testing.AppEngineRule; +import java.util.List; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -612,4 +624,32 @@ public class DomainInfoFlowTest extends ResourceFlowTestCase>() { + @Override + public boolean apply(List keys) { + return FluentIterable.from(keys) + .transform(KEY_TO_KIND_FUNCTION) + .anyMatch(Predicates.or( + equalTo(Key.getKind(ContactResource.class)), + equalTo(Key.getKind(HostResource.class)))); + }}) + .size(); + assertThat(numReadsWithContactsOrHosts).isEqualTo(1); + } } diff --git a/javatests/google/registry/model/domain/DomainResourceTest.java b/javatests/google/registry/model/domain/DomainResourceTest.java index 19d443cbd..55b67af47 100644 --- a/javatests/google/registry/model/domain/DomainResourceTest.java +++ b/javatests/google/registry/model/domain/DomainResourceTest.java @@ -14,9 +14,7 @@ package google.registry.model.domain; -import static com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig.getLocalMemcacheService; import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Iterables.skip; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.EppResourceUtils.loadByForeignKey; import static google.registry.testing.DatastoreHelper.cloneAndSetAutoTimestamps; @@ -28,15 +26,12 @@ import static google.registry.testing.DomainResourceSubject.assertAboutDomains; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.joda.money.CurrencyUnit.USD; -import com.google.appengine.api.memcache.MemcacheServicePb.MemcacheFlushRequest; -import com.google.appengine.tools.development.LocalRpcService; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.googlecode.objectify.Key; -import google.registry.flows.EppXmlTransformer; import google.registry.model.EntityTestCase; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; @@ -48,11 +43,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.eppcommon.Trid; -import google.registry.model.eppoutput.EppOutput; -import google.registry.model.eppoutput.EppResponse; -import google.registry.model.eppoutput.Result.Code; import google.registry.model.host.HostResource; -import google.registry.model.ofy.RequestCapturingAsyncDatastoreService; import google.registry.model.poll.PollMessage; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; @@ -60,7 +51,6 @@ import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferStatus; import google.registry.testing.ExceptionRule; -import google.registry.xml.ValidationMode; import java.util.List; import org.joda.money.Money; import org.joda.time.DateTime; @@ -428,24 +418,6 @@ public class DomainResourceTest extends EntityTestCase { renewedThreeTimes.autorenewBillingEvent)); } - @Test - public void testMarshalingLoadsResourcesEfficiently() throws Exception { - // All of the resources are in memcache because they were put there when initially persisted. - // Clear out memcache so that we count actual datastore calls. - getLocalMemcacheService().flushAll( - new LocalRpcService.Status(), MemcacheFlushRequest.newBuilder().build()); - int numPreviousReads = RequestCapturingAsyncDatastoreService.getReads().size(); - EppXmlTransformer.marshal( - EppOutput.create(new EppResponse.Builder() - .setResultFromCode(Code.SUCCESS) - .setResData(domain) - .setTrid(Trid.create(null, "abc")) - .build()), - ValidationMode.STRICT); - // Assert that there was only one call to datastore (that may have loaded many keys). - assertThat(skip(RequestCapturingAsyncDatastoreService.getReads(), numPreviousReads)).hasSize(1); - } - @Test public void testToHydratedString_notCircular() { domain.toHydratedString(); // If there are circular references, this will overflow the stack. diff --git a/javatests/google/registry/model/translators/StatusValueAdapterTest.java b/javatests/google/registry/model/translators/StatusValueAdapterTest.java index 3a9685a50..7b6d4b34a 100644 --- a/javatests/google/registry/model/translators/StatusValueAdapterTest.java +++ b/javatests/google/registry/model/translators/StatusValueAdapterTest.java @@ -15,9 +15,11 @@ package google.registry.model.translators; import static com.google.common.truth.Truth.assertThat; +import static google.registry.util.DateTimeUtils.START_OF_TIME; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import google.registry.flows.EppXmlTransformer; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.EppInput; @@ -25,10 +27,11 @@ import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; import google.registry.model.eppoutput.EppOutput; import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.HostCommand; -import google.registry.model.host.HostResource; +import google.registry.model.host.HostInfoData; import google.registry.testing.AppEngineRule; import google.registry.testing.EppLoader; import google.registry.xml.ValidationMode; +import java.net.InetAddress; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,8 +53,14 @@ public class StatusValueAdapterTest { String marshalled = new String( EppXmlTransformer.marshal( EppOutput.create(new EppResponse.Builder() - .setResData(new HostResource.Builder() - .addStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED) + .setResData(HostInfoData.newBuilder() + .setCreationClientId("") + .setCreationTime(START_OF_TIME) + .setCurrentSponsorClientId("") + .setFullyQualifiedHostName("") + .setInetAddresses(ImmutableSet.of()) + .setRepoId("") + .setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED)) .build()) .build()), ValidationMode.LENIENT), diff --git a/javatests/google/registry/testing/DatastoreHelper.java b/javatests/google/registry/testing/DatastoreHelper.java index 2ff7a3785..febad5cf6 100644 --- a/javatests/google/registry/testing/DatastoreHelper.java +++ b/javatests/google/registry/testing/DatastoreHelper.java @@ -112,6 +112,7 @@ public class DatastoreHelper { public static HostResource newHostResource(String hostName) { return new HostResource.Builder() .setFullyQualifiedHostName(hostName) + .setCreationClientId("TheRegistrar") .setCurrentSponsorClientId("TheRegistrar") .setCreationTimeForTest(START_OF_TIME) .setRepoId(generateNewContactHostRoid()) @@ -218,6 +219,7 @@ public class DatastoreHelper { return new ContactResource.Builder() .setRepoId(repoId) .setContactId(contactId) + .setCreationClientId("TheRegistrar") .setCurrentSponsorClientId("TheRegistrar") .setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("2fooBAR"))) .setCreationTimeForTest(START_OF_TIME)