diff --git a/java/google/registry/model/domain/DomainApplication.java b/java/google/registry/model/domain/DomainApplication.java index 5bb948043..88c0430d2 100644 --- a/java/google/registry/model/domain/DomainApplication.java +++ b/java/google/registry/model/domain/DomainApplication.java @@ -14,17 +14,24 @@ package google.registry.model.domain; +import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.EntitySubclass; +import com.googlecode.objectify.annotation.OnLoad; import google.registry.model.annotations.ExternalMessagingName; import google.registry.model.domain.launch.ApplicationStatus; import google.registry.model.domain.launch.LaunchPhase; import google.registry.model.eppcommon.Trid; +import google.registry.model.eppinput.EppInput; +import google.registry.model.eppinput.EppInput.ResourceCommandWrapper; +import google.registry.model.reporting.HistoryEntry; 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; @@ -72,7 +79,7 @@ public class DomainApplication extends DomainBase { /** The requested registration period. */ @XmlTransient Period period; - + /** The current status of this application. */ @XmlTransient ApplicationStatus applicationStatus; @@ -85,6 +92,70 @@ public class DomainApplication extends DomainBase { @XmlTransient Money auctionPrice; + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + @OnLoad + void setYears() { + if (period == null) { + // Extract the registration period from the XML used to create the domain application. + try { + HistoryEntry history = ofy().load() + .type(HistoryEntry.class) + .ancestor(this) + .order("modificationTime") + .first() + .now(); + if (history != null) { + byte[] xmlBytes = history.getXmlBytes(); + EppInput eppInput = unmarshal(EppInput.class, xmlBytes); + period = ((DomainCommand.Create) + ((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand()) + .getResourceCommand()).getPeriod(); + } + } catch (Exception e) { + // If we encounter an exception, give up on this defaulting. + } + } + } + + /** + * Unmarshal bytes into Epp classes. + * + *

This method, and the things it depends on, are copied from EppXmlTransformer, because that + * class is in the flows directory, and we don't want a build dependency of model on build. It can + * be removed when the @OnLoad method is removed. + * + * @param clazz type to return, specified as a param to enforce typesafe generics + * @see "http://errorprone.info/bugpattern/TypeParameterUnusedInFormals" + */ + private static T unmarshal(Class clazz, byte[] bytes) throws Exception { + return INPUT_TRANSFORMER.unmarshal(clazz, new ByteArrayInputStream(bytes)); + } + + // Hardcoded XML schemas, ordered with respect to dependency. + private static final ImmutableList SCHEMAS = ImmutableList.of( + "eppcom.xsd", + "epp.xsd", + "contact.xsd", + "host.xsd", + "domain.xsd", + "rgp.xsd", + "secdns.xsd", + "fee06.xsd", + "fee11.xsd", + "fee12.xsd", + "metadata.xsd", + "mark.xsd", + "dsig.xsd", + "smd.xsd", + "launch.xsd", + "allocate.xsd", + "flags.xsd"); + + private static final XmlTransformer INPUT_TRANSFORMER = + new XmlTransformer(SCHEMAS, EppInput.class); + + // End of copied stuff used by @OnLoad method + @Override public String getFullyQualifiedDomainName() { return fullyQualifiedDomainName; @@ -158,7 +229,7 @@ public class DomainApplication extends DomainBase { getInstance().phase = phase; return this; } - + public Builder setPeriod(Period period) { getInstance().period = period; return this; diff --git a/javatests/google/registry/model/domain/DomainApplicationTest.java b/javatests/google/registry/model/domain/DomainApplicationTest.java index 4c62c98d8..364ee21f7 100644 --- a/javatests/google/registry/model/domain/DomainApplicationTest.java +++ b/javatests/google/registry/model/domain/DomainApplicationTest.java @@ -16,6 +16,7 @@ package google.registry.model.domain; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.EppResourceUtils.loadDomainApplication; +import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.cloneAndSetAutoTimestamps; import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.newDomainApplication; @@ -23,12 +24,17 @@ import static google.registry.testing.DatastoreHelper.newHostResource; 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.FullFieldsTestEntityHelper.makeHistoryEntry; +import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.joda.money.CurrencyUnit.USD; +import static org.joda.time.DateTimeZone.UTC; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; +import com.googlecode.objectify.VoidWork; import google.registry.model.EntityTestCase; import google.registry.model.billing.BillingEvent; import google.registry.model.domain.launch.ApplicationStatus; @@ -39,12 +45,14 @@ import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.Trid; import google.registry.model.host.HostResource; +import google.registry.model.reporting.HistoryEntry; import google.registry.model.smd.EncodedSignedMark; 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 org.joda.money.Money; +import org.joda.time.DateTime; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -102,7 +110,7 @@ public class DomainApplicationTest extends EntityTestCase { .build()) .setCreationTrid(Trid.create("client creation trid")) .setPhase(LaunchPhase.LANDRUSH) - .setPeriod(Period.create(5, Period.Unit.YEARS)) + // TODO(b/32447342): set period .setEncodedSignedMarks(ImmutableList.of(EncodedSignedMark.create("base64", "abcdefg="))) .setApplicationStatus(ApplicationStatus.ALLOCATED) .setAuctionPrice(Money.of(USD, 11)) @@ -118,6 +126,8 @@ public class DomainApplicationTest extends EntityTestCase { @Test public void testIndexing() throws Exception { + domainApplication = persistResource( + domainApplication.asBuilder().setPeriod(Period.create(5, Period.Unit.YEARS)).build()); verifyIndexing( domainApplication, "allContacts.contactId.linked", @@ -188,4 +198,71 @@ public class DomainApplicationTest extends EntityTestCase { // If there are circular references, this will overflow the stack. domainApplication.toHydratedString(); } + + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + private void triggerTheOnLoadMethod() { + ofy().transact(new VoidWork() { + @Override + public void vrun() { + domainApplication = ofy().load().fromEntity(ofy().save().toEntity(domainApplication)); + } + }); + } + + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + @Test + public void testPeriodIsNullByDefault() { + triggerTheOnLoadMethod(); + assertThat(domainApplication.getPeriod()).isNull(); + } + + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + @Test + public void testPeriodIsOneYearBecauseHistoryEntryHasNoPeriod() { + persistResource(makeHistoryEntry( + domainApplication, + HistoryEntry.Type.DOMAIN_APPLICATION_CREATE, + Period.create(5, Period.Unit.YEARS), + "testing", + DateTime.now(UTC), + loadFileWithSubstitutions( + getClass(), "domain_create_landrush.xml", ImmutableMap.of()))); + triggerTheOnLoadMethod(); + assertThat(domainApplication.getPeriod()).isNotNull(); + assertThat(domainApplication.getPeriod()).isEqualTo(Period.create(1, Period.Unit.YEARS)); + } + + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + @Test + public void testPeriodDefaultedFromHistoryEntry() { + persistResource(makeHistoryEntry( + domainApplication, + HistoryEntry.Type.DOMAIN_APPLICATION_CREATE, + Period.create(5, Period.Unit.YEARS), + "testing", + DateTime.now(UTC), + loadFileWithSubstitutions( + getClass(), "domain_create_landrush_with_period.xml", ImmutableMap.of("PERIOD", "5")))); + triggerTheOnLoadMethod(); + assertThat(domainApplication.getPeriod()).isNotNull(); + assertThat(domainApplication.getPeriod()).isEqualTo(Period.create(5, Period.Unit.YEARS)); + } + + // TODO(b/32447342): remove this once the period has been populated on all DomainApplications + @Test + public void testPeriodAlreadySet() { + domainApplication = persistResource( + domainApplication.asBuilder().setPeriod(Period.create(1, Period.Unit.YEARS)).build()); + persistResource(makeHistoryEntry( + domainApplication, + HistoryEntry.Type.DOMAIN_APPLICATION_CREATE, + Period.create(5, Period.Unit.YEARS), + "testing", + DateTime.now(UTC), + loadFileWithSubstitutions( + getClass(), "domain_create_landrush_with_period.xml", ImmutableMap.of("PERIOD", "5")))); + triggerTheOnLoadMethod(); + assertThat(domainApplication.getPeriod()).isNotNull(); + assertThat(domainApplication.getPeriod()).isEqualTo(Period.create(1, Period.Unit.YEARS)); + } } diff --git a/javatests/google/registry/model/domain/testdata/domain_create_landrush_with_period.xml b/javatests/google/registry/model/domain/testdata/domain_create_landrush_with_period.xml new file mode 100644 index 000000000..47a2c9f3c --- /dev/null +++ b/javatests/google/registry/model/domain/testdata/domain_create_landrush_with_period.xml @@ -0,0 +1,25 @@ + + + + + + example.tld + %PERIOD% + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + landrush + + + ABC-12345 + + diff --git a/javatests/google/registry/testing/FullFieldsTestEntityHelper.java b/javatests/google/registry/testing/FullFieldsTestEntityHelper.java index 4a0c6f44e..46fcb2af8 100644 --- a/javatests/google/registry/testing/FullFieldsTestEntityHelper.java +++ b/javatests/google/registry/testing/FullFieldsTestEntityHelper.java @@ -266,11 +266,21 @@ public final class FullFieldsTestEntityHelper { Period period, String reason, DateTime modificationTime) { + return makeHistoryEntry(resource, type, period, reason, modificationTime, ""); + } + + public static HistoryEntry makeHistoryEntry( + EppResource resource, + HistoryEntry.Type type, + Period period, + String reason, + DateTime modificationTime, + String xml) { HistoryEntry.Builder builder = new HistoryEntry.Builder() .setParent(resource) .setType(type) .setPeriod(period) - .setXmlBytes("".getBytes(UTF_8)) + .setXmlBytes(xml.getBytes(UTF_8)) .setModificationTime(modificationTime) .setClientId("foo") .setTrid(Trid.create("ABC-123"))