Record domain transaction for domain transfers

This is the last of many cls adding explicit logging in all our domain
mutation flows to facilitate transaction reporting.

The transfer process is as follows:
GAINING sends a TransferRequest to LOSING
LOSING either acks (TransferApprove), nacks (TransferReject) or does nothing
(auto approve). For acks and autoapproves, we produce a +1 counter for GAINING
and LOSING for domain-gaining/losing-successful for each registrar, to be
reported on the approve date + the transfer grace period. For nacks, we produce
a +1 counter for domain-gaining/losing-nacked for each registrar.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166535579
This commit is contained in:
larryruili 2017-07-13 15:42:35 -04:00 committed by Ben McIlwain
parent 7ee8bc9070
commit 16e8286dca
15 changed files with 319 additions and 95 deletions

View file

@ -160,7 +160,7 @@ public class DomainAllocateFlow implements TransactionalFlow {
String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr());
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
HistoryEntry historyEntry = buildHistoryEntry(
repoId, period, now, registry.getAddGracePeriodLength(), registry.getTldStr());
repoId, registry.getTldStr(), now, period, registry.getAddGracePeriodLength());
entitiesToSave.add(historyEntry);
ImmutableSet<? extends ImmutableObject> billsAndPolls = createBillingEventsAndPollMessages(
domainName, application, historyEntry, isSunrushAddGracePeriod, registry, now, years);
@ -234,7 +234,7 @@ public class DomainAllocateFlow implements TransactionalFlow {
}
private HistoryEntry buildHistoryEntry(
String repoId, Period period, DateTime now, Duration addGracePeriod, String tld) {
String repoId, String tld, DateTime now, Period period, Duration addGracePeriod) {
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_ALLOCATE)
.setPeriod(period)

View file

@ -262,7 +262,7 @@ public class DomainCreateFlow implements TransactionalFlow {
String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr());
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
HistoryEntry historyEntry = buildHistoryEntry(
repoId, period, now, registry.getAddGracePeriodLength(), registry.getTldStr());
repoId, registry.getTldStr(), now, period, registry.getAddGracePeriodLength());
// Bill for the create.
BillingEvent.OneTime createBillingEvent =
createOneTimeBillingEvent(
@ -365,7 +365,7 @@ public class DomainCreateFlow implements TransactionalFlow {
}
private HistoryEntry buildHistoryEntry(
String repoId, Period period, DateTime now, Duration addGracePeriod, String tld) {
String repoId, String tld, DateTime now, Period period, Duration addGracePeriod) {
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(period)

View file

@ -24,22 +24,22 @@ import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.createCancellingRecords;
import static google.registry.flows.domain.DomainFlowUtils.createCancelingRecords;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.isAddsField;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.isRenewsField;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.ADD_FIELDS;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.RENEW_FIELDS;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.union;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.dns.DnsQueue;
import google.registry.flows.EppException;
@ -245,17 +245,11 @@ public final class DomainDeleteFlow implements TransactionalFlow {
registry.getAutoRenewGracePeriodLength(),
registry.getRenewGracePeriodLength()));
ImmutableSet<DomainTransactionRecord> cancelledRecords =
createCancellingRecords(
createCancelingRecords(
existingResource,
now,
maxGracePeriod,
new Predicate<DomainTransactionRecord>() {
@Override
public boolean apply(@Nullable DomainTransactionRecord domainTransactionRecord) {
return isAddsField(domainTransactionRecord.getReportField())
|| isRenewsField(domainTransactionRecord.getReportField());
}
});
Sets.immutableEnumSet(Sets.union(ADD_FIELDS, RENEW_FIELDS)));
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_DELETE)
.setModificationTime(now)

View file

@ -108,6 +108,7 @@ import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservationType;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tmch.ClaimsListShard;
import google.registry.util.FormattingLogger;
@ -925,11 +926,11 @@ public class DomainFlowUtils {
* hasn't been reported yet and b) matches the predicate 3. Return the transactionRecords under
* the most recent HistoryEntry that fits the above criteria, with negated reportAmounts.
*/
static ImmutableSet<DomainTransactionRecord> createCancellingRecords(
static ImmutableSet<DomainTransactionRecord> createCancelingRecords(
DomainResource domainResource,
final DateTime now,
Duration maxSearchPeriod,
final Predicate<DomainTransactionRecord> isCancellable) {
final ImmutableSet<TransactionReportField> cancelableFields) {
List<HistoryEntry> recentHistoryEntries = ofy().load()
.type(HistoryEntry.class)
@ -937,22 +938,25 @@ public class DomainFlowUtils {
.filter("modificationTime >=", now.minus(maxSearchPeriod))
.order("modificationTime")
.list();
Optional<HistoryEntry> entryToCancel = FluentIterable.from(recentHistoryEntries)
.filter(
new Predicate<HistoryEntry>() {
@Override
public boolean apply(HistoryEntry historyEntry) {
// Look for add and renew transaction records that have yet to be reported
for (DomainTransactionRecord record : historyEntry.getDomainTransactionRecords()) {
if (isCancellable.apply(record) && record.getReportingTime().isAfter(now)) {
return true;
Optional<HistoryEntry> entryToCancel =
FluentIterable.from(recentHistoryEntries)
.filter(
new Predicate<HistoryEntry>() {
@Override
public boolean apply(HistoryEntry historyEntry) {
// Look for add and renew transaction records that have yet to be reported
for (DomainTransactionRecord record :
historyEntry.getDomainTransactionRecords()) {
if (cancelableFields.contains(record.getReportField())
&& record.getReportingTime().isAfter(now)) {
return true;
}
}
return false;
}
}
return false;
}
})
// We only want to cancel out the most recent add or renewal
.last();
})
// We only want to cancel out the most recent add or renewal
.last();
ImmutableSet.Builder<DomainTransactionRecord> recordsBuilder = new ImmutableSet.Builder<>();
if (entryToCancel.isPresent()) {
for (DomainTransactionRecord record : entryToCancel.get().getDomainTransactionRecords()) {

View file

@ -156,7 +156,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
.build());
Registry registry = Registry.get(existingDomain.getTld());
HistoryEntry historyEntry = buildHistoryEntry(
existingDomain, command.getPeriod(), now, registry.getRenewGracePeriodLength());
existingDomain, now, command.getPeriod(), registry.getRenewGracePeriodLength());
String tld = existingDomain.getTld();
// Bill for this explicit renew itself.
BillingEvent.OneTime explicitRenewEvent =
@ -213,7 +213,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
}
private HistoryEntry buildHistoryEntry(
DomainResource existingDomain, Period period, DateTime now, Duration renewGracePeriod) {
DomainResource existingDomain, DateTime now, Period period, Duration renewGracePeriod) {
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_RENEW)
.setPeriod(period)

View file

@ -22,12 +22,15 @@ import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.createCancelingRecords;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainTransferUtils.createGainingTransferPollMessage;
import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.base.Optional;
@ -52,6 +55,7 @@ import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.TransferData;
@ -109,12 +113,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
}
TransferData transferData = existingDomain.getTransferData();
String gainingClientId = transferData.getGainingClientId();
HistoryEntry historyEntry = historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE)
.setModificationTime(now)
.setOtherClientId(gainingClientId)
.setParent(Key.create(existingDomain))
.build();
Registry registry = Registry.get(existingDomain.getTld());
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, registry, now, gainingClientId);
// Bill for the transfer.
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
@ -191,4 +191,28 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
targetId, newDomain.getTransferData(), newDomain.getRegistrationExpirationTime()))
.build();
}
private HistoryEntry buildHistoryEntry(
DomainResource existingDomain, Registry registry, DateTime now, String gainingClientId) {
ImmutableSet<DomainTransactionRecord> cancelingRecords =
createCancelingRecords(
existingDomain,
now,
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
ImmutableSet.of(TRANSFER_SUCCESSFUL));
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE)
.setModificationTime(now)
.setOtherClientId(gainingClientId)
.setParent(Key.create(existingDomain))
.setDomainTransactionRecords(
union(
cancelingRecords,
DomainTransactionRecord.create(
existingDomain.getTld(),
now.plus(registry.getTransferGracePeriodLength()),
TRANSFER_SUCCESSFUL,
1)))
.build();
}
}

View file

@ -21,13 +21,16 @@ import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyTransferInitiator;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.createCancelingRecords;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainTransferUtils.createLosingTransferPollMessage;
import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager;
@ -41,6 +44,8 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.TransferStatus;
@ -90,12 +95,8 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
if (!isSuperuser) {
checkAllowedAccessToTld(clientId, existingDomain.getTld());
}
HistoryEntry historyEntry = historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL)
.setOtherClientId(existingDomain.getTransferData().getLosingClientId())
.setModificationTime(now)
.setParent(Key.create(existingDomain))
.build();
Registry registry = Registry.get(existingDomain.getTld());
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, registry, now);
DomainResource newDomain =
denyPendingTransfer(existingDomain, TransferStatus.CLIENT_CANCELLED, now);
ofy().save().<ImmutableObject>entities(
@ -113,4 +114,21 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
.setResData(createTransferResponse(targetId, newDomain.getTransferData(), null))
.build();
}
private HistoryEntry buildHistoryEntry(
DomainResource existingDomain, Registry registry, DateTime now) {
ImmutableSet<DomainTransactionRecord> cancelingRecords =
createCancelingRecords(
existingDomain,
now,
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
ImmutableSet.of(TRANSFER_SUCCESSFUL));
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL)
.setOtherClientId(existingDomain.getTransferData().getLosingClientId())
.setModificationTime(now)
.setParent(Key.create(existingDomain))
.setDomainTransactionRecords(cancelingRecords)
.build();
}
}

View file

@ -21,13 +21,18 @@ import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.createCancelingRecords;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainTransferUtils.createGainingTransferPollMessage;
import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_NACKED;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager;
@ -41,6 +46,8 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.TransferStatus;
@ -84,12 +91,8 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
validateClientIsLoggedIn(clientId);
DateTime now = ofy().getTransactionTime();
DomainResource existingDomain = loadAndVerifyExistence(DomainResource.class, targetId, now);
HistoryEntry historyEntry = historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REJECT)
.setModificationTime(now)
.setOtherClientId(existingDomain.getTransferData().getGainingClientId())
.setParent(Key.create(existingDomain))
.build();
Registry registry = Registry.get(existingDomain.getTld());
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, registry, now);
verifyOptionalAuthInfo(authInfo, existingDomain);
verifyHasPendingTransfer(existingDomain);
verifyResourceOwnership(clientId, existingDomain);
@ -113,4 +116,25 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
.setResData(createTransferResponse(targetId, newDomain.getTransferData(), null))
.build();
}
private HistoryEntry buildHistoryEntry(
DomainResource existingDomain, Registry registry, DateTime now) {
ImmutableSet<DomainTransactionRecord> cancelingRecords =
createCancelingRecords(
existingDomain,
now,
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
ImmutableSet.of(TRANSFER_SUCCESSFUL));
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REJECT)
.setModificationTime(now)
.setOtherClientId(existingDomain.getTransferData().getGainingClientId())
.setParent(Key.create(existingDomain))
.setDomainTransactionRecords(
union(
cancelingRecords,
DomainTransactionRecord.create(
existingDomain.getTld(), now, TRANSFER_NACKED, 1)))
.build();
}
}

View file

@ -61,6 +61,8 @@ import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.TransferData;
@ -141,7 +143,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
eppInput.getSingleExtension(FeeTransferCommandExtension.class);
FeesAndCredits feesAndCredits = pricingLogic.getTransferPrice(registry, targetId, now);
validateFeeChallenge(targetId, tld, now, feeTransfer, feesAndCredits);
HistoryEntry historyEntry = buildHistoryEntry(period, existingDomain, now);
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, registry, now, period);
DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength());
// If the domain will be in the auto-renew grace period at the moment of transfer, the transfer
@ -252,13 +254,21 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
}
private HistoryEntry buildHistoryEntry(
Period period, DomainResource existingDomain, DateTime now) {
DomainResource existingDomain, Registry registry, DateTime now, Period period) {
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST)
.setOtherClientId(existingDomain.getCurrentSponsorClientId())
.setPeriod(period)
.setModificationTime(now)
.setParent(Key.create(existingDomain))
.setDomainTransactionRecords(
ImmutableSet.of(
DomainTransactionRecord.create(
registry.getTldStr(),
now.plus(registry.getAutomaticTransferLength())
.plus(registry.getTransferGracePeriodLength()),
TransactionReportField.TRANSFER_SUCCESSFUL,
1)))
.build();
}

View file

@ -97,10 +97,8 @@ public class DomainTransactionRecord extends ImmutableObject implements Buildabl
NET_RENEWS_8_YR,
NET_RENEWS_9_YR,
NET_RENEWS_10_YR,
TRANSFER_GAINING_SUCCESSFUL,
TRANSFER_GAINING_NACKED,
TRANSFER_LOSING_SUCCESSFUL,
TRANSFER_LOSING_NACKED,
TRANSFER_SUCCESSFUL,
TRANSFER_NACKED,
DELETED_DOMAINS_GRACE,
DELETED_DOMAINS_NOGRACE,
RESTORED_DOMAINS;
@ -115,7 +113,7 @@ public class DomainTransactionRecord extends ImmutableObject implements Buildabl
return nameToField("NET_RENEWS_%d_YR", years);
}
private static final ImmutableSet<TransactionReportField> ADD_FIELDS =
public static final ImmutableSet<TransactionReportField> ADD_FIELDS =
ImmutableSet.of(
NET_ADDS_1_YR,
NET_ADDS_2_YR,
@ -128,7 +126,7 @@ public class DomainTransactionRecord extends ImmutableObject implements Buildabl
NET_ADDS_9_YR,
NET_ADDS_10_YR);
private static final ImmutableSet<TransactionReportField> RENEW_FIELDS =
public static final ImmutableSet<TransactionReportField> RENEW_FIELDS =
ImmutableSet.of(
NET_RENEWS_1_YR,
NET_RENEWS_2_YR,
@ -141,16 +139,6 @@ public class DomainTransactionRecord extends ImmutableObject implements Buildabl
NET_RENEWS_9_YR,
NET_RENEWS_10_YR);
/** Boilerplate to determine if a field is one of the NET_ADDS fields. */
public static boolean isAddsField(TransactionReportField field) {
return ADD_FIELDS.contains(field);
}
/** Boilerplate to determine if a field is one of the NET_ADDS fields. */
public static boolean isRenewsField(TransactionReportField field) {
return RENEW_FIELDS.contains(field);
}
private static TransactionReportField nameToField(String enumTemplate, int years) {
checkArgument(
years >= 1 && years <= 10, "domain add and renew years must be between 1 and 10");