From df53080f7855f39a2e8a837028fca765e99b6250 Mon Sep 17 00:00:00 2001 From: nickfelt Date: Mon, 26 Sep 2016 13:01:23 -0700 Subject: [PATCH] Reorganize DomainTransferRequestFlow helper method structure This attempts to improve the organization of the helper methods in DomainTransferRequestFlow created by the flattening in [] The primary changes are: - new createTransferServerApproveEntities() method that now makes all the server approve entities in one centralized place - new createPendingTransferData() method that takes the server approve entities and hides the slightly hacky code that parses out the right ones to stuff into the TransferData This seems like an overall simpler structure to me. With this change, run() is 20 lines shorter and the flow overall is 40 lines shorter (not counting a big blob of javadoc I added). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134315295 --- .../domain/DomainTransferRequestFlow.java | 282 ++++++++---------- 1 file changed, 128 insertions(+), 154 deletions(-) diff --git a/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 99aa6b340..0287f1d75 100644 --- a/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -16,7 +16,6 @@ package google.registry.flows.domain; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Sets.union; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.ResourceFlowUtils.verifyRequiredAuthInfoForResourceTransfer; @@ -46,7 +45,6 @@ import google.registry.flows.LoggedInFlow; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; import google.registry.flows.exceptions.ObjectAlreadySponsoredException; -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; @@ -74,6 +72,7 @@ import google.registry.model.transfer.TransferStatus; import javax.inject.Inject; import org.joda.money.Money; import org.joda.time.DateTime; +import org.joda.time.Duration; /** * An EPP flow that requests a transfer on a domain. @@ -139,65 +138,44 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra FeeTransformCommandExtension feeTransfer = eppInput.getFirstExtensionOfClasses( FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); validateFeeChallenge(targetId, tld, now, feeTransfer, renewCost); - ImmutableSet.Builder entitiesToSave = new ImmutableSet.Builder<>(); HistoryEntry historyEntry = buildHistory(period, existingDomain); - entitiesToSave.add(historyEntry); DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength()); // The new expiration time if there is a server approval. DateTime serverApproveNewExpirationTime = extendRegistrationWithCap( automaticTransferTime, existingDomain.getRegistrationExpirationTime(), years); - ImmutableSet billingEvents = createBillingEvents( - renewCost, - registry, - existingDomain, - historyEntry, - automaticTransferTime, - serverApproveNewExpirationTime, - years); - entitiesToSave.addAll(billingEvents); - ImmutableSet pollMessages = createPollMessages( - existingDomain, - historyEntry, - automaticTransferTime, - serverApproveNewExpirationTime, - years); - entitiesToSave.addAll(pollMessages); - ImmutableSet.Builder> serverApproveEntities = - new ImmutableSet.Builder<>(); - for (TransferServerApproveEntity entity : union(billingEvents, pollMessages)) { - serverApproveEntities.add(Key.create(entity)); - } + // Create speculative entities in anticipation of an automatic server approval. + ImmutableSet serverApproveEntities = + createTransferServerApproveEntities( + automaticTransferTime, + serverApproveNewExpirationTime, + historyEntry, + existingDomain, + renewCost, + years); // Create the transfer data that represents the pending transfer. - TransferData pendingTransferData = createTransferDataBuilder() - .setTransferStatus(TransferStatus.PENDING) - .setLosingClientId(existingDomain.getCurrentSponsorClientId()) - .setPendingTransferExpirationTime(automaticTransferTime) - .setExtendedRegistrationYears(years) - .setServerApproveBillingEvent(Key.create( - getOnlyElement(filter(billingEvents, BillingEvent.OneTime.class)))) - .setServerApproveAutorenewEvent(Key.create( - getOnlyElement(filter(billingEvents, BillingEvent.Recurring.class)))) - .setServerApproveAutorenewPollMessage(Key.create( - getOnlyElement(filter(pollMessages, PollMessage.Autorenew.class)))) - .setServerApproveEntities(serverApproveEntities.build()) - .build(); - // When a transfer is requested, a poll message is created to notify the losing registrar. + TransferData pendingTransferData = createPendingTransferData( + createTransferDataBuilder(existingDomain, automaticTransferTime, years), + serverApproveEntities); + // Create a poll message to notify the losing registrar that a transfer was requested. PollMessage requestPollMessage = createLosingTransferPollMessage( targetId, pendingTransferData, serverApproveNewExpirationTime, historyEntry) .asBuilder().setEventTime(now).build(); - entitiesToSave.add(requestPollMessage); // End the old autorenew event and poll message at the implicit transfer time. This may delete - // the poll message if it has no events left. Note that this is still left on the domain as the - // autorenewBillingEvent because it is still the current autorenew event until the transfer - // happens. If you read the domain after the transfer occurs, then cloneProjectedAtTime() will - // move the serverApproveAutoRenewEvent into the autoRenewEvent field. + // the poll message if it has no events left. Note that if the automatic transfer succeeds, then + // cloneProjectedAtTime() will replace these old autorenew entities with the server approve ones + // that we've created in this flow and stored in pendingTransferData. updateAutorenewRecurrenceEndTime(existingDomain, automaticTransferTime); handleExtraFlowLogic(years, existingDomain, historyEntry); DomainResource newDomain = existingDomain.asBuilder() .setTransferData(pendingTransferData) .addStatusValue(StatusValue.PENDING_TRANSFER) .build(); - ofy().save().entities(entitiesToSave.add(newDomain).build()); + ofy().save() + .entities(new ImmutableSet.Builder<>() + .add(newDomain, historyEntry, requestPollMessage) + .addAll(serverApproveEntities) + .build()) + .now(); return createOutput( SUCCESS_WITH_ACTION_PENDING, createResponse(period, existingDomain, newDomain), @@ -232,38 +210,56 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra .build(); } - private ImmutableSet createBillingEvents( - Money renewCost, - Registry registry, - DomainResource existingDomain, - HistoryEntry historyEntry, + /** + * Returns a set of entities created speculatively in anticipation of a server approval. + * + *

This set consists of: + *

    + *
  • The one-time billing event charging the gaining registrar for the transfer + *
  • A cancellation of an autorenew charge for the losing registrar, if the autorenew grace + * period will apply at transfer time + *
  • A new post-transfer autorenew billing event for the domain (and gaining registrar) + *
  • A new post-transfer autorenew poll message for the domain (and gaining registrar) + *
  • A poll message for the gaining registrar + *
  • A poll message for the losing registrar + *
+ */ + private ImmutableSet createTransferServerApproveEntities( DateTime automaticTransferTime, DateTime serverApproveNewExpirationTime, + HistoryEntry historyEntry, + DomainResource existingDomain, + Money renewCost, int years) { - ImmutableSet.Builder billingEvents = new ImmutableSet.Builder<>(); - BillingEvent.OneTime transferBillingEvent = - createTransferBillingEvent(years, renewCost, registry, historyEntry); - BillingEvent.Recurring gainingClientAutorenewEvent = createGainingClientAutorenewEvent( - historyEntry, serverApproveNewExpirationTime); - billingEvents.add(transferBillingEvent, gainingClientAutorenewEvent); - // If there will be an autorenew between now and the automatic transfer time, and if the - // autorenew grace period length is long enough that the domain will still be within it at the - // automatic transfer time, then the transfer will subsume the autorenew so we need to write out - // a cancellation for it. - DateTime oldExpirationTime = existingDomain.getRegistrationExpirationTime(); - if (automaticTransferTime.isAfter(oldExpirationTime) && automaticTransferTime.isBefore( - oldExpirationTime.plus(registry.getAutoRenewGracePeriodLength()))) { - BillingEvent.Cancellation autorenewCancellation = - createAutorenewCancellation( - existingDomain, historyEntry, automaticTransferTime, registry); - billingEvents.add(autorenewCancellation); - } - return billingEvents.build(); + // Create a TransferData for the server-approve case to use for the speculative poll messages. + TransferData serverApproveTransferData = + createTransferDataBuilder(existingDomain, automaticTransferTime, years) + .setTransferStatus(TransferStatus.SERVER_APPROVED) + .build(); + Registry registry = Registry.get(existingDomain.getTld()); + return new ImmutableSet.Builder() + .add(createTransferBillingEvent( + automaticTransferTime, historyEntry, registry, renewCost, years)) + .addAll(createOptionalAutorenewCancellation( + automaticTransferTime, historyEntry, existingDomain) + .asSet()) + .add(createGainingClientAutorenewEvent( + serverApproveNewExpirationTime, historyEntry)) + .add(createGainingClientAutorenewPollMessage( + serverApproveNewExpirationTime, historyEntry)) + .add(createGainingTransferPollMessage( + targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry)) + .add(createLosingTransferPollMessage( + targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry)) + .build(); } private BillingEvent.OneTime createTransferBillingEvent( - int years, Money renewCost, Registry registry, HistoryEntry historyEntry) { - DateTime automaticTransferTime = now.plus(registry.getAutomaticTransferLength()); + DateTime automaticTransferTime, + HistoryEntry historyEntry, + Registry registry, + Money renewCost, + int years) { return new BillingEvent.OneTime.Builder() .setReason(Reason.TRANSFER) .setTargetId(targetId) @@ -276,8 +272,42 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra .build(); } + /** + * Creates an optional autorenew cancellation if one would apply to the server-approved transfer. + * + *

If there will be an autorenew between now and the automatic transfer time, and if the + * autorenew grace period length is long enough that the domain will still be within it at the + * automatic transfer time, then the transfer will subsume the autorenew and we need to write out + * a cancellation for it. + */ + // TODO(b/19430703): the above logic is incomplete; it doesn't handle a grace period that started + // before the transfer was requested and continues through the automatic transfer time. + private Optional createOptionalAutorenewCancellation( + DateTime automaticTransferTime, + HistoryEntry historyEntry, + DomainResource existingDomain) { + Registry registry = Registry.get(existingDomain.getTld()); + DateTime oldExpirationTime = existingDomain.getRegistrationExpirationTime(); + Duration autoRenewGracePeriodLength = registry.getAutoRenewGracePeriodLength(); + if (automaticTransferTime.isAfter(oldExpirationTime) + && automaticTransferTime.isBefore(oldExpirationTime.plus(autoRenewGracePeriodLength))) { + return Optional.of(new BillingEvent.Cancellation.Builder() + .setReason(Reason.RENEW) + .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) + .setTargetId(targetId) + .setClientId(existingDomain.getCurrentSponsorClientId()) + .setEventTime(automaticTransferTime) + .setBillingTime(existingDomain.getRegistrationExpirationTime() + .plus(registry.getAutoRenewGracePeriodLength())) + .setRecurringEventKey(existingDomain.getAutorenewBillingEvent()) + .setParent(historyEntry) + .build()); + } + return Optional.absent(); + } + private BillingEvent.Recurring createGainingClientAutorenewEvent( - HistoryEntry historyEntry, DateTime serverApproveNewExpirationTime) { + DateTime serverApproveNewExpirationTime, HistoryEntry historyEntry) { return new BillingEvent.Recurring.Builder() .setReason(Reason.RENEW) .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) @@ -289,71 +319,8 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra .build(); } - /** - * Creates an autorenew cancellation. - * - *

If there will be an autorenew between now and the automatic transfer time, and if the - * autorenew grace period length is long enough that the domain will still be within it at the - * automatic transfer time, then the transfer will subsume the autorenew and we need to write out - * a cancellation for it. - */ - private BillingEvent.Cancellation createAutorenewCancellation( - DomainResource existingDomain, - HistoryEntry historyEntry, - DateTime automaticTransferTime, - Registry registry) { - return new BillingEvent.Cancellation.Builder() - .setReason(Reason.RENEW) - .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) - .setTargetId(targetId) - .setClientId(existingDomain.getCurrentSponsorClientId()) - .setEventTime(automaticTransferTime) - .setBillingTime(existingDomain.getRegistrationExpirationTime() - .plus(registry.getAutoRenewGracePeriodLength())) - .setRecurringEventKey(existingDomain.getAutorenewBillingEvent()) - .setParent(historyEntry) - .build(); - } - - /** Create the message that will be sent to the gaining registrar on server approval. */ - private PollMessage createServerApproveGainingPollMessage( - DomainResource existingDomain, - HistoryEntry historyEntry, - DateTime automaticTransferTime, - DateTime serverApproveNewExpirationTime, - int years) { - return createGainingTransferPollMessage( - targetId, - createTransferDataBuilder() - .setTransferStatus(TransferStatus.SERVER_APPROVED) - .setLosingClientId(existingDomain.getCurrentSponsorClientId()) - .setPendingTransferExpirationTime(automaticTransferTime) - .setExtendedRegistrationYears(years) - .build(), - serverApproveNewExpirationTime, - historyEntry); - } - - private ImmutableSet createPollMessages( - DomainResource existingDomain, - HistoryEntry historyEntry, - DateTime automaticTransferTime, - DateTime serverApproveNewExpirationTime, - int years) { - PollMessage.Autorenew gainingClientAutorenewPollMessage = - createGainingClientAutorenewPollMessage(historyEntry, serverApproveNewExpirationTime); - PollMessage serverApproveGainingPollMessage = createServerApproveGainingPollMessage( - existingDomain, historyEntry, automaticTransferTime, serverApproveNewExpirationTime, years); - PollMessage serverApproveLosingPollMessage = createServerApproveLosingPollMessage( - existingDomain, historyEntry, automaticTransferTime, serverApproveNewExpirationTime, years); - return ImmutableSet.of( - gainingClientAutorenewPollMessage, - serverApproveGainingPollMessage, - serverApproveLosingPollMessage); - } - private PollMessage.Autorenew createGainingClientAutorenewPollMessage( - HistoryEntry historyEntry, DateTime serverApproveNewExpirationTime) { + DateTime serverApproveNewExpirationTime, HistoryEntry historyEntry) { return new PollMessage.Autorenew.Builder() .setTargetId(targetId) .setClientId(gainingClientId) @@ -364,30 +331,37 @@ public final class DomainTransferRequestFlow extends LoggedInFlow implements Tra .build(); } - /** Create the message that will be sent to the losing registrar on server approval. */ - private PollMessage createServerApproveLosingPollMessage( + private Builder createTransferDataBuilder( DomainResource existingDomain, - HistoryEntry historyEntry, DateTime automaticTransferTime, - DateTime serverApproveNewExpirationTime, int years) { - return createLosingTransferPollMessage( - targetId, - createTransferDataBuilder() - .setTransferStatus(TransferStatus.SERVER_APPROVED) - .setLosingClientId(existingDomain.getCurrentSponsorClientId()) - .setPendingTransferExpirationTime(automaticTransferTime) - .setExtendedRegistrationYears(years) - .build(), - serverApproveNewExpirationTime, - historyEntry); - } - - private Builder createTransferDataBuilder() { return new TransferData.Builder() + .setTransferRequestTrid(trid) .setTransferRequestTime(now) .setGainingClientId(gainingClientId) - .setTransferRequestTrid(trid); + .setLosingClientId(existingDomain.getCurrentSponsorClientId()) + .setPendingTransferExpirationTime(automaticTransferTime) + .setExtendedRegistrationYears(years); + } + + private TransferData createPendingTransferData( + TransferData.Builder transferDataBuilder, + ImmutableSet serverApproveEntities) { + ImmutableSet.Builder> serverApproveEntityKeys = + new ImmutableSet.Builder<>(); + for (TransferServerApproveEntity entity : serverApproveEntities) { + serverApproveEntityKeys.add(Key.create(entity)); + } + return transferDataBuilder + .setTransferStatus(TransferStatus.PENDING) + .setServerApproveBillingEvent(Key.create( + getOnlyElement(filter(serverApproveEntities, BillingEvent.OneTime.class)))) + .setServerApproveAutorenewEvent(Key.create( + getOnlyElement(filter(serverApproveEntities, BillingEvent.Recurring.class)))) + .setServerApproveAutorenewPollMessage(Key.create( + getOnlyElement(filter(serverApproveEntities, PollMessage.Autorenew.class)))) + .setServerApproveEntities(serverApproveEntityKeys.build()) + .build(); } private void handleExtraFlowLogic(