Handle pending transfers on domain imports from RDE deposits

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148496597
This commit is contained in:
Wolfgang Meyers 2017-02-24 13:27:07 -08:00 committed by Ben McIlwain
parent 3ca9bb6aeb
commit 9f90597691
14 changed files with 752 additions and 125 deletions

View file

@ -10,9 +10,11 @@ java_library(
deps = [ deps = [
"//java/google/registry/config", "//java/google/registry/config",
"//java/google/registry/dns", "//java/google/registry/dns",
"//java/google/registry/flows",
"//java/google/registry/gcs", "//java/google/registry/gcs",
"//java/google/registry/mapreduce", "//java/google/registry/mapreduce",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/pricing",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/util", "//java/google/registry/util",
"//java/google/registry/xjc", "//java/google/registry/xjc",
@ -28,5 +30,6 @@ java_library(
"@com_google_guava", "@com_google_guava",
"@javax_servlet_api", "@javax_servlet_api",
"@joda_time", "@joda_time",
"@org_joda_money",
], ],
) )

View file

@ -140,7 +140,7 @@ public class RdeContactImportAction implements Runnable {
public void vrun() { public void vrun() {
ContactResource contact = ContactResource contact =
XjcToContactResourceConverter.convertContact(xjcContact); XjcToContactResourceConverter.convertContact(xjcContact);
getImportUtils().importContact(contact); getImportUtils().importEppResource(contact);
} }
}); });
// Record number of contacts imported // Record number of contacts imported

View file

@ -14,10 +14,18 @@
package google.registry.rde.imports; package google.registry.rde.imports;
import static google.registry.flows.domain.DomainTransferUtils.createLosingTransferPollMessage;
import static google.registry.flows.domain.DomainTransferUtils.createPendingTransferData;
import static google.registry.flows.domain.DomainTransferUtils.createTransferServerApproveEntities;
import static google.registry.mapreduce.MapreduceRunner.PARAM_MAP_SHARDS; import static google.registry.mapreduce.MapreduceRunner.PARAM_MAP_SHARDS;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.rde.imports.RdeImportUtils.createAutoRenewBillingEventForDomainImport;
import static google.registry.rde.imports.RdeImportUtils.createAutoRenewPollMessageForDomainImport;
import static google.registry.rde.imports.RdeImportUtils.createHistoryEntryForDomainImport;
import static google.registry.rde.imports.RdeImportsModule.PATH; import static google.registry.rde.imports.RdeImportsModule.PATH;
import static google.registry.util.PipelineUtils.createJobPath; import static google.registry.util.PipelineUtils.createJobPath;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.appengine.tools.cloudstorage.GcsService; import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory; import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
@ -25,13 +33,20 @@ import com.google.appengine.tools.cloudstorage.RetryParams;
import com.google.appengine.tools.mapreduce.Mapper; import com.google.appengine.tools.mapreduce.Mapper;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.VoidWork; import com.googlecode.objectify.VoidWork;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule; import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.dns.DnsQueue; import google.registry.dns.DnsQueue;
import google.registry.gcs.GcsUtils; import google.registry.gcs.GcsUtils;
import google.registry.mapreduce.MapreduceRunner; import google.registry.mapreduce.MapreduceRunner;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.model.transfer.TransferStatus;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.Response; import google.registry.request.Response;
@ -41,6 +56,7 @@ import google.registry.xjc.JaxbFragment;
import google.registry.xjc.rdedomain.XjcRdeDomain; import google.registry.xjc.rdedomain.XjcRdeDomain;
import google.registry.xjc.rdedomain.XjcRdeDomainElement; import google.registry.xjc.rdedomain.XjcRdeDomainElement;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.money.Money;
/** /**
* A mapreduce that imports domains from an escrow file. * A mapreduce that imports domains from an escrow file.
@ -145,17 +161,70 @@ public class RdeDomainImportAction implements Runnable {
public void map(JaxbFragment<XjcRdeDomainElement> fragment) { public void map(JaxbFragment<XjcRdeDomainElement> fragment) {
final XjcRdeDomain xjcDomain = fragment.getInstance().getValue(); final XjcRdeDomain xjcDomain = fragment.getInstance().getValue();
try { try {
logger.infofmt("Converting xml for domain %s", xjcDomain.getName());
// Record number of attempted map operations // Record number of attempted map operations
getContext().incrementCounter("domain imports attempted"); getContext().incrementCounter("domain imports attempted");
logger.infofmt("Saving domain %s", xjcDomain.getName()); logger.infofmt("Saving domain %s", xjcDomain.getName());
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
DomainResource domain = HistoryEntry historyEntry = createHistoryEntryForDomainImport(xjcDomain);
XjcToDomainResourceConverter.convertDomain(xjcDomain); BillingEvent.Recurring autorenewBillingEvent =
getImportUtils().importDomain(domain); createAutoRenewBillingEventForDomainImport(xjcDomain, historyEntry);
PollMessage.Autorenew autorenewPollMessage =
createAutoRenewPollMessageForDomainImport(xjcDomain, historyEntry);
DomainResource domain = XjcToDomainResourceConverter.convertDomain(
xjcDomain, autorenewBillingEvent, autorenewPollMessage);
getDnsQueue().addDomainRefreshTask(domain.getFullyQualifiedDomainName()); getDnsQueue().addDomainRefreshTask(domain.getFullyQualifiedDomainName());
// Keep a list of "extra objects" that need to be saved along with the domain
// and add to it if necessary.
ImmutableSet<Object> extraEntitiesToSave =
getImportUtils().createIndexesForEppResource(domain);
// Create speculative server approval entities for pending transfers
if (domain.getTransferData().getTransferStatus() == TransferStatus.PENDING) {
TransferData transferData = domain.getTransferData();
checkArgumentNotNull(transferData,
"Domain %s is in pending transfer but has no transfer data",
domain.getFullyQualifiedDomainName());
Money transferCost = getDomainRenewCost(
domain.getFullyQualifiedDomainName(),
transferData.getPendingTransferExpirationTime(),
transferData.getExtendedRegistrationYears());
// Create speculative entities in anticipation of an automatic server approval.
ImmutableSet<TransferServerApproveEntity> serverApproveEntities =
createTransferServerApproveEntities(
transferData.getPendingTransferExpirationTime(),
domain.getRegistrationExpirationTime()
.plusYears(transferData.getExtendedRegistrationYears()),
historyEntry,
domain,
historyEntry.getTrid(),
transferData.getGainingClientId(),
transferCost,
transferData.getExtendedRegistrationYears(),
transferData.getTransferRequestTime());
transferData =
createPendingTransferData(transferData.asBuilder(), serverApproveEntities);
// Create a poll message to notify the losing registrar that a transfer was requested.
PollMessage requestPollMessage = createLosingTransferPollMessage(domain.getRepoId(),
transferData, transferData.getPendingTransferExpirationTime(), historyEntry)
.asBuilder().setEventTime(transferData.getTransferRequestTime()).build();
domain = domain.asBuilder().setTransferData(transferData).build();
autorenewBillingEvent = autorenewBillingEvent.asBuilder()
.setRecurrenceEndTime(transferData.getPendingTransferExpirationTime()).build();
autorenewPollMessage = autorenewPollMessage.asBuilder()
.setAutorenewEndTime(transferData.getPendingTransferExpirationTime()).build();
extraEntitiesToSave = new ImmutableSet.Builder<>()
.add(requestPollMessage)
.addAll(extraEntitiesToSave)
.addAll(serverApproveEntities).build();
} // End pending transfer check
ofy().save()
.entities(new ImmutableSet.Builder<>()
.add(domain, historyEntry, autorenewBillingEvent, autorenewPollMessage)
.addAll(extraEntitiesToSave)
.build())
.now();
} }
}); });
// Record the number of domains imported // Record the number of domains imported

View file

@ -125,7 +125,7 @@ public class RdeHostImportAction implements Runnable {
@Override @Override
public void vrun() { public void vrun() {
HostResource host = XjcToHostResourceConverter.convert(xjcHost); HostResource host = XjcToHostResourceConverter.convert(xjcHost);
getImportUtils().importHost(host); getImportUtils().importEppResource(host);
} }
}); });
// Record number of hosts imported // Record number of hosts imported

View file

@ -16,34 +16,47 @@ package google.registry.rde.imports;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.appengine.tools.cloudstorage.GcsFilename; import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils; import google.registry.gcs.GcsUtils;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource; import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.model.index.EppResourceIndex; import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.ofy.Ofy; import google.registry.model.ofy.Ofy;
import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.RegistryNotFoundException; import google.registry.model.registry.Registry.RegistryNotFoundException;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.reporting.HistoryEntry;
import google.registry.util.Clock; import google.registry.util.Clock;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import google.registry.xjc.XjcXmlTransformer;
import google.registry.xjc.rdedomain.XjcRdeDomain;
import google.registry.xjc.rdedomain.XjcRdeDomainElement;
import google.registry.xjc.rderegistrar.XjcRdeRegistrar; import google.registry.xjc.rderegistrar.XjcRdeRegistrar;
import google.registry.xml.XmlException;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.UUID; import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import org.joda.time.DateTime;
/** /**
* Utility functions for escrow file import. * Utility functions for escrow file import.
@ -66,10 +79,11 @@ public class RdeImportUtils {
this.escrowBucketName = escrowBucketName; this.escrowBucketName = escrowBucketName;
} }
private <T extends EppResource> void importEppResource(final T resource, final String type) { public <T extends EppResource & ForeignKeyedEppResource> ImmutableSet<Object>
createIndexesForEppResource(T resource) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Class<T> resourceClass = (Class<T>) resource.getClass(); Class<T> resourceClass = (Class<T>) resource.getClass();
EppResource existing = ofy.load().key(Key.create(resource)).now(); Object existing = ofy.load().key(Key.create(resource)).now();
if (existing != null) { if (existing != null) {
// This will roll back the transaction and prevent duplicate history entries from being saved. // This will roll back the transaction and prevent duplicate history entries from being saved.
throw new ResourceExistsException(); throw new ResourceExistsException();
@ -80,51 +94,30 @@ public class RdeImportUtils {
checkState( checkState(
existingForeignKeyIndex == null, existingForeignKeyIndex == null,
"New %s resource has existing foreign key index; foreignKey=%s, repoId=%s", "New %s resource has existing foreign key index; foreignKey=%s, repoId=%s",
type, resource.getClass().getCanonicalName(),
resource.getForeignKey(), resource.getForeignKey(),
resource.getRepoId()); resource.getRepoId());
ofy.save().entity(resource); return ImmutableSet.<Object>of(ForeignKeyIndex.create(resource, resource.getDeletionTime()),
ofy.save().entity(ForeignKeyIndex.create(resource, resource.getDeletionTime())); EppResourceIndex.create(Key.create(resource)));
ofy.save().entity(EppResourceIndex.create(Key.create(resource)));
logger.infofmt(
"Imported %s resource - ROID=%s, id=%s",
type, resource.getRepoId(), resource.getForeignKey());
} }
/** /**
* Imports a host from an escrow file. * Imports a resource from an escrow file.
* *
* <p>The host will only be imported if it has not been previously imported. * <p>The host will only be imported if it has not been previously imported.
* *
* <p>If the host is imported, {@link ForeignKeyIndex} and {@link EppResourceIndex} are also * <p>If the host is imported, {@link ForeignKeyIndex} and {@link EppResourceIndex} are also
* created. * created.
*/ */
public void importHost(final HostResource resource) { public <T extends EppResource & ForeignKeyedEppResource> void
importEppResource(resource, "host"); importEppResource(final T resource) {
} ofy.save().entities(new ImmutableSet.Builder<>()
.add(resource)
/** .addAll(createIndexesForEppResource(resource))
* Imports a contact from an escrow file. .build());
* logger.infofmt(
* <p>The contact will only be imported if it has not been previously imported. "Imported %s resource - ROID=%s, id=%s",
* resource.getClass().getCanonicalName(), resource.getRepoId(), resource.getForeignKey());
* <p>If the contact is imported, {@link ForeignKeyIndex} and {@link EppResourceIndex} are also
* created.
*/
public void importContact(final ContactResource resource) {
importEppResource(resource, "contact");
}
/**
* Imports a domain from an escrow file.
*
* <p>The domain will only be imported if it has not been previously imported.
*
* <p>If the domain is imported, {@link ForeignKeyIndex} and {@link EppResourceIndex} are also
* created.
*/
public void importDomain(final DomainResource resource) {
importEppResource(resource, "domain");
} }
/** /**
@ -184,4 +177,59 @@ public class RdeImportUtils {
return Trid.create( return Trid.create(
"Import_" + BaseEncoding.base64().encode(UUID.randomUUID().toString().getBytes())); "Import_" + BaseEncoding.base64().encode(UUID.randomUUID().toString().getBytes()));
} }
public static BillingEvent.Recurring createAutoRenewBillingEventForDomainImport(
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;
}
public static PollMessage.Autorenew createAutoRenewPollMessageForDomainImport(
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;
}
public static HistoryEntry createHistoryEntryForDomainImport(XjcRdeDomain domain) {
XjcRdeDomainElement element = new XjcRdeDomainElement(domain);
final HistoryEntry historyEntry =
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.RDE_IMPORT)
.setClientId(domain.getClID())
.setTrid(generateTridForImport())
.setModificationTime(DateTime.now())
.setXmlBytes(getObjectXml(element))
.setBySuperuser(true)
.setReason("RDE Import")
.setRequestedByRegistrar(false)
.setParent(Key.create(null, DomainResource.class, domain.getRoid()))
.build();
return historyEntry;
}
public static byte[] getObjectXml(Object jaxbElement) {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
XjcXmlTransformer.marshalLenient(jaxbElement, bout, UTF_8);
return bout.toByteArray();
} catch (XmlException e) {
throw new RuntimeException(e);
}
}
} }

View file

@ -17,9 +17,6 @@ package google.registry.rde.imports;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Iterables.transform;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.rde.imports.RdeImportUtils.generateTridForImport;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@ -29,10 +26,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent; 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.contact.ContactResource;
import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainAuthInfo; import google.registry.model.domain.DomainAuthInfo;
@ -47,7 +41,6 @@ import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registries; import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData; import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.util.NonFinalForTesting; import google.registry.util.NonFinalForTesting;
@ -67,6 +60,7 @@ import java.security.ProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Random; import java.util.Random;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Years;
/** Utility class that converts an {@link XjcRdeDomainElement} into a {@link DomainResource}. */ /** Utility class that converts an {@link XjcRdeDomainElement} into a {@link DomainResource}. */
final class XjcToDomainResourceConverter extends XjcToEppResourceConverter { final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
@ -184,14 +178,10 @@ final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
} }
/** Converts {@link XjcRdeDomain} to {@link DomainResource}. */ /** Converts {@link XjcRdeDomain} to {@link DomainResource}. */
static DomainResource convertDomain(XjcRdeDomain domain) { static DomainResource convertDomain(
// First create history entry and autorenew billing event/poll message XjcRdeDomain domain,
// Autorenew billing event is required for creating AUTO_RENEW grace period BillingEvent.Recurring autoRenewBillingEvent,
HistoryEntry historyEntry = createHistoryEntry(domain); PollMessage.Autorenew autoRenewPollMessage) {
BillingEvent.Recurring autoRenewBillingEvent =
createAutoRenewBillingEvent(domain, historyEntry);
PollMessage.Autorenew pollMessage = createAutoRenewPollMessage(domain, historyEntry);
ofy().save().<ImmutableObject>entities(historyEntry, autoRenewBillingEvent, pollMessage);
GracePeriodConverter gracePeriodConverter = GracePeriodConverter gracePeriodConverter =
new GracePeriodConverter(domain, Key.create(autoRenewBillingEvent)); new GracePeriodConverter(domain, Key.create(autoRenewBillingEvent));
DomainResource.Builder builder = DomainResource.Builder builder =
@ -202,7 +192,7 @@ final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
.setCurrentSponsorClientId(domain.getClID()) .setCurrentSponsorClientId(domain.getClID())
.setCreationClientId(domain.getCrRr().getValue()) .setCreationClientId(domain.getCrRr().getValue())
.setCreationTime(domain.getCrDate()) .setCreationTime(domain.getCrDate())
.setAutorenewPollMessage(Key.create(pollMessage)) .setAutorenewPollMessage(Key.create(autoRenewPollMessage))
.setAutorenewBillingEvent(Key.create(autoRenewBillingEvent)) .setAutorenewBillingEvent(Key.create(autoRenewBillingEvent))
.setRegistrationExpirationTime(domain.getExDate()) .setRegistrationExpirationTime(domain.getExDate())
.setLastEppUpdateTime(domain.getUpDate()) .setLastEppUpdateTime(domain.getUpDate())
@ -218,7 +208,7 @@ final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
? ImmutableSet.<DelegationSignerData>of() ? ImmutableSet.<DelegationSignerData>of()
: ImmutableSet.copyOf( : ImmutableSet.copyOf(
transform(domain.getSecDNS().getDsDatas(), SECDNS_CONVERTER))) transform(domain.getSecDNS().getDsDatas(), SECDNS_CONVERTER)))
.setTransferData(convertDomainTransferData(domain.getTrnData())) .setTransferData(convertDomainTransferData(domain.getTrnData(), domain.getExDate()))
// authInfo pw must be a token between 6 and 16 characters in length // authInfo pw must be a token between 6 and 16 characters in length
// generate a token of 16 characters as the default authInfo pw // generate a token of 16 characters as the default authInfo pw
.setAuthInfo(DomainAuthInfo .setAuthInfo(DomainAuthInfo
@ -229,51 +219,6 @@ final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
return builder.build(); 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(generateTridForImport())
.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 */ /** Returns {@link Key} for registrant from foreign key */
private static Key<ContactResource> convertRegistrant(String contactId) { private static Key<ContactResource> convertRegistrant(String contactId) {
Key<ContactResource> key = Key<ContactResource> key =
@ -296,16 +241,34 @@ final class XjcToDomainResourceConverter extends XjcToEppResourceConverter {
} }
/** Converts {@link XjcRdeDomainTransferDataType} to {@link TransferData}. */ /** Converts {@link XjcRdeDomainTransferDataType} to {@link TransferData}. */
private static TransferData convertDomainTransferData(XjcRdeDomainTransferDataType data) { private static TransferData convertDomainTransferData(XjcRdeDomainTransferDataType data,
DateTime domainExpiration) {
if (data == null) { if (data == null) {
return TransferData.EMPTY; return TransferData.EMPTY;
} }
// If the transfer is pending, calculate the number of years to add to the domain expiration
// on approval of the transfer.
TransferStatus transferStatus = TRANSFER_STATUS_MAPPER.xmlToEnum(data.getTrStatus().value());
// Get new expiration date
DateTime newExpirationTime = domainExpiration;
if (transferStatus == TransferStatus.PENDING) {
// Default to domain expiration time plus one year if no expiration is specified
if (data.getExDate() == null) {
newExpirationTime = newExpirationTime.plusYears(1);
} else {
newExpirationTime = data.getExDate();
}
}
return new TransferData.Builder() return new TransferData.Builder()
.setTransferStatus(TRANSFER_STATUS_MAPPER.xmlToEnum(data.getTrStatus().value())) .setTransferStatus(transferStatus)
.setGainingClientId(data.getReRr().getValue()) .setGainingClientId(data.getReRr().getValue())
.setLosingClientId(data.getAcRr().getValue()) .setLosingClientId(data.getAcRr().getValue())
.setTransferRequestTime(data.getReDate()) .setTransferRequestTime(data.getReDate())
.setPendingTransferExpirationTime(data.getAcDate()) .setPendingTransferExpirationTime(data.getAcDate())
// This will be wrong for domains that are not in pending transfer,
// but there isn't a reliable way to calculate it.
.setExtendedRegistrationYears(
Years.yearsBetween(domainExpiration, newExpirationTime).getYears())
.build(); .build();
} }

View file

@ -31,6 +31,7 @@ java_library(
"@com_google_truth", "@com_google_truth",
"@joda_time", "@joda_time",
"@junit", "@junit",
"@org_joda_money",
"@org_mockito_all", "@org_mockito_all",
], ],
) )

View file

@ -18,11 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.getHistoryEntries; import static google.registry.testing.DatastoreHelper.getHistoryEntries;
import static google.registry.testing.DatastoreHelper.getPollMessages;
import static google.registry.testing.DatastoreHelper.newDomainResource; import static google.registry.testing.DatastoreHelper.newDomainResource;
import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResource; import static google.registry.testing.DatastoreHelper.persistSimpleResource;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued; import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.Assert.fail;
import com.google.appengine.tools.cloudstorage.GcsFilename; import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsService; import com.google.appengine.tools.cloudstorage.GcsService;
@ -35,9 +39,13 @@ import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.ConfigModule; import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.gcs.GcsUtils; import google.registry.gcs.GcsUtils;
import google.registry.mapreduce.MapreduceRunner; import google.registry.mapreduce.MapreduceRunner;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
import google.registry.testing.mapreduce.MapreduceTestCase; import google.registry.testing.mapreduce.MapreduceTestCase;
@ -45,7 +53,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Seconds;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -56,6 +67,8 @@ import org.mockito.runners.MockitoJUnitRunner;
public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImportAction> { public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImportAction> {
private static final ByteSource DEPOSIT_1_DOMAIN = RdeImportsTestData.get("deposit_1_domain.xml"); private static final ByteSource DEPOSIT_1_DOMAIN = RdeImportsTestData.get("deposit_1_domain.xml");
private static final ByteSource DEPOSIT_1_DOMAIN_PENDING_TRANSFER =
RdeImportsTestData.get("deposit_1_domain_pending_transfer.xml");
private static final String IMPORT_BUCKET_NAME = "import-bucket"; private static final String IMPORT_BUCKET_NAME = "import-bucket";
private static final String IMPORT_FILE_NAME = "escrow-file.xml"; private static final String IMPORT_FILE_NAME = "escrow-file.xml";
@ -82,7 +95,7 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
} }
@Test @Test
public void test_mapreduceSuccessfullyImportsDomain() throws Exception { public void testMapreduceSuccessfullyImportsDomain() throws Exception {
pushToGcs(DEPOSIT_1_DOMAIN); pushToGcs(DEPOSIT_1_DOMAIN);
runMapreduce(); runMapreduce();
List<DomainResource> domains = ofy().load().type(DomainResource.class).list(); List<DomainResource> domains = ofy().load().type(DomainResource.class).list();
@ -91,7 +104,7 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
} }
@Test @Test
public void test_mapreduceSuccessfullyCreatesHistoryEntry() throws Exception { public void testMapreduceSuccessfullyCreatesHistoryEntry() throws Exception {
pushToGcs(DEPOSIT_1_DOMAIN); pushToGcs(DEPOSIT_1_DOMAIN);
runMapreduce(); runMapreduce();
List<DomainResource> domains = ofy().load().type(DomainResource.class).list(); List<DomainResource> domains = ofy().load().type(DomainResource.class).list();
@ -104,7 +117,7 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
/** Ensures that a second pass on a domain does not import a new domain. */ /** Ensures that a second pass on a domain does not import a new domain. */
@Test @Test
public void test_mapreduceTwiceDoesNotDuplicateResources() throws Exception { public void testMapreduceTwiceDoesNotDuplicateResources() throws Exception {
pushToGcs(DEPOSIT_1_DOMAIN); pushToGcs(DEPOSIT_1_DOMAIN);
// Create domain and history entry first // Create domain and history entry first
DomainResource existingDomain = DomainResource existingDomain =
@ -135,8 +148,147 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
assertDnsTasksEnqueued("example1.test"); assertDnsTasksEnqueued("example1.test");
} }
/**
* Verifies the state of an imported pending transfer before and after implicit server approval
*/
@Test
public void testMapreducePendingTransferServerApproval() throws Exception {
pushToGcs(DEPOSIT_1_DOMAIN_PENDING_TRANSFER);
runMapreduce();
List<DomainResource> domains = ofy().load().type(DomainResource.class).list();
assertThat(domains).hasSize(1);
checkDomain(domains.get(0));
// implicit server approval happens at 2015-01-08T22:00:00.0Z
DateTime serverApprovalTime = DateTime.parse("2015-01-08T22:00:00.0Z");
// Domain should be assigned to RegistrarX before server approval
DomainResource beforeApproval =
domains.get(0).cloneProjectedAtTime(serverApprovalTime.minus(Seconds.ONE));
assertThat(beforeApproval.getCurrentSponsorClientId()).isEqualTo("RegistrarX");
assertThat(loadAutorenewBillingEventForDomain(beforeApproval).getClientId())
.isEqualTo("RegistrarX");
assertThat(loadAutorenewPollMessageForDomain(beforeApproval).getClientId())
.isEqualTo("RegistrarX");
// Current expiration is 2015-04-03T22:00:00.0Z
assertThat(beforeApproval.getRegistrationExpirationTime())
.isEqualTo(DateTime.parse("2015-04-03T22:00:00.0Z"));
// Domain is not yet in transfer grace period
assertThat(beforeApproval.getGracePeriodStatuses()).doesNotContain(GracePeriodStatus.TRANSFER);
// Check autorenew events - recurrence end is set to transfer approval time,
// and client id is set to losing registrar. This is important in case the
// transfer is cancelled or rejected.
// Event time is set to domain expiration date.
checkAutorenewBillingEvent(
beforeApproval,
"RegistrarX",
DateTime.parse("2015-04-03T22:00:00.0Z"),
DateTime.parse("2015-01-08T22:00:00.0Z"));
checkAutorenewPollMessage(
beforeApproval,
"RegistrarX",
DateTime.parse("2015-04-03T22:00:00.0Z"),
DateTime.parse("2015-01-08T22:00:00.0Z"));
// Domain should be assigned to RegistrarY after server approval
DomainResource afterApproval =
domains.get(0).cloneProjectedAtTime(serverApprovalTime.plus(Seconds.ONE));
assertThat(afterApproval.getCurrentSponsorClientId()).isEqualTo("RegistrarY");
assertThat(loadAutorenewBillingEventForDomain(afterApproval).getClientId())
.isEqualTo("RegistrarY");
assertThat(loadAutorenewPollMessageForDomain(afterApproval).getClientId())
.isEqualTo("RegistrarY");
// New expiration should be incremented by 1 year
assertThat(afterApproval.getRegistrationExpirationTime())
.isEqualTo(DateTime.parse("2016-04-03T22:00:00.0Z"));
// Domain should now be in transfer grace period
assertThat(afterApproval.getGracePeriodStatuses()).contains(GracePeriodStatus.TRANSFER);
// Check autorenew events - recurrence end is set to END_OF_TIME,
// and client id is set to gaining registrar. This represents the new state of the domain,
// unless the transfer is cancelled during the grace period, at which time it will
// revert to the previous state.
// Event time is set to domain expiration date.
checkAutorenewBillingEvent(
afterApproval, "RegistrarY", DateTime.parse("2016-04-03T22:00:00.0Z"), END_OF_TIME);
checkAutorenewPollMessage(
afterApproval, "RegistrarY", DateTime.parse("2016-04-03T22:00:00.0Z"), END_OF_TIME);
}
@Test
public void testMapreducePendingTransferEvents() throws Exception {
pushToGcs(DEPOSIT_1_DOMAIN_PENDING_TRANSFER);
runMapreduce();
List<DomainResource> domains = ofy().load().type(DomainResource.class).list();
assertThat(domains).hasSize(1);
checkDomain(domains.get(0));
checkTransferRequestPollMessage(domains.get(0), "RegistrarX",
DateTime.parse("2015-01-03T22:00:00.0Z"));
checkTransferServerApprovalPollMessage(domains.get(0), "RegistrarX",
DateTime.parse("2015-01-08T22:00:00.0Z"));
checkTransferServerApprovalPollMessage(domains.get(0), "RegistrarY",
DateTime.parse("2015-01-08T22:00:00.0Z"));
// Billing event is set to the end of the transfer grace period, 5 days after server approval
checkTransferBillingEvent(domains.get(0), DateTime.parse("2015-01-13T22:00:00.0Z"));
}
private static void checkTransferBillingEvent(
DomainResource domain, DateTime automaticTransferTime) {
for (BillingEvent.OneTime event :
ofy().load().type(BillingEvent.OneTime.class).ancestor(domain).list()) {
if (event.getReason() == BillingEvent.Reason.TRANSFER) {
assertThat(event.getCost()).isEqualTo(Money.of(USD, 11));
assertThat(event.getClientId()).isEqualTo("RegistrarY");
assertThat(event.getBillingTime()).isEqualTo(automaticTransferTime);
}
}
}
/** Verifies the existence of a transfer request poll message */
private static void checkTransferRequestPollMessage(
DomainResource domain, String clientId, DateTime expectedAt) {
for (PollMessage message : getPollMessages(domain)) {
if (TransferStatus.PENDING.getMessage().equals(message.getMsg())
&& clientId.equals(message.getClientId())
&& expectedAt.equals(message.getEventTime())) {
return;
}
}
fail("Expected transfer request poll message");
}
/** Verifies the existence of a transfer request poll message */
private static void checkTransferServerApprovalPollMessage(
DomainResource domain, String clientId, DateTime expectedAt) {
for (PollMessage message : getPollMessages(domain)) {
if (TransferStatus.SERVER_APPROVED.getMessage().equals(message.getMsg())
&& clientId.equals(message.getClientId())
&& expectedAt.equals(message.getEventTime())) {
return;
}
}
fail("Expected transfer request poll message");
}
/** Verifies autorenew {@link PollMessage} is correct */
private static void checkAutorenewPollMessage(
DomainResource domain, String clientId, DateTime expectedAt, DateTime recurrenceEndTime) {
PollMessage.Autorenew autorenewPollMessage = loadAutorenewPollMessageForDomain(domain);
assertThat(autorenewPollMessage).isNotNull();
assertThat(autorenewPollMessage.getClientId()).isEqualTo(clientId);
assertThat(autorenewPollMessage.getEventTime()).isEqualTo(expectedAt);
assertThat(autorenewPollMessage.getAutorenewEndTime()).isEqualTo(recurrenceEndTime);
}
/** Verifies autorenew {@link BillingEvent} is correct */
private static void checkAutorenewBillingEvent(
DomainResource domain, String clientId, DateTime expectedAt, DateTime recurrenceEndTime) {
BillingEvent.Recurring autorenewBillingEvent = loadAutorenewBillingEventForDomain(domain);
assertThat(autorenewBillingEvent).isNotNull();
assertThat(autorenewBillingEvent.getClientId()).isEqualTo(clientId);
assertThat(autorenewBillingEvent.getEventTime()).isEqualTo(expectedAt);
assertThat(autorenewBillingEvent.getRecurrenceEndTime()).isEqualTo(recurrenceEndTime);
}
/** Verify history entry fields are correct */ /** Verify history entry fields are correct */
private void checkHistoryEntry(HistoryEntry entry, DomainResource parent) { private static void checkHistoryEntry(HistoryEntry entry, DomainResource parent) {
assertThat(entry.getType()).isEqualTo(HistoryEntry.Type.RDE_IMPORT); assertThat(entry.getType()).isEqualTo(HistoryEntry.Type.RDE_IMPORT);
assertThat(entry.getClientId()).isEqualTo(parent.getCurrentSponsorClientId()); assertThat(entry.getClientId()).isEqualTo(parent.getCurrentSponsorClientId());
assertThat(entry.getXmlBytes().length).isGreaterThan(0); assertThat(entry.getXmlBytes().length).isGreaterThan(0);
@ -146,8 +298,8 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
assertThat(entry.getParent()).isEqualTo(Key.create(parent)); assertThat(entry.getParent()).isEqualTo(Key.create(parent));
} }
/** Verifies that domain id and ROID match expected values */ /** Verifies that domain fields match expected values */
private void checkDomain(DomainResource domain) { private static void checkDomain(DomainResource domain) {
assertThat(domain.getFullyQualifiedDomainName()).isEqualTo("example1.test"); assertThat(domain.getFullyQualifiedDomainName()).isEqualTo("example1.test");
assertThat(domain.getRepoId()).isEqualTo("Dexample1-TEST"); assertThat(domain.getRepoId()).isEqualTo("Dexample1-TEST");
} }
@ -174,6 +326,16 @@ public class RdeDomainImportActionTest extends MapreduceTestCase<RdeDomainImport
return result; return result;
} }
@Nullable
private static BillingEvent.Recurring loadAutorenewBillingEventForDomain(DomainResource domain) {
return ofy().load().key(domain.getAutorenewBillingEvent()).now();
}
@Nullable
private static PollMessage.Autorenew loadAutorenewPollMessageForDomain(DomainResource domain) {
return ofy().load().key(domain.getAutorenewPollMessage()).now();
}
private static HistoryEntry createHistoryEntry(String roid, String clid, byte[] objectXml) { private static HistoryEntry createHistoryEntry(String roid, String clid, byte[] objectXml) {
return new HistoryEntry.Builder() return new HistoryEntry.Builder()
.setType(HistoryEntry.Type.RDE_IMPORT) .setType(HistoryEntry.Type.RDE_IMPORT)

View file

@ -114,7 +114,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importContact(newContact); rdeImportUtils.importEppResource(newContact);
}}); }});
assertEppResourceIndexEntityFor(newContact); assertEppResourceIndexEntityFor(newContact);
assertForeignKeyIndexFor(newContact); assertForeignKeyIndexFor(newContact);
@ -141,7 +141,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importContact(updatedContact); rdeImportUtils.importEppResource(updatedContact);
}}); }});
fail("Expected ResourceExistsException"); fail("Expected ResourceExistsException");
} catch (ResourceExistsException expected) { } catch (ResourceExistsException expected) {
@ -161,7 +161,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importHost(newHost); rdeImportUtils.importEppResource(newHost);
}}); }});
assertEppResourceIndexEntityFor(newHost); assertEppResourceIndexEntityFor(newHost);
@ -189,7 +189,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importHost(updatedHost); rdeImportUtils.importEppResource(updatedHost);
}}); }});
fail("Expected ResourceExistsException"); fail("Expected ResourceExistsException");
} catch (ResourceExistsException expected) { } catch (ResourceExistsException expected) {
@ -208,7 +208,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importDomain(newDomain); rdeImportUtils.importEppResource(newDomain);
} }
}); });
@ -236,7 +236,7 @@ public class RdeImportUtilsTest extends ShardableTestCase {
ofy().transact(new VoidWork() { ofy().transact(new VoidWork() {
@Override @Override
public void vrun() { public void vrun() {
rdeImportUtils.importDomain(updatedDomain); rdeImportUtils.importEppResource(updatedDomain);
}}); }});
fail("Expected ResourceExistsException"); fail("Expected ResourceExistsException");
} catch (ResourceExistsException expected) { } catch (ResourceExistsException expected) {

View file

@ -18,6 +18,9 @@ import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.rde.imports.RdeImportTestUtils.checkTrid; import static google.registry.rde.imports.RdeImportTestUtils.checkTrid;
import static google.registry.rde.imports.RdeImportUtils.createAutoRenewBillingEventForDomainImport;
import static google.registry.rde.imports.RdeImportUtils.createAutoRenewPollMessageForDomainImport;
import static google.registry.rde.imports.RdeImportUtils.createHistoryEntryForDomainImport;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.getHistoryEntries; import static google.registry.testing.DatastoreHelper.getHistoryEntries;
import static google.registry.testing.DatastoreHelper.persistActiveContact; import static google.registry.testing.DatastoreHelper.persistActiveContact;
@ -44,6 +47,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.ExceptionRule; import google.registry.testing.ExceptionRule;
@ -368,12 +372,69 @@ public class XjcToDomainResourceConverterTest {
assertThat(pollMessage.getMsg()).isEqualTo("Domain was auto-renewed."); assertThat(pollMessage.getMsg()).isEqualTo("Domain was auto-renewed.");
} }
@Test
public void testConvertDomainResourcePendingTransfer() throws Exception {
persistActiveContact("jd1234");
persistActiveContact("sh8013");
final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pending_transfer.xml");
DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain));
assertThat(domain.getTransferData()).isNotNull();
assertThat(domain.getTransferData().getTransferStatus()).isEqualTo(TransferStatus.PENDING);
assertThat(domain.getTransferData().getGainingClientId()).isEqualTo("RegistrarY");
assertThat(domain.getTransferData().getTransferRequestTime())
.isEqualTo(DateTime.parse("2015-01-03T22:00:00.0Z"));
assertThat(domain.getTransferData().getLosingClientId()).isEqualTo("RegistrarX");
assertThat(domain.getTransferData().getPendingTransferExpirationTime())
.isEqualTo(DateTime.parse("2015-01-08T22:00:00.0Z"));
}
@Test
public void testConvertDomainResourcePendingTransferDefaultExtendedYears() throws Exception {
persistActiveContact("jd1234");
persistActiveContact("sh8013");
final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pending_transfer.xml");
DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain));
assertThat(domain.getTransferData()).isNotNull();
assertThat(domain.getTransferData().getExtendedRegistrationYears()).isEqualTo(1);
}
@Test
public void testConvertDomainResourcePendingTransferExtendOneYear() throws Exception {
persistActiveContact("jd1234");
persistActiveContact("sh8013");
final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pending_transfer_1yr.xml");
DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain));
assertThat(domain.getTransferData()).isNotNull();
assertThat(domain.getTransferData().getExtendedRegistrationYears()).isEqualTo(1);
}
@Test
public void testConvertDomainResourcePendingTransferExtendTwoYears() throws Exception {
persistActiveContact("jd1234");
persistActiveContact("sh8013");
final XjcRdeDomain xjcDomain = loadDomainFromRdeXml("domain_fragment_pending_transfer_2yr.xml");
DomainResource domain = persistResource(convertDomainInTransaction(xjcDomain));
assertThat(domain.getTransferData()).isNotNull();
assertThat(domain.getTransferData().getExtendedRegistrationYears()).isEqualTo(2);
}
private static DomainResource convertDomainInTransaction(final XjcRdeDomain xjcDomain) { private static DomainResource convertDomainInTransaction(final XjcRdeDomain xjcDomain) {
return ofy().transact(new Work<DomainResource>() { final HistoryEntry historyEntry = createHistoryEntryForDomainImport(xjcDomain);
final BillingEvent.Recurring autorenewBillingEvent =
createAutoRenewBillingEventForDomainImport(xjcDomain, historyEntry);
final PollMessage.Autorenew autorenewPollMessage =
createAutoRenewPollMessageForDomainImport(xjcDomain, historyEntry);
return ofy()
.transact(
new Work<DomainResource>() {
@SuppressWarnings("unchecked")
@Override @Override
public DomainResource run() { public DomainResource run() {
return XjcToDomainResourceConverter.convertDomain(xjcDomain); ofy().save().entities(historyEntry, autorenewBillingEvent, autorenewPollMessage);
}}); return XjcToDomainResourceConverter.convertDomain(
xjcDomain, autorenewBillingEvent, autorenewPollMessage);
}
});
} }
private XjcRdeDomain loadDomainFromRdeXml(String filename) { private XjcRdeDomain loadDomainFromRdeXml(String filename) {

View file

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="UTF-8"?>
<rde:deposit type="FULL" id="20101017001" prevId="20101010001"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0"
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1"
xmlns:rde="urn:ietf:params:xml:ns:rde-1.0"
xmlns:rdeHeader="urn:ietf:params:xml:ns:rdeHeader-1.0"
xmlns:rdeDom="urn:ietf:params:xml:ns:rdeDomain-1.0"
xmlns:rdeHost="urn:ietf:params:xml:ns:rdeHost-1.0"
xmlns:rdeContact="urn:ietf:params:xml:ns:rdeContact-1.0"
xmlns:rdeRegistrar="urn:ietf:params:xml:ns:rdeRegistrar-1.0"
xmlns:rdeIDN="urn:ietf:params:xml:ns:rdeIDN-1.0"
xmlns:rdeNNDN="urn:ietf:params:xml:ns:rdeNNDN-1.0"
xmlns:rdeEppParams="urn:ietf:params:xml:ns:rdeEppParams-1.0"
xmlns:rdePolicy="urn:ietf:params:xml:ns:rdePolicy-1.0"
xmlns:epp="urn:ietf:params:xml:ns:epp-1.0">
<rde:watermark>2010-10-17T00:00:00Z</rde:watermark>
<rde:rdeMenu>
<rde:version>1.0</rde:version>
<rde:objURI>urn:ietf:params:xml:ns:rdeHeader-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeContact-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeHost-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeDomain-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeRegistrar-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeIDN-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeNNDN-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeEppParams-1.0</rde:objURI>
</rde:rdeMenu>
<!-- Contents -->
<rde:contents>
<!-- Header -->
<rdeHeader:header>
<rdeHeader:tld>test</rdeHeader:tld>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeDomain-1.0">2
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeHost-1.0">1
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeContact-1.0">1
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeRegistrar-1.0">1
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeIDN-1.0">1
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeNNDN-1.0">1
</rdeHeader:count>
<rdeHeader:count
uri="urn:ietf:params:xml:ns:rdeEppParams-1.0">1
</rdeHeader:count>
</rdeHeader:header>
<!-- Domain: example1.test -->
<rdeDom:domain>
<rdeDom:name>example1.test</rdeDom:name>
<rdeDom:roid>Dexample1-TEST</rdeDom:roid>
<rdeDom:status s="ok"/>
<rdeDom:registrant>jd1234</rdeDom:registrant>
<rdeDom:contact type="admin">sh8013</rdeDom:contact>
<rdeDom:contact type="tech">sh8013</rdeDom:contact>
<rdeDom:clID>RegistrarX</rdeDom:clID>
<rdeDom:crRr client="jdoe">RegistrarX</rdeDom:crRr>
<rdeDom:crDate>1999-04-03T22:00:00.0Z</rdeDom:crDate>
<rdeDom:exDate>2015-04-03T22:00:00.0Z</rdeDom:exDate>
<rdeDom:upRr>RegistrarY</rdeDom:upRr>
<rdeDom:upDate>2015-01-03T22:00:00.0Z</rdeDom:upDate>
<rdeDom:trnData>
<rdeDom:trStatus>pending</rdeDom:trStatus>
<rdeDom:reRr>RegistrarY</rdeDom:reRr>
<rdeDom:reDate>2015-01-03T22:00:00.0Z</rdeDom:reDate>
<rdeDom:acRr>RegistrarX</rdeDom:acRr>
<rdeDom:acDate>2015-01-08T22:00:00.0Z</rdeDom:acDate>
</rdeDom:trnData>
</rdeDom:domain>
<!-- Host: ns1.example.com -->
<rdeHost:host>
<rdeHost:name>ns1.example.com</rdeHost:name>
<rdeHost:roid>Hns1_example_com-TEST</rdeHost:roid>
<rdeHost:status s="ok"/>
<rdeHost:status s="linked"/>
<rdeHost:addr ip="v4">192.0.2.2</rdeHost:addr>
<rdeHost:addr ip="v4">192.0.2.29</rdeHost:addr>
<rdeHost:addr ip="v6">1080:0:0:0:8:800:200C:417A
</rdeHost:addr>
<rdeHost:clID>RegistrarX</rdeHost:clID>
<rdeHost:crRr>RegistrarX</rdeHost:crRr>
<rdeHost:crDate>1999-05-08T12:10:00.0Z</rdeHost:crDate>
<rdeHost:upRr>RegistrarX</rdeHost:upRr>
<rdeHost:upDate>2009-10-03T09:34:00.0Z</rdeHost:upDate>
</rdeHost:host>
<!-- Host: ns1.example1.test -->
<rdeHost:host>
<rdeHost:name>ns1.example1.test</rdeHost:name>
<rdeHost:roid>Hns1_example1_test-TEST</rdeHost:roid>
<rdeHost:status s="ok"/>
<rdeHost:status s="linked"/>
<rdeHost:addr ip="v4">192.0.2.2</rdeHost:addr>
<rdeHost:addr ip="v4">192.0.2.29</rdeHost:addr>
<rdeHost:addr ip="v6">1080:0:0:0:8:800:200C:417A
</rdeHost:addr>
<rdeHost:clID>RegistrarX</rdeHost:clID>
<rdeHost:crRr>RegistrarX</rdeHost:crRr>
<rdeHost:crDate>1999-05-08T12:10:00.0Z</rdeHost:crDate>
<rdeHost:upRr>RegistrarX</rdeHost:upRr>
<rdeHost:upDate>2009-10-03T09:34:00.0Z</rdeHost:upDate>
</rdeHost:host>
<!-- Contact: sh8013 -->
<rdeContact:contact>
<rdeContact:id>sh8013</rdeContact:id>
<rdeContact:roid>Csh8013-TEST</rdeContact:roid>
<rdeContact:status s="linked"/>
<rdeContact:status s="clientDeleteProhibited"/>
<rdeContact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</rdeContact:postalInfo>
<rdeContact:voice x="1234">+1.7035555555
</rdeContact:voice>
<rdeContact:fax>+1.7035555556
</rdeContact:fax>
<rdeContact:email>jdoe@example.test
</rdeContact:email>
<rdeContact:clID>RegistrarX</rdeContact:clID>
<rdeContact:crRr client="jdoe">RegistrarX
</rdeContact:crRr>
<rdeContact:crDate>2009-09-13T08:01:00.0Z</rdeContact:crDate>
<rdeContact:upRr client="jdoe">RegistrarX
</rdeContact:upRr>
<rdeContact:upDate>2009-11-26T09:10:00.0Z</rdeContact:upDate>
<rdeContact:trDate>2009-12-03T09:05:00.0Z</rdeContact:trDate>
<rdeContact:disclose flag="0">
<contact:voice/>
<contact:email/>
</rdeContact:disclose>
</rdeContact:contact>
<!-- Registrar: RegistrarX -->
<rdeRegistrar:registrar>
<rdeRegistrar:id>RegistrarX</rdeRegistrar:id>
<rdeRegistrar:name>Registrar X</rdeRegistrar:name>
<rdeRegistrar:gurid>123</rdeRegistrar:gurid>
<rdeRegistrar:status>ok</rdeRegistrar:status>
<rdeRegistrar:postalInfo type="int">
<rdeRegistrar:addr>
<rdeRegistrar:street>123 Example Dr.
</rdeRegistrar:street>
<rdeRegistrar:street>Suite 100
</rdeRegistrar:street>
<rdeRegistrar:city>Dulles</rdeRegistrar:city>
<rdeRegistrar:sp>VA</rdeRegistrar:sp>
<rdeRegistrar:pc>20166-6503</rdeRegistrar:pc>
<rdeRegistrar:cc>US</rdeRegistrar:cc>
</rdeRegistrar:addr>
</rdeRegistrar:postalInfo>
<rdeRegistrar:voice x="1234">+1.7035555555
</rdeRegistrar:voice>
<rdeRegistrar:fax>+1.7035555556
</rdeRegistrar:fax>
<rdeRegistrar:email>jdoe@example.test
</rdeRegistrar:email>
<rdeRegistrar:url>http://www.example.test
</rdeRegistrar:url>
<rdeRegistrar:whoisInfo>
<rdeRegistrar:name>whois.example.test
</rdeRegistrar:name>
<rdeRegistrar:url>http://whois.example.test
</rdeRegistrar:url>
</rdeRegistrar:whoisInfo>
<rdeRegistrar:crDate>2005-04-23T11:49:00.0Z</rdeRegistrar:crDate>
<rdeRegistrar:upDate>2009-02-17T17:51:00.0Z</rdeRegistrar:upDate>
</rdeRegistrar:registrar>
<!-- IDN Table -->
<rdeIDN:idnTableRef id="pt-BR">
<rdeIDN:url>
http://www.iana.org/domains/idn-tables/tables/br_pt-br_1.0.html
</rdeIDN:url>
<rdeIDN:urlPolicy>
http://registro.br/dominio/regras.html
</rdeIDN:urlPolicy>
</rdeIDN:idnTableRef>
<!-- NNDN: pinguino.test -->
<rdeNNDN:NNDN>
<rdeNNDN:aName>xn--exampl-gva.test</rdeNNDN:aName>
<rdeNNDN:idnTableId>pt-BR</rdeNNDN:idnTableId>
<rdeNNDN:originalName>example1.test</rdeNNDN:originalName>
<rdeNNDN:nameState>withheld</rdeNNDN:nameState>
<rdeNNDN:crDate>2005-04-23T11:49:00.0Z</rdeNNDN:crDate>
</rdeNNDN:NNDN>
<!-- EppParams -->
<rdeEppParams:eppParams>
<rdeEppParams:version>1.0</rdeEppParams:version>
<rdeEppParams:lang>en</rdeEppParams:lang>
<rdeEppParams:objURI>
urn:ietf:params:xml:ns:domain-1.0
</rdeEppParams:objURI>
<rdeEppParams:objURI>
urn:ietf:params:xml:ns:contact-1.0
</rdeEppParams:objURI>
<rdeEppParams:objURI>
urn:ietf:params:xml:ns:host-1.0
</rdeEppParams:objURI>
<rdeEppParams:svcExtension>
<epp:extURI>urn:ietf:params:xml:ns:rgp-1.0
</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:secDNS-1.1
</epp:extURI>
</rdeEppParams:svcExtension>
<rdeEppParams:dcp>
<epp:access><epp:all/></epp:access>
<epp:statement>
<epp:purpose>
<epp:admin/>
<epp:prov/>
</epp:purpose>
<epp:recipient>
<epp:ours/>
<epp:public/>
</epp:recipient>
<epp:retention>
<epp:stated/>
</epp:retention>
</epp:statement>
</rdeEppParams:dcp>
</rdeEppParams:eppParams>
<rdePolicy:policy
scope="//rde:deposit/rde:contents/rdeDomain:domain"
element="rdeDom:registrant" />
</rde:contents>
</rde:deposit>

View file

@ -0,0 +1,23 @@
<rdeDom:domain
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
xmlns:rdeDom="urn:ietf:params:xml:ns:rdeDomain-1.0">
<rdeDom:name>example1.example</rdeDom:name>
<rdeDom:roid>Dexample1-TEST</rdeDom:roid>
<rdeDom:status s="pendingTransfer"/>
<rdeDom:registrant>jd1234</rdeDom:registrant>
<rdeDom:contact type="admin">sh8013</rdeDom:contact>
<rdeDom:contact type="tech">sh8013</rdeDom:contact>
<rdeDom:clID>RegistrarX</rdeDom:clID>
<rdeDom:crRr client="jdoe">RegistrarX</rdeDom:crRr>
<rdeDom:crDate>1999-04-03T22:00:00.0Z</rdeDom:crDate>
<rdeDom:exDate>2015-04-03T22:00:00.0Z</rdeDom:exDate>
<rdeDom:upRr>RegistrarY</rdeDom:upRr>
<rdeDom:upDate>2015-01-03T22:00:00.0Z</rdeDom:upDate>
<rdeDom:trnData>
<rdeDom:trStatus>pending</rdeDom:trStatus>
<rdeDom:reRr>RegistrarY</rdeDom:reRr>
<rdeDom:reDate>2015-01-03T22:00:00.0Z</rdeDom:reDate>
<rdeDom:acRr>RegistrarX</rdeDom:acRr>
<rdeDom:acDate>2015-01-08T22:00:00.0Z</rdeDom:acDate>
</rdeDom:trnData>
</rdeDom:domain>

View file

@ -0,0 +1,24 @@
<rdeDom:domain
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
xmlns:rdeDom="urn:ietf:params:xml:ns:rdeDomain-1.0">
<rdeDom:name>example1.example</rdeDom:name>
<rdeDom:roid>Dexample1-TEST</rdeDom:roid>
<rdeDom:status s="pendingTransfer"/>
<rdeDom:registrant>jd1234</rdeDom:registrant>
<rdeDom:contact type="admin">sh8013</rdeDom:contact>
<rdeDom:contact type="tech">sh8013</rdeDom:contact>
<rdeDom:clID>RegistrarX</rdeDom:clID>
<rdeDom:crRr client="jdoe">RegistrarX</rdeDom:crRr>
<rdeDom:crDate>1999-04-03T22:00:00.0Z</rdeDom:crDate>
<rdeDom:exDate>2015-04-03T22:00:00.0Z</rdeDom:exDate>
<rdeDom:upRr>RegistrarY</rdeDom:upRr>
<rdeDom:upDate>2015-01-03T22:00:00.0Z</rdeDom:upDate>
<rdeDom:trnData>
<rdeDom:trStatus>pending</rdeDom:trStatus>
<rdeDom:reRr>RegistrarY</rdeDom:reRr>
<rdeDom:reDate>2015-01-03T22:00:00.0Z</rdeDom:reDate>
<rdeDom:acRr>RegistrarX</rdeDom:acRr>
<rdeDom:acDate>2015-01-08T22:00:00.0Z</rdeDom:acDate>
<rdeDom:exDate>2016-04-03T22:00:00.0Z</rdeDom:exDate>
</rdeDom:trnData>
</rdeDom:domain>

View file

@ -0,0 +1,24 @@
<rdeDom:domain
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
xmlns:rdeDom="urn:ietf:params:xml:ns:rdeDomain-1.0">
<rdeDom:name>example1.example</rdeDom:name>
<rdeDom:roid>Dexample1-TEST</rdeDom:roid>
<rdeDom:status s="pendingTransfer"/>
<rdeDom:registrant>jd1234</rdeDom:registrant>
<rdeDom:contact type="admin">sh8013</rdeDom:contact>
<rdeDom:contact type="tech">sh8013</rdeDom:contact>
<rdeDom:clID>RegistrarX</rdeDom:clID>
<rdeDom:crRr client="jdoe">RegistrarX</rdeDom:crRr>
<rdeDom:crDate>1999-04-03T22:00:00.0Z</rdeDom:crDate>
<rdeDom:exDate>2015-04-03T22:00:00.0Z</rdeDom:exDate>
<rdeDom:upRr>RegistrarY</rdeDom:upRr>
<rdeDom:upDate>2015-01-03T22:00:00.0Z</rdeDom:upDate>
<rdeDom:trnData>
<rdeDom:trStatus>pending</rdeDom:trStatus>
<rdeDom:reRr>RegistrarY</rdeDom:reRr>
<rdeDom:reDate>2015-01-03T22:00:00.0Z</rdeDom:reDate>
<rdeDom:acRr>RegistrarX</rdeDom:acRr>
<rdeDom:acDate>2015-01-08T22:00:00.0Z</rdeDom:acDate>
<rdeDom:exDate>2017-04-03T22:00:00.0Z</rdeDom:exDate>
</rdeDom:trnData>
</rdeDom:domain>