From b9222759dc89f6d1760aa07138b19db8cc927913 Mon Sep 17 00:00:00 2001 From: Wolfgang Meyers Date: Wed, 8 Feb 2017 14:13:34 -0800 Subject: [PATCH] Refactor common domain transfer logic into a utility class These methods will also be used for RDE imports (in a follow-up). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146955581 --- .../flows/domain/DomainFlowUtils.java | 56 ---- .../domain/DomainTransferApproveFlow.java | 4 +- .../domain/DomainTransferCancelFlow.java | 4 +- .../flows/domain/DomainTransferQueryFlow.java | 2 +- .../domain/DomainTransferRejectFlow.java | 4 +- .../domain/DomainTransferRequestFlow.java | 159 +--------- .../flows/domain/DomainTransferUtils.java | 289 ++++++++++++++++++ 7 files changed, 302 insertions(+), 216 deletions(-) create mode 100644 java/google/registry/flows/domain/DomainTransferUtils.java diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index aa080a9ab..57777273e 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -94,7 +94,6 @@ import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.host.HostResource; -import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; import google.registry.model.registry.Registry; @@ -102,8 +101,6 @@ import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservationType; import google.registry.model.reporting.HistoryEntry; import google.registry.model.tmch.ClaimsListShard; -import google.registry.model.transfer.TransferData; -import google.registry.model.transfer.TransferResponse.DomainTransferResponse; import google.registry.util.Idn; import java.math.BigDecimal; import java.util.HashSet; @@ -624,59 +621,6 @@ public class DomainFlowUtils { } } - /** Create a poll message for the gaining client in a transfer. */ - static PollMessage createGainingTransferPollMessage( - String targetId, - TransferData transferData, - @Nullable DateTime extendedRegistrationExpirationTime, - HistoryEntry historyEntry) { - return new PollMessage.OneTime.Builder() - .setClientId(transferData.getGainingClientId()) - .setEventTime(transferData.getPendingTransferExpirationTime()) - .setMsg(transferData.getTransferStatus().getMessage()) - .setResponseData(ImmutableList.of( - createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime), - DomainPendingActionNotificationResponse.create( - targetId, - transferData.getTransferStatus().isApproved(), - transferData.getTransferRequestTrid(), - historyEntry.getModificationTime()))) - .setParent(historyEntry) - .build(); - } - - /** Create a poll message for the losing client in a transfer. */ - static PollMessage createLosingTransferPollMessage( - String targetId, - TransferData transferData, - @Nullable DateTime extendedRegistrationExpirationTime, - HistoryEntry historyEntry) { - return new PollMessage.OneTime.Builder() - .setClientId(transferData.getLosingClientId()) - .setEventTime(transferData.getPendingTransferExpirationTime()) - .setMsg(transferData.getTransferStatus().getMessage()) - .setResponseData(ImmutableList.of( - createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime))) - .setParent(historyEntry) - .build(); - } - - /** Create a {@link DomainTransferResponse} off of the info in a {@link TransferData}. */ - static DomainTransferResponse createTransferResponse( - String targetId, - TransferData transferData, - @Nullable DateTime extendedRegistrationExpirationTime) { - return new DomainTransferResponse.Builder() - .setFullyQualifiedDomainNameName(targetId) - .setGainingClientId(transferData.getGainingClientId()) - .setLosingClientId(transferData.getLosingClientId()) - .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime()) - .setTransferRequestTime(transferData.getTransferRequestTime()) - .setTransferStatus(transferData.getTransferStatus()) - .setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime) - .build(); - } - /** * Adds a secDns extension to a list if the given set of dsData is non-empty. * diff --git a/java/google/registry/flows/domain/DomainTransferApproveFlow.java b/java/google/registry/flows/domain/DomainTransferApproveFlow.java index 75df35d89..022296acd 100644 --- a/java/google/registry/flows/domain/DomainTransferApproveFlow.java +++ b/java/google/registry/flows/domain/DomainTransferApproveFlow.java @@ -23,9 +23,9 @@ 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.createGainingTransferPollMessage; -import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; 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.pricing.PricingEngineProxy.getDomainRenewCost; diff --git a/java/google/registry/flows/domain/DomainTransferCancelFlow.java b/java/google/registry/flows/domain/DomainTransferCancelFlow.java index 4a7196c2d..88579f3d0 100644 --- a/java/google/registry/flows/domain/DomainTransferCancelFlow.java +++ b/java/google/registry/flows/domain/DomainTransferCancelFlow.java @@ -20,9 +20,9 @@ import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyHasPendingTransfer; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; -import static google.registry.flows.domain.DomainFlowUtils.createLosingTransferPollMessage; -import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; 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.util.DateTimeUtils.END_OF_TIME; diff --git a/java/google/registry/flows/domain/DomainTransferQueryFlow.java b/java/google/registry/flows/domain/DomainTransferQueryFlow.java index f5dbd2189..149cc110e 100644 --- a/java/google/registry/flows/domain/DomainTransferQueryFlow.java +++ b/java/google/registry/flows/domain/DomainTransferQueryFlow.java @@ -17,7 +17,7 @@ package google.registry.flows.domain; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo; -import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; +import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import com.google.common.base.Optional; diff --git a/java/google/registry/flows/domain/DomainTransferRejectFlow.java b/java/google/registry/flows/domain/DomainTransferRejectFlow.java index 84d10904a..6028ebb64 100644 --- a/java/google/registry/flows/domain/DomainTransferRejectFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRejectFlow.java @@ -21,9 +21,9 @@ 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.createGainingTransferPollMessage; -import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; 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.util.DateTimeUtils.END_OF_TIME; diff --git a/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 79d3245be..a2f8eddc1 100644 --- a/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -14,25 +14,23 @@ package google.registry.flows.domain; -import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.getOnlyElement; import static google.registry.flows.FlowUtils.validateClientIsLoggedIn; import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence; import static google.registry.flows.ResourceFlowUtils.verifyAuthInfo; import static google.registry.flows.ResourceFlowUtils.verifyAuthInfoPresentForResourceTransfer; import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses; import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld; -import static google.registry.flows.domain.DomainFlowUtils.createGainingTransferPollMessage; -import static google.registry.flows.domain.DomainFlowUtils.createLosingTransferPollMessage; -import static google.registry.flows.domain.DomainFlowUtils.createTransferResponse; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; +import static google.registry.flows.domain.DomainTransferUtils.createLosingTransferPollMessage; +import static google.registry.flows.domain.DomainTransferUtils.createPendingTransferData; +import static google.registry.flows.domain.DomainTransferUtils.createTransferResponse; +import static google.registry.flows.domain.DomainTransferUtils.createTransferServerApproveEntities; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING; import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.util.DateTimeUtils.END_OF_TIME; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -46,9 +44,6 @@ import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.exceptions.AlreadyPendingTransferException; import google.registry.flows.exceptions.ObjectAlreadySponsoredException; -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.DomainCommand.Transfer; import google.registry.model.domain.DomainResource; import google.registry.model.domain.Period; @@ -70,9 +65,7 @@ import google.registry.model.transfer.TransferData.TransferServerApproveEntity; import google.registry.model.transfer.TransferResponse.DomainTransferResponse; 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. @@ -155,6 +148,8 @@ public final class DomainTransferRequestFlow implements TransactionalFlow { serverApproveNewExpirationTime, historyEntry, existingDomain, + trid, + gainingClientId, feesAndCredits.getTotalCost(), years, now); @@ -218,128 +213,6 @@ public final class DomainTransferRequestFlow implements TransactionalFlow { .build(); } - /** - * Returns a set of entities created speculatively in anticipation of a server approval. - * - *

This set consists of: - *

- */ - private ImmutableSet createTransferServerApproveEntities( - DateTime automaticTransferTime, - DateTime serverApproveNewExpirationTime, - HistoryEntry historyEntry, - DomainResource existingDomain, - Money transferCost, - int years, - DateTime now) { - // Create a TransferData for the server-approve case to use for the speculative poll messages. - TransferData serverApproveTransferData = - createTransferDataBuilder(existingDomain, automaticTransferTime, years, now) - .setTransferStatus(TransferStatus.SERVER_APPROVED) - .build(); - Registry registry = Registry.get(existingDomain.getTld()); - return new ImmutableSet.Builder() - .add(createTransferBillingEvent( - automaticTransferTime, historyEntry, registry, transferCost, 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( - DateTime automaticTransferTime, - HistoryEntry historyEntry, - Registry registry, - Money transferCost, - int years) { - return new BillingEvent.OneTime.Builder() - .setReason(Reason.TRANSFER) - .setTargetId(targetId) - .setClientId(gainingClientId) - .setCost(transferCost) - .setPeriodYears(years) - .setEventTime(automaticTransferTime) - .setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength())) - .setParent(historyEntry) - .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( - DateTime serverApproveNewExpirationTime, HistoryEntry historyEntry) { - return new BillingEvent.Recurring.Builder() - .setReason(Reason.RENEW) - .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) - .setTargetId(targetId) - .setClientId(gainingClientId) - .setEventTime(serverApproveNewExpirationTime) - .setRecurrenceEndTime(END_OF_TIME) - .setParent(historyEntry) - .build(); - } - - private PollMessage.Autorenew createGainingClientAutorenewPollMessage( - DateTime serverApproveNewExpirationTime, HistoryEntry historyEntry) { - return new PollMessage.Autorenew.Builder() - .setTargetId(targetId) - .setClientId(gainingClientId) - .setEventTime(serverApproveNewExpirationTime) - .setAutorenewEndTime(END_OF_TIME) - .setMsg("Domain was auto-renewed.") - .setParent(historyEntry) - .build(); - } - private Builder createTransferDataBuilder( DomainResource existingDomain, DateTime automaticTransferTime, int years, DateTime now) { return new TransferData.Builder() @@ -351,26 +224,6 @@ public final class DomainTransferRequestFlow implements TransactionalFlow { .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 DomainTransferResponse createResponse( Period period, DomainResource existingDomain, DomainResource newDomain, DateTime now) { // If the registration were approved this instant, this is what the new expiration would be, diff --git a/java/google/registry/flows/domain/DomainTransferUtils.java b/java/google/registry/flows/domain/DomainTransferUtils.java new file mode 100644 index 000000000..dc550e602 --- /dev/null +++ b/java/google/registry/flows/domain/DomainTransferUtils.java @@ -0,0 +1,289 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.flows.domain; + +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.getOnlyElement; +import static google.registry.util.DateTimeUtils.END_OF_TIME; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.googlecode.objectify.Key; +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.eppcommon.Trid; +import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; +import google.registry.model.poll.PollMessage; +import google.registry.model.registry.Registry; +import google.registry.model.reporting.HistoryEntry; +import google.registry.model.transfer.TransferData; +import google.registry.model.transfer.TransferData.Builder; +import google.registry.model.transfer.TransferData.TransferServerApproveEntity; +import google.registry.model.transfer.TransferResponse.DomainTransferResponse; +import google.registry.model.transfer.TransferStatus; +import javax.annotation.Nullable; +import org.joda.money.Money; +import org.joda.time.DateTime; +import org.joda.time.Duration; + +/** + * Utility logic for facilitating domain transfers. + */ +public final class DomainTransferUtils { + + /** + * Sets up {@link TransferData} for a domain with links to entities for server approval. + */ + public static 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(); + } + + /** + * 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 + *
+ */ + public static ImmutableSet createTransferServerApproveEntities( + DateTime automaticTransferTime, + DateTime serverApproveNewExpirationTime, + HistoryEntry historyEntry, + DomainResource existingDomain, + Trid trid, + String gainingClientId, + Money transferCost, + int years, + DateTime now) { + String targetId = existingDomain.getFullyQualifiedDomainName(); + // Create a TransferData for the server-approve case to use for the speculative poll messages. + TransferData serverApproveTransferData = + createTransferDataBuilder( + existingDomain, trid, gainingClientId, automaticTransferTime, years, now) + .setTransferStatus(TransferStatus.SERVER_APPROVED) + .build(); + Registry registry = Registry.get(existingDomain.getTld()); + return new ImmutableSet.Builder() + .add( + createTransferBillingEvent( + automaticTransferTime, + historyEntry, + targetId, + gainingClientId, + registry, + transferCost, + years)) + .addAll( + createOptionalAutorenewCancellation( + automaticTransferTime, historyEntry, targetId, existingDomain) + .asSet()) + .add( + createGainingClientAutorenewEvent( + serverApproveNewExpirationTime, historyEntry, targetId, gainingClientId)) + .add( + createGainingClientAutorenewPollMessage( + serverApproveNewExpirationTime, historyEntry, targetId, gainingClientId)) + .add( + createGainingTransferPollMessage( + targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry)) + .add( + createLosingTransferPollMessage( + targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry)) + .build(); + } + + /** Create a poll message for the gaining client in a transfer. */ + public static PollMessage createGainingTransferPollMessage( + String targetId, + TransferData transferData, + @Nullable DateTime extendedRegistrationExpirationTime, + HistoryEntry historyEntry) { + return new PollMessage.OneTime.Builder() + .setClientId(transferData.getGainingClientId()) + .setEventTime(transferData.getPendingTransferExpirationTime()) + .setMsg(transferData.getTransferStatus().getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime), + DomainPendingActionNotificationResponse.create( + targetId, + transferData.getTransferStatus().isApproved(), + transferData.getTransferRequestTrid(), + historyEntry.getModificationTime()))) + .setParent(historyEntry) + .build(); + } + + /** Create a poll message for the losing client in a transfer. */ + public static PollMessage createLosingTransferPollMessage( + String targetId, + TransferData transferData, + @Nullable DateTime extendedRegistrationExpirationTime, + HistoryEntry historyEntry) { + return new PollMessage.OneTime.Builder() + .setClientId(transferData.getLosingClientId()) + .setEventTime(transferData.getPendingTransferExpirationTime()) + .setMsg(transferData.getTransferStatus().getMessage()) + .setResponseData(ImmutableList.of( + createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime))) + .setParent(historyEntry) + .build(); + } + + /** Create a {@link DomainTransferResponse} off of the info in a {@link TransferData}. */ + static DomainTransferResponse createTransferResponse( + String targetId, + TransferData transferData, + @Nullable DateTime extendedRegistrationExpirationTime) { + return new DomainTransferResponse.Builder() + .setFullyQualifiedDomainNameName(targetId) + .setGainingClientId(transferData.getGainingClientId()) + .setLosingClientId(transferData.getLosingClientId()) + .setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime()) + .setTransferRequestTime(transferData.getTransferRequestTime()) + .setTransferStatus(transferData.getTransferStatus()) + .setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime) + .build(); + } + + private static PollMessage.Autorenew createGainingClientAutorenewPollMessage( + DateTime serverApproveNewExpirationTime, + HistoryEntry historyEntry, + String targetId, + String gainingClientId) { + return new PollMessage.Autorenew.Builder() + .setTargetId(targetId) + .setClientId(gainingClientId) + .setEventTime(serverApproveNewExpirationTime) + .setAutorenewEndTime(END_OF_TIME) + .setMsg("Domain was auto-renewed.") + .setParent(historyEntry) + .build(); + } + + private static BillingEvent.Recurring createGainingClientAutorenewEvent( + DateTime serverApproveNewExpirationTime, + HistoryEntry historyEntry, + String targetId, + String gainingClientId) { + return new BillingEvent.Recurring.Builder() + .setReason(Reason.RENEW) + .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) + .setTargetId(targetId) + .setClientId(gainingClientId) + .setEventTime(serverApproveNewExpirationTime) + .setRecurrenceEndTime(END_OF_TIME) + .setParent(historyEntry) + .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 static Optional createOptionalAutorenewCancellation( + DateTime automaticTransferTime, + HistoryEntry historyEntry, + String targetId, + 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 static BillingEvent.OneTime createTransferBillingEvent( + DateTime automaticTransferTime, + HistoryEntry historyEntry, + String targetId, + String gainingClientId, + Registry registry, + Money transferCost, + int years) { + return new BillingEvent.OneTime.Builder() + .setReason(Reason.TRANSFER) + .setTargetId(targetId) + .setClientId(gainingClientId) + .setCost(transferCost) + .setPeriodYears(years) + .setEventTime(automaticTransferTime) + .setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength())) + .setParent(historyEntry) + .build(); + } + + private static Builder createTransferDataBuilder( + DomainResource existingDomain, + Trid trid, + String gainingClientId, + DateTime automaticTransferTime, + int years, + DateTime now) { + return new TransferData.Builder() + .setTransferRequestTrid(trid) + .setTransferRequestTime(now) + .setGainingClientId(gainingClientId) + .setLosingClientId(existingDomain.getCurrentSponsorClientId()) + .setPendingTransferExpirationTime(automaticTransferTime) + .setExtendedRegistrationYears(years); + } + + private DomainTransferUtils() {} +}