diff --git a/java/google/registry/model/domain/rgp/GracePeriodStatus.java b/java/google/registry/model/domain/rgp/GracePeriodStatus.java index 26285750e..9f4bccf62 100644 --- a/java/google/registry/model/domain/rgp/GracePeriodStatus.java +++ b/java/google/registry/model/domain/rgp/GracePeriodStatus.java @@ -14,8 +14,17 @@ package google.registry.model.domain.rgp; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static java.util.Arrays.asList; + +import com.google.common.base.Function; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import google.registry.model.translators.EnumToAttributeAdapter; import google.registry.model.translators.EnumToAttributeAdapter.EppEnum; +import java.util.Map; +import javax.annotation.Nullable; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @@ -90,6 +99,18 @@ public enum GracePeriodStatus implements EppEnum { */ TRANSFER("transferPeriod"); + /** Provide a quick lookup of GracePeriodStatus from XML name. */ + private static final Map XML_NAME_TO_GRACE_PERIOD_STATUS = + Maps.uniqueIndex( + // SUNRUSH_ADD isn't a real grace period type visible in EPP XML, so exclude it. + Iterables.filter(asList(GracePeriodStatus.values()), not(equalTo(SUNRUSH_ADD))), + new Function() { + @Override + public String apply(GracePeriodStatus gracePeriodStatus) { + return gracePeriodStatus.xmlName; + } + }); + @XmlAttribute(name = "s") private final String xmlName; @@ -101,4 +122,14 @@ public enum GracePeriodStatus implements EppEnum { public String getXmlName() { return xmlName; } + + /** + * Maps from xmlName to {@link GracePeriodStatus}. + * + * If no match is found for xmlName, null is returned. + */ + @Nullable + public static GracePeriodStatus fromXmlName(String xmlName) { + return XML_NAME_TO_GRACE_PERIOD_STATUS.get(xmlName); + } } diff --git a/java/google/registry/model/index/ForeignKeyIndex.java b/java/google/registry/model/index/ForeignKeyIndex.java index e2fd1675a..d5c637080 100644 --- a/java/google/registry/model/index/ForeignKeyIndex.java +++ b/java/google/registry/model/index/ForeignKeyIndex.java @@ -122,7 +122,7 @@ public abstract class ForeignKeyIndex extends BackupGroup /** * Loads a {@link Key} to an {@link EppResource} from the datastore by foreign key. * - *

Returns absent if no foreign key index with this foreign key was ever created, or if the + *

Returns null if no foreign key index with this foreign key was ever created, or if the * most recently created foreign key index was deleted before time "now". This method does not * actually check that the referenced resource actually exists. However, for normal epp resources, * it is safe to assume that the referenced resource exists if the foreign key index does. diff --git a/java/google/registry/model/reporting/HistoryEntry.java b/java/google/registry/model/reporting/HistoryEntry.java index 763efe7ad..05dba91f0 100644 --- a/java/google/registry/model/reporting/HistoryEntry.java +++ b/java/google/registry/model/reporting/HistoryEntry.java @@ -62,6 +62,8 @@ public class HistoryEntry extends ImmutableObject implements Buildable { HOST_DELETE_FAILURE, HOST_PENDING_DELETE, HOST_UPDATE, + /** Resource was created by an escrow file import. */ + RDE_IMPORT, /** * A synthetic history entry created by a tool or back-end migration script outside of the scope * of usual EPP flows. These are sometimes needed to serve as parents for billing events or poll diff --git a/java/google/registry/rde/XjcToDomainResourceConverter.java b/java/google/registry/rde/XjcToDomainResourceConverter.java new file mode 100644 index 000000000..f5881a406 --- /dev/null +++ b/java/google/registry/rde/XjcToDomainResourceConverter.java @@ -0,0 +1,307 @@ +// 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.rde; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.transform; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.util.DateTimeUtils.END_OF_TIME; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; + +import com.google.common.base.Ascii; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.net.InternetDomainName; +import com.googlecode.objectify.Key; +import google.registry.model.ImmutableObject; +import google.registry.model.billing.BillingEvent; +import google.registry.model.billing.BillingEvent.Flag; +import google.registry.model.billing.BillingEvent.Reason; +import google.registry.model.contact.ContactResource; +import google.registry.model.domain.DesignatedContact; +import google.registry.model.domain.DomainResource; +import google.registry.model.domain.GracePeriod; +import google.registry.model.domain.rgp.GracePeriodStatus; +import google.registry.model.domain.secdns.DelegationSignerData; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppcommon.Trid; +import google.registry.model.host.HostResource; +import google.registry.model.index.ForeignKeyIndex; +import google.registry.model.poll.PollMessage; +import google.registry.model.registry.Registries; +import google.registry.model.registry.Registry; +import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferStatus; +import google.registry.util.XmlToEnumMapper; +import google.registry.xjc.domain.XjcDomainContactType; +import google.registry.xjc.domain.XjcDomainNsType; +import google.registry.xjc.domain.XjcDomainStatusType; +import google.registry.xjc.rdedomain.XjcRdeDomain; +import google.registry.xjc.rdedomain.XjcRdeDomainElement; +import google.registry.xjc.rdedomain.XjcRdeDomainTransferDataType; +import google.registry.xjc.rgp.XjcRgpStatusType; +import google.registry.xjc.secdns.XjcSecdnsDsDataType; +import org.joda.time.DateTime; + +/** Utility class that converts an {@link XjcRdeDomainElement} into a {@link DomainResource}. */ +final class XjcToDomainResourceConverter extends XjcToEppResourceConverter { + + private static final XmlToEnumMapper TRANSFER_STATUS_MAPPER = + XmlToEnumMapper.create(TransferStatus.values()); + + private static final Function STATUS_CONVERTER = + new Function() { + @Override + public StatusValue apply(XjcDomainStatusType status) { + return convertStatusType(status); + } + }; + + private static final Function> HOST_OBJ_CONVERTER = + new Function>() { + @Override + public Key apply(String fullyQualifiedHostName) { + Key key = + ForeignKeyIndex.loadAndGetKey( + HostResource.class, fullyQualifiedHostName, DateTime.now()); + checkState( + key != null, + String.format("HostResource not found with name '%s'", fullyQualifiedHostName)); + return key; + } + }; + + private static final Function CONTACT_CONVERTER = + new Function() { + @Override + public DesignatedContact apply(XjcDomainContactType contact) { + return convertContactType(contact); + } + }; + + private static final Function SECDNS_CONVERTER = + new Function() { + @Override + public DelegationSignerData apply(XjcSecdnsDsDataType secdns) { + return convertSecdnsDsDataType(secdns); + } + }; + + /** Converts {@link XjcRgpStatusType} to {@link GracePeriod} */ + private static class GracePeriodConverter implements Function { + + private final XjcRdeDomain domain; + private final Key autoRenewBillingEvent; + private final Registry tld; + + GracePeriodConverter(XjcRdeDomain domain, Key autoRenewBillingEvent) { + this.domain = domain; + this.autoRenewBillingEvent = autoRenewBillingEvent; + this.tld = + Registry.get( + Registries.findTldForNameOrThrow(InternetDomainName.from(domain.getName())) + .toString()); + } + + @Override + public GracePeriod apply(XjcRgpStatusType gracePeriodStatus) { + // TODO: (wolfgang) address these items in code review: + // verify that this logic is correct + switch (gracePeriodStatus.getS()) { + case ADD_PERIOD: + return GracePeriod.createWithoutBillingEvent( + GracePeriodStatus.ADD, + domain.getCrDate().plus(this.tld.getAddGracePeriodLength()), + domain.getCrRr().getClient()); + case AUTO_RENEW_PERIOD: + return GracePeriod.createForRecurring( + GracePeriodStatus.AUTO_RENEW, + domain.getUpDate().plus(this.tld.getAutoRenewGracePeriodLength()), + domain.getClID(), + autoRenewBillingEvent); + case PENDING_DELETE: + return GracePeriod.createWithoutBillingEvent( + GracePeriodStatus.PENDING_DELETE, + domain.getUpDate().plus(this.tld.getPendingDeleteLength()), + domain.getClID()); + case REDEMPTION_PERIOD: + return GracePeriod.createWithoutBillingEvent( + GracePeriodStatus.REDEMPTION, + domain.getUpDate().plus(this.tld.getRedemptionGracePeriodLength()), + domain.getClID()); + case RENEW_PERIOD: + return GracePeriod.createWithoutBillingEvent( + GracePeriodStatus.RENEW, + domain.getUpDate().plus(this.tld.getRenewGracePeriodLength()), + domain.getClID()); + case TRANSFER_PERIOD: + return GracePeriod.createWithoutBillingEvent( + GracePeriodStatus.TRANSFER, + domain.getUpDate().plus(this.tld.getTransferGracePeriodLength()), + domain.getTrnData().getReRr().getValue()); + default: + throw new IllegalArgumentException( + "Unsupported grace period status: " + gracePeriodStatus.getS()); + } + } + } + + /** Converts {@link XjcRdeDomain} to {@link DomainResource}. */ + static DomainResource convertDomain(XjcRdeDomain domain) { + // First create history entry and autorenew billing event/poll message + // Autorenew billing event is required for creating AUTO_RENEW grace period + HistoryEntry historyEntry = createHistoryEntry(domain); + BillingEvent.Recurring autoRenewBillingEvent = + createAutoRenewBillingEvent(domain, historyEntry); + PollMessage.Autorenew pollMessage = createAutoRenewPollMessage(domain, historyEntry); + ofy().save().entities(historyEntry, autoRenewBillingEvent, pollMessage); + GracePeriodConverter gracePeriodConverter = + new GracePeriodConverter(domain, Key.create(autoRenewBillingEvent)); + DomainResource.Builder builder = + new DomainResource.Builder() + .setFullyQualifiedDomainName(domain.getName()) + .setRepoId(domain.getRoid()) + .setIdnTableName(domain.getIdnTableId()) + .setCurrentSponsorClientId(domain.getClID()) + .setCreationClientId(domain.getCrRr().getValue()) + .setCreationTime(domain.getCrDate()) + .setRegistrationExpirationTime(domain.getExDate()) + .setLastEppUpdateTime(domain.getUpDate()) + .setLastEppUpdateClientId(domain.getUpRr() == null ? null : domain.getUpRr().getValue()) + .setLastTransferTime(domain.getTrDate()) + .setStatusValues(ImmutableSet.copyOf(transform(domain.getStatuses(), STATUS_CONVERTER))) + .setNameservers(convertNameservers(domain.getNs())) + .setGracePeriods( + ImmutableSet.copyOf(transform(domain.getRgpStatuses(), gracePeriodConverter))) + .setContacts(ImmutableSet.copyOf(transform(domain.getContacts(), CONTACT_CONVERTER))) + .setDsData( + domain.getSecDNS() == null + ? ImmutableSet.of() + : ImmutableSet.copyOf( + transform(domain.getSecDNS().getDsDatas(), SECDNS_CONVERTER))) + .setTransferData(convertDomainTransferData(domain.getTrnData())); + checkArgumentNotNull( + domain.getRegistrant(), "Registrant is missing for domain '%s'", domain.getName()); + builder = builder.setRegistrant(convertRegistrant(domain.getRegistrant())); + return builder.build(); + } + + private static BillingEvent.Recurring createAutoRenewBillingEvent( + XjcRdeDomain domain, HistoryEntry historyEntry) { + final BillingEvent.Recurring billingEvent = + new BillingEvent.Recurring.Builder() + .setReason(Reason.RENEW) + .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) + .setTargetId(domain.getRoid()) + .setClientId(domain.getClID()) + .setEventTime(domain.getExDate()) + .setRecurrenceEndTime(END_OF_TIME) + .setParent(historyEntry) + .build(); + return billingEvent; + } + + private static PollMessage.Autorenew createAutoRenewPollMessage( + XjcRdeDomain domain, HistoryEntry historyEntry) { + final PollMessage.Autorenew pollMessage = + new PollMessage.Autorenew.Builder() + .setTargetId(domain.getRoid()) + .setClientId(domain.getClID()) + .setEventTime(domain.getExDate()) + .setMsg("Domain was auto-renewed.") + .setParent(historyEntry) + .build(); + return pollMessage; + } + + private static HistoryEntry createHistoryEntry(XjcRdeDomain domain) { + XjcRdeDomainElement element = new XjcRdeDomainElement(domain); + final HistoryEntry historyEntry = + new HistoryEntry.Builder() + .setType(HistoryEntry.Type.RDE_IMPORT) + .setClientId(domain.getClID()) + .setTrid(Trid.create(null)) + .setModificationTime(DateTime.now()) + .setXmlBytes(getObjectXml(element)) + .setBySuperuser(true) + .setReason("RDE Import") + .setRequestedByRegistrar(false) + .setParent(Key.create(null, DomainResource.class, domain.getRoid())) + .build(); + return historyEntry; + } + + /** Returns {@link Key} for registrant from foreign key */ + private static Key convertRegistrant(String contactId) { + Key key = + ForeignKeyIndex.loadAndGetKey(ContactResource.class, contactId, DateTime.now()); + checkState(key != null, "Registrant not found: '%s'", contactId); + return key; + } + + /** Converts {@link XjcDomainNsType} to ImmutableSet>. */ + private static ImmutableSet> convertNameservers(XjcDomainNsType ns) { + // If no hosts are specified, return an empty set + if (ns == null || (ns.getHostAttrs() == null && ns.getHostObjs() == null)) { + return ImmutableSet.of(); + } + // Domain linked hosts must be specified by host object, not host attributes. + checkArgument( + ns.getHostAttrs() == null || ns.getHostAttrs().isEmpty(), + "Host attributes are not yet supported"); + return ImmutableSet.copyOf(Iterables.transform(ns.getHostObjs(), HOST_OBJ_CONVERTER)); + } + + /** Converts {@link XjcRdeDomainTransferDataType} to {@link TransferData}. */ + private static TransferData convertDomainTransferData(XjcRdeDomainTransferDataType data) { + if (data == null) { + return TransferData.EMPTY; + } + return new TransferData.Builder() + .setTransferStatus(TRANSFER_STATUS_MAPPER.xmlToEnum(data.getTrStatus().value())) + .setGainingClientId(data.getReRr().getValue()) + .setLosingClientId(data.getAcRr().getValue()) + .setTransferRequestTime(data.getReDate()) + .setPendingTransferExpirationTime(data.getAcDate()) + .build(); + } + + /** Converts {@link XjcDomainStatusType} to {@link StatusValue}. */ + private static StatusValue convertStatusType(XjcDomainStatusType type) { + return StatusValue.fromXmlName(type.getS().value()); + } + + /** Converts {@link XjcSecdnsDsDataType} to {@link DelegationSignerData}. */ + private static DelegationSignerData convertSecdnsDsDataType(XjcSecdnsDsDataType secdns) { + return DelegationSignerData.create( + secdns.getKeyTag(), secdns.getAlg(), secdns.getDigestType(), secdns.getDigest()); + } + + /** Converts {@link XjcDomainContactType} to {@link DesignatedContact}. */ + private static DesignatedContact convertContactType(XjcDomainContactType contact) { + String contactId = contact.getValue(); + Key key = + ForeignKeyIndex.loadAndGetKey(ContactResource.class, contactId, DateTime.now()); + checkState(key != null, "Contact not found: '%s'", contactId); + DesignatedContact.Type type = + DesignatedContact.Type.valueOf(Ascii.toUpperCase(contact.getType().toString())); + return DesignatedContact.create(type, key); + } + + private XjcToDomainResourceConverter() {} +} diff --git a/java/google/registry/rde/XjcToEppResourceConverter.java b/java/google/registry/rde/XjcToEppResourceConverter.java new file mode 100644 index 000000000..715339223 --- /dev/null +++ b/java/google/registry/rde/XjcToEppResourceConverter.java @@ -0,0 +1,63 @@ +// 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.rde; + +import com.google.common.base.Joiner; +import google.registry.model.EppResource; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +/** + * Base class for Jaxb object to {@link EppResource} converters + */ +public abstract class XjcToEppResourceConverter { + + /** List of packages to initialize JAXBContext. **/ + private static final String JAXB_CONTEXT_PACKAGES = Joiner.on(":") + .join(Arrays.asList( + "google.registry.xjc.contact", + "google.registry.xjc.domain", + "google.registry.xjc.host", + "google.registry.xjc.mark", + "google.registry.xjc.rde", + "google.registry.xjc.rdecontact", + "google.registry.xjc.rdedomain", + "google.registry.xjc.rdeeppparams", + "google.registry.xjc.rdeheader", + "google.registry.xjc.rdeidn", + "google.registry.xjc.rdenndn", + "google.registry.xjc.rderegistrar", + "google.registry.xjc.smd")); + + /** Creates a {@link Marshaller} for serializing Jaxb objects */ + private static Marshaller createMarshaller() throws JAXBException { + return JAXBContext.newInstance(JAXB_CONTEXT_PACKAGES).createMarshaller(); + } + + protected static byte[] getObjectXml(JAXBElement jaxbElement) { + try { + Marshaller marshaller = createMarshaller(); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + marshaller.marshal(jaxbElement, bout); + return bout.toByteArray(); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } +} diff --git a/javatests/google/registry/rde/XjcToDomainResourceConverterTest.java b/javatests/google/registry/rde/XjcToDomainResourceConverterTest.java new file mode 100644 index 000000000..090cbab69 --- /dev/null +++ b/javatests/google/registry/rde/XjcToDomainResourceConverterTest.java @@ -0,0 +1,375 @@ +// 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.rde; + +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.createTld; +import static google.registry.testing.DatastoreHelper.getHistoryEntries; +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.util.DateTimeUtils.END_OF_TIME; +import static java.util.Arrays.asList; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import com.google.common.io.ByteSource; +import com.googlecode.objectify.Key; +import com.googlecode.objectify.Work; +import google.registry.model.billing.BillingEvent; +import google.registry.model.billing.BillingEvent.Flag; +import google.registry.model.billing.BillingEvent.Reason; +import google.registry.model.contact.ContactResource; +import google.registry.model.domain.DesignatedContact; +import google.registry.model.domain.DomainResource; +import google.registry.model.domain.GracePeriod; +import google.registry.model.domain.rgp.GracePeriodStatus; +import google.registry.model.domain.secdns.DelegationSignerData; +import google.registry.model.eppcommon.StatusValue; +import google.registry.model.host.HostResource; +import google.registry.model.poll.PollMessage; +import google.registry.model.reporting.HistoryEntry; +import google.registry.testing.AppEngineRule; +import google.registry.testing.ExceptionRule; +import google.registry.xjc.rdedomain.XjcRdeDomain; +import google.registry.xjc.rdedomain.XjcRdeDomainElement; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link XjcToDomainResourceConverter} */ +@RunWith(JUnit4.class) +public class XjcToDomainResourceConverterTest { + + //List of packages to initialize JAXBContext + private static final String JAXB_CONTEXT_PACKAGES = Joiner.on(":").join(asList( + "google.registry.xjc.contact", + "google.registry.xjc.domain", + "google.registry.xjc.host", + "google.registry.xjc.mark", + "google.registry.xjc.rde", + "google.registry.xjc.rdecontact", + "google.registry.xjc.rdedomain", + "google.registry.xjc.rdeeppparams", + "google.registry.xjc.rdeheader", + "google.registry.xjc.rdeidn", + "google.registry.xjc.rdenndn", + "google.registry.xjc.rderegistrar", + "google.registry.xjc.smd")); + + @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); + + @Rule public final ExceptionRule thrown = new ExceptionRule(); + + private Unmarshaller unmarshaller; + + @Before + public void before() throws Exception { + createTld("example"); + unmarshaller = JAXBContext.newInstance(JAXB_CONTEXT_PACKAGES).createUnmarshaller(); + } + + @Test + public void testConvertDomainResource() throws Exception { + final ContactResource jd1234 = persistActiveContact("jd1234"); + final ContactResource sh8013 = persistActiveContact("sh8013"); + ImmutableSet expectedContacts = + ImmutableSet.of( + DesignatedContact.create(DesignatedContact.Type.ADMIN, Key.create(sh8013)), + DesignatedContact.create(DesignatedContact.Type.TECH, Key.create(sh8013))); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getFullyQualifiedDomainName()).isEqualTo("example1.example"); + assertThat(domain.getRepoId()).isEqualTo("Dexample1-TEST"); + // A DomainResource has status INACTIVE if there are no nameservers. + assertThat(domain.getStatusValues()).isEqualTo(ImmutableSet.of(StatusValue.INACTIVE)); + assertThat(domain.getRegistrant().getName()).isEqualTo(jd1234.getRepoId()); + assertThat(domain.getContacts()).isEqualTo(expectedContacts); + assertThat(domain.getCurrentSponsorClientId()).isEqualTo("RegistrarX"); + assertThat(domain.getCreationClientId()).isEqualTo("RegistrarX"); + assertThat(domain.getCreationTime()).isEqualTo(DateTime.parse("1999-04-03T22:00:00.0Z")); + assertThat(domain.getRegistrationExpirationTime()) + .isEqualTo(DateTime.parse("2015-04-03T22:00:00.0Z")); + assertThat(domain.getGracePeriods()).isEmpty(); + assertThat(domain.getLastEppUpdateClientId()).isNull(); + assertThat(domain.getLastEppUpdateTime()).isNull(); + } + + @Test + public void testConvertDomainResourceAddPeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_addPeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.ADD); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getCrRr().getClient()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getCrDate().plusDays(5)); + } + + @Test + public void testConvertDomainResourceAutoRenewPeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_autoRenewPeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.AUTO_RENEW); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getUpDate().plusDays(45)); + } + + @Test + public void testConvertDomainResourceRedemptionPeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_redemptionPeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.REDEMPTION); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getUpDate().plusDays(30)); + } + + @Test + public void testConvertDomainResourceRenewPeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_renewPeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.RENEW); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getUpDate().plusDays(5)); + } + + @Test + public void testConvertDomainResourcePendingDeletePeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pendingDeletePeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.PENDING_DELETE); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getUpDate().plusDays(5)); + } + + @Test + public void testConvertDomainResourcePendingRestorePeriodUnsupported() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pendingRestorePeriod.xml"); + thrown.expect( + IllegalArgumentException.class, "Unsupported grace period status: PENDING_RESTORE"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceTransferPeriod() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_transferPeriod.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getGracePeriods()).hasSize(1); + GracePeriod gracePeriod = domain.getGracePeriods().asList().get(0); + assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.TRANSFER); + assertThat(gracePeriod.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(gracePeriod.getExpirationTime()).isEqualTo(xjcDomain.getUpDate().plusDays(5)); + } + + @Test + public void testConvertDomainResourceEppUpdateRegistrar() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_up_rr.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getLastEppUpdateClientId()).isEqualTo("RegistrarX"); + } + + @Test + public void testConvertDomainResourceWithHostObjs() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + HostResource host1 = persistActiveHost("ns1.example.net"); + HostResource host2 = persistActiveHost("ns2.example.net"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_host_objs.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + assertThat(domain.getNameservers()).containsExactly(Key.create(host1), Key.create(host2)); + } + + @Test + public void testConvertDomainResourceWithHostAttrs() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + persistActiveHost("ns1.example.net"); + persistActiveHost("ns2.example.net"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_host_attrs.xml"); + thrown.expect(IllegalArgumentException.class, "Host attributes are not yet supported"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceHostNotFound() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + persistActiveHost("ns1.example.net"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_host_objs.xml"); + thrown.expect( + IllegalStateException.class, "HostResource not found with name 'ns2.example.net'"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceRegistrantNotFound() throws Exception { + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + thrown.expect(IllegalStateException.class, "Registrant not found: 'jd1234'"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceRegistrantMissing() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_registrant_missing.xml"); + thrown.expect( + IllegalArgumentException.class, "Registrant is missing for domain 'example1.example'"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceAdminNotFound() throws Exception { + persistActiveContact("jd1234"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + thrown.expect(IllegalStateException.class, "Contact not found: 'sh8013'"); + convertDomainInTransaction(xjcDomain); + } + + @Test + public void testConvertDomainResourceSecDnsData() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_secdns.xml"); + DomainResource domain = convertDomainInTransaction(xjcDomain); + byte[] digest = + base16().decode("5FA1FA1C2F70AA483FE178B765D82B272072B4E4167902C5B7F97D46C8899F44"); + assertThat(domain.getDsData()).containsExactly(DelegationSignerData.create(4609, 8, 2, digest)); + } + + @Test + public void testConvertDomainResourceHistoryEntry() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + // First import in a transaction, then verify in another transaction. + // Ancestor queries don't work within the same transaction. + DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain)); + List historyEntries = getHistoryEntries(domain); + assertThat(historyEntries).hasSize(1); + HistoryEntry entry = historyEntries.get(0); + assertThat(entry.getType()).isEqualTo(HistoryEntry.Type.RDE_IMPORT); + assertThat(entry.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(entry.getBySuperuser()).isTrue(); + assertThat(entry.getReason()).isEqualTo("RDE Import"); + assertThat(entry.getRequestedByRegistrar()).isFalse(); + // check xml against original domain xml + try (InputStream ins = new ByteArrayInputStream(entry.getXmlBytes())) { + XjcRdeDomain unmarshalledXml = ((XjcRdeDomainElement) unmarshaller.unmarshal(ins)).getValue(); + assertThat(unmarshalledXml.getName()).isEqualTo(xjcDomain.getName()); + assertThat(unmarshalledXml.getRoid()).isEqualTo(xjcDomain.getRoid()); + } + } + + @Test + public void testConvertDomainResourceAutoRenewBillingEvent() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + // First import in a transaction, then verify in another transaction. + // Ancestor queries don't work within the same transaction. + DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain)); + List historyEntries = getHistoryEntries(domain); + assertThat(historyEntries).hasSize(1); + HistoryEntry entry = historyEntries.get(0); + List billingEvents = + ofy().load().type(BillingEvent.Recurring.class).ancestor(entry).list(); + assertThat(billingEvents).hasSize(1); + BillingEvent.Recurring autoRenewEvent = billingEvents.get(0); + assertThat(autoRenewEvent.getReason()).isEqualTo(Reason.RENEW); + assertThat(autoRenewEvent.getFlags()).isEqualTo(ImmutableSet.of(Flag.AUTO_RENEW)); + assertThat(autoRenewEvent.getTargetId()).isEqualTo(xjcDomain.getRoid()); + assertThat(autoRenewEvent.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(autoRenewEvent.getEventTime()).isEqualTo(xjcDomain.getExDate()); + assertThat(autoRenewEvent.getRecurrenceEndTime()).isEqualTo(END_OF_TIME); + } + + @Test + public void testConvertDomainResourceAutoRenewPollMessage() throws Exception { + persistActiveContact("jd1234"); + persistActiveContact("sh8013"); + final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment.xml"); + // First import in a transaction, then verify in another transaction. + // Ancestor queries don't work within the same transaction. + DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain)); + List historyEntries = getHistoryEntries(domain); + assertThat(historyEntries).hasSize(1); + HistoryEntry entry = historyEntries.get(0); + List pollMessages = ofy().load().type(PollMessage.class).ancestor(entry).list(); + assertThat(pollMessages).hasSize(1); + PollMessage pollMessage = pollMessages.get(0); + assertThat(pollMessage).isInstanceOf(PollMessage.Autorenew.class); + assertThat(((PollMessage.Autorenew) pollMessage).getTargetId()).isEqualTo(xjcDomain.getRoid()); + assertThat(pollMessage.getClientId()).isEqualTo(xjcDomain.getClID()); + assertThat(pollMessage.getEventTime()).isEqualTo(xjcDomain.getExDate()); + assertThat(pollMessage.getMsg()).isEqualTo("Domain was auto-renewed."); + } + + private static DomainResource convertDomainInTransaction(final XjcRdeDomain xjcDomain) { + return ofy().transact(new Work() { + @Override + public DomainResource run() { + return XjcToDomainResourceConverter.convertDomain(xjcDomain); + }}); + } + + private XjcRdeDomain loadDomainFromRdeXml(String filename) { + try { + ByteSource source = RdeTestData.get(filename); + try (InputStream ins = source.openStream()) { + return ((XjcRdeDomainElement) unmarshaller.unmarshal(ins)).getValue(); + } + } catch (JAXBException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/javatests/google/registry/rde/testdata/domain_fragment.xml b/javatests/google/registry/rde/testdata/domain_fragment.xml new file mode 100644 index 000000000..39940b8a3 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment.xml @@ -0,0 +1,14 @@ + + example1.example + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_addPeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_addPeriod.xml new file mode 100644 index 000000000..f920a832e --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_addPeriod.xml @@ -0,0 +1,15 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_autoRenewPeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_autoRenewPeriod.xml new file mode 100644 index 000000000..ee7244db1 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_autoRenewPeriod.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_host_attrs.xml b/javatests/google/registry/rde/testdata/domain_fragment_host_attrs.xml new file mode 100644 index 000000000..7c6005ccd --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_host_attrs.xml @@ -0,0 +1,26 @@ + + example1.example + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + + + ns1.example.net + 192.0.2.2 + 1080:0:0:0:8:800:200C:417A + + + ns2.example.net + + + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_host_objs.xml b/javatests/google/registry/rde/testdata/domain_fragment_host_objs.xml new file mode 100644 index 000000000..9395c05d7 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_host_objs.xml @@ -0,0 +1,18 @@ + + example1.example + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + + ns1.example.net + ns2.example.net + + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_pendingDeletePeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_pendingDeletePeriod.xml new file mode 100644 index 000000000..cbd4cf742 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_pendingDeletePeriod.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_pendingRestorePeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_pendingRestorePeriod.xml new file mode 100644 index 000000000..1834ff004 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_pendingRestorePeriod.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_redemptionPeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_redemptionPeriod.xml new file mode 100644 index 000000000..11c34817f --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_redemptionPeriod.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_registrant_missing.xml b/javatests/google/registry/rde/testdata/domain_fragment_registrant_missing.xml new file mode 100644 index 000000000..b3c778dd3 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_registrant_missing.xml @@ -0,0 +1,13 @@ + + example1.example + Dexample1-TEST + + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_renewPeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_renewPeriod.xml new file mode 100644 index 000000000..2ca5714e3 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_renewPeriod.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_secdns.xml b/javatests/google/registry/rde/testdata/domain_fragment_secdns.xml new file mode 100644 index 000000000..db7200412 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_secdns.xml @@ -0,0 +1,23 @@ + + example1.example + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + + + 4609 + 8 + 2 + 5FA1FA1C2F70AA483FE178B765D82B272072B4E4167902C5B7F97D46C8899F44 + + + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_transferPeriod.xml b/javatests/google/registry/rde/testdata/domain_fragment_transferPeriod.xml new file mode 100644 index 000000000..41661bb23 --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_transferPeriod.xml @@ -0,0 +1,24 @@ + + example1.example + Dexample1-TEST + + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z + + clientApproved + RegistrarX + 2014-10-08T16:23:21.897803Z + RegistrarY + 2014-10-09T08:25:43.305554Z + 2017-08-07T18:05:14.039016Z + + diff --git a/javatests/google/registry/rde/testdata/domain_fragment_up_rr.xml b/javatests/google/registry/rde/testdata/domain_fragment_up_rr.xml new file mode 100644 index 000000000..dac24751c --- /dev/null +++ b/javatests/google/registry/rde/testdata/domain_fragment_up_rr.xml @@ -0,0 +1,16 @@ + + example1.example + Dexample1-TEST + + jd1234 + sh8013 + sh8013 + RegistrarX + RegistrarX + RegistrarX + 1999-04-03T22:00:00.0Z + 2015-04-03T22:00:00.0Z + 2014-04-03T22:00:00.0Z +