From 95cc7ab3d80b976ee9acba3a833a29b2b52650e1 Mon Sep 17 00:00:00 2001 From: mountford Date: Wed, 7 Sep 2016 15:23:50 -0700 Subject: [PATCH] Add extra logic for all relevant flows This CL enhances various domain flows (check, create, delete, renew, restore, transfer, update) so that they invoke the appropriate methods on the object implementing the TLD's RegistryExtraFlowLogic (if any). TldSpecificLogicProxy is also updated to invoke RegistryExtraFlowLogic proxy (if any) to fetch the appropriate price. The tests use a made-up extra flow logic object which can be attached to a test TLD to make sure that the proper routines are being invoked. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=132486734 --- java/google/registry/flows/EppException.java | 8 + .../flows/ResourceSyncDeleteFlow.java | 4 +- .../flows/ResourceTransferRequestFlow.java | 15 +- .../flows/domain/BaseDomainCreateFlow.java | 25 +- .../flows/domain/BaseDomainUpdateFlow.java | 19 +- .../domain/DomainApplicationCreateFlow.java | 3 +- .../flows/domain/DomainCheckFlow.java | 4 +- .../flows/domain/DomainCreateFlow.java | 13 +- .../domain/DomainCreateOrAllocateFlow.java | 1 + .../flows/domain/DomainDeleteFlow.java | 20 +- .../flows/domain/DomainFlowUtils.java | 44 ++- .../registry/flows/domain/DomainInfoFlow.java | 6 +- .../flows/domain/DomainRenewFlow.java | 21 +- .../domain/DomainRestoreRequestFlow.java | 14 + .../domain/DomainTransferRequestFlow.java | 21 +- .../flows/domain/DomainUpdateFlow.java | 6 +- .../flows/domain/RegistryExtraFlowLogic.java | 91 +++++- .../domain/RegistryExtraFlowLogicProxy.java | 23 +- .../flows/domain/TldSpecificLogicProxy.java | 297 ++++++++++++++++++ .../registry/model/domain/fee/BaseFee.java | 4 +- .../registry/model/domain/fee/Credit.java | 5 +- .../fee/FeeTransformCommandExtensionImpl.java | 4 +- .../fee/FeeTransformResponseExtension.java | 6 +- .../FeeTransformResponseExtensionImpl.java | 9 +- ...ansformResponseExtensionImplNoCredits.java | 5 +- .../flags/FlagsCreateCommandExtension.java | 4 + .../flags/FlagsTransferCommandExtension.java | 8 + .../pricing/TldSpecificLogicProxy.java | 195 ------------ .../flows/domain/DomainCreateFlowTest.java | 21 +- .../flows/domain/DomainDeleteFlowTest.java | 15 +- .../flows/domain/DomainRenewFlowTest.java | 14 +- .../domain/DomainRestoreRequestFlowTest.java | 22 +- .../domain/DomainTransferRequestFlowTest.java | 13 + .../flows/domain/DomainUpdateFlowTest.java | 2 +- .../domain/TldSpecificLogicProxyTest.java | 221 +++++++++++++ ...eck_fee_multiple_commands_response_v06.xml | 2 +- ...eck_fee_multiple_commands_response_v12.xml | 2 +- ..._check_fee_premium_response_v11_update.xml | 2 +- .../domain_check_fee_premium_response_v12.xml | 2 +- .../domain/testdata/domain_create_flags.xml | 32 ++ .../domain/testdata/domain_delete_flags.xml | 11 + .../domain/testdata/domain_renew_flags.xml | 13 + .../domain_transfer_request_flags.xml | 27 ++ .../domain_update_restore_request_flags.xml | 17 + .../model/domain/TestExtraLogicManager.java | 170 +++++++++- .../pricing/TldSpecificLogicProxyTest.java | 106 ------- 46 files changed, 1173 insertions(+), 394 deletions(-) create mode 100644 java/google/registry/flows/domain/TldSpecificLogicProxy.java delete mode 100644 java/google/registry/pricing/TldSpecificLogicProxy.java create mode 100644 javatests/google/registry/flows/domain/TldSpecificLogicProxyTest.java create mode 100644 javatests/google/registry/flows/domain/testdata/domain_create_flags.xml create mode 100644 javatests/google/registry/flows/domain/testdata/domain_delete_flags.xml create mode 100644 javatests/google/registry/flows/domain/testdata/domain_renew_flags.xml create mode 100644 javatests/google/registry/flows/domain/testdata/domain_transfer_request_flags.xml create mode 100644 javatests/google/registry/flows/domain/testdata/domain_update_restore_request_flags.xml delete mode 100644 javatests/google/registry/pricing/TldSpecificLogicProxyTest.java diff --git a/java/google/registry/flows/EppException.java b/java/google/registry/flows/EppException.java index 405810d18..345e8ca4d 100644 --- a/java/google/registry/flows/EppException.java +++ b/java/google/registry/flows/EppException.java @@ -250,4 +250,12 @@ public abstract class EppException extends Exception { super("Specified protocol version is not implemented"); } } + + /** Specified protocol version is not implemented. */ + @EppResultCode(Code.CommandFailed) + public static class CommandFailedException extends EppException { + public CommandFailedException() { + super("Command failed"); + } + } } diff --git a/java/google/registry/flows/ResourceSyncDeleteFlow.java b/java/google/registry/flows/ResourceSyncDeleteFlow.java index 974a3dcfa..31171ae92 100644 --- a/java/google/registry/flows/ResourceSyncDeleteFlow.java +++ b/java/google/registry/flows/ResourceSyncDeleteFlow.java @@ -36,7 +36,7 @@ public abstract class ResourceSyncDeleteFlow @Override @SuppressWarnings("unchecked") - protected final R createOrMutateResource() { + protected final R createOrMutateResource() throws EppException { B builder = (B) prepareDeletedResourceAsBuilder(existingResource, now); setDeleteProperties(builder); return builder.build(); @@ -52,7 +52,7 @@ public abstract class ResourceSyncDeleteFlow /** Set any resource-specific properties before deleting. */ @SuppressWarnings("unused") - protected void setDeleteProperties(B builder) {} + protected void setDeleteProperties(B builder) throws EppException {} /** Modify any other resources that need to be informed of this delete. */ protected void modifySyncDeleteRelatedResources() {} diff --git a/java/google/registry/flows/ResourceTransferRequestFlow.java b/java/google/registry/flows/ResourceTransferRequestFlow.java index 01a75fdb7..4b60f7d70 100644 --- a/java/google/registry/flows/ResourceTransferRequestFlow.java +++ b/java/google/registry/flows/ResourceTransferRequestFlow.java @@ -77,7 +77,7 @@ import org.joda.time.Duration; }}; @Override - protected final void initResourceCreateOrMutateFlow() { + protected final void initResourceCreateOrMutateFlow() throws EppException { initResourceTransferRequestFlow(); } @@ -100,7 +100,8 @@ import org.joda.time.Duration; verifyTransferRequestIsAllowed(); } - private TransferData.Builder createTransferDataBuilder(TransferStatus transferStatus) { + private TransferData.Builder + createTransferDataBuilder(TransferStatus transferStatus) throws EppException { TransferData.Builder builder = new TransferData.Builder() .setGainingClientId(gainingClient.getId()) .setTransferRequestTime(now) @@ -113,7 +114,7 @@ import org.joda.time.Duration; } private PollMessage createPollMessage( - Client client, TransferStatus transferStatus, DateTime eventTime) { + Client client, TransferStatus transferStatus, DateTime eventTime) throws EppException { ImmutableList.Builder responseData = new ImmutableList.Builder<>(); responseData.add(createTransferResponse( existingResource, createTransferDataBuilder(transferStatus).build(), now)); @@ -132,7 +133,7 @@ import org.joda.time.Duration; @Override @SuppressWarnings("unchecked") - protected final R createOrMutateResource() { + protected final R createOrMutateResource() throws EppException { // Figure out transfer expiration time once we've verified that the existingResource does in // fact exist (otherwise we won't know which TLD to get this figure off of). transferExpirationTime = now.plus(getAutomaticTransferLength()); @@ -158,7 +159,7 @@ import org.joda.time.Duration; } /** Subclasses can override this to do further initialization. */ - protected void initResourceTransferRequestFlow() {} + protected void initResourceTransferRequestFlow() throws EppException {} /** * Subclasses can override this to return the keys of any entities that need to be deleted if the @@ -173,8 +174,8 @@ import org.joda.time.Duration; protected void verifyTransferRequestIsAllowed() throws EppException {} /** Subclasses can override this to modify fields on the transfer data builder. */ - protected void setTransferDataProperties( - @SuppressWarnings("unused") TransferData.Builder builder) {} + @SuppressWarnings("unused") + protected void setTransferDataProperties(TransferData.Builder builder) throws EppException {} @Override protected final EppOutput getOutput() throws EppException { diff --git a/java/google/registry/flows/domain/BaseDomainCreateFlow.java b/java/google/registry/flows/domain/BaseDomainCreateFlow.java index f481f8d44..3b619a561 100644 --- a/java/google/registry/flows/domain/BaseDomainCreateFlow.java +++ b/java/google/registry/flows/domain/BaseDomainCreateFlow.java @@ -51,12 +51,14 @@ import google.registry.flows.EppException.StatusProhibitsOperationException; import google.registry.flows.EppException.UnimplementedOptionException; import google.registry.flows.ResourceCreateFlow; import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException; +import google.registry.flows.domain.TldSpecificLogicProxy.EppCommandOperations; import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase.Builder; import google.registry.model.domain.DomainCommand.Create; import google.registry.model.domain.DomainResource; import google.registry.model.domain.LrpToken; import google.registry.model.domain.fee.FeeTransformCommandExtension; +import google.registry.model.domain.flags.FlagsCreateCommandExtension; import google.registry.model.domain.launch.LaunchCreateExtension; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchNotice.InvalidChecksumException; @@ -67,8 +69,6 @@ import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.smd.SignedMark; import google.registry.model.tmch.ClaimsListShard; -import google.registry.pricing.TldSpecificLogicProxy; -import google.registry.pricing.TldSpecificLogicProxy.EppCommandOperations; import java.util.Set; import javax.annotation.Nullable; @@ -95,16 +95,20 @@ public abstract class BaseDomainCreateFlow lrpToken; + protected Optional extraFlowLogic; + @Override public final void initResourceCreateOrMutateFlow() throws EppException { command = cloneAndLinkReferences(command, now); - registerExtensions(SecDnsCreateExtension.class); + registerExtensions(SecDnsCreateExtension.class, FlagsCreateCommandExtension.class); + registerExtensions(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); secDnsCreate = eppInput.getSingleExtension(SecDnsCreateExtension.class); launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class); feeCreate = eppInput.getFirstExtensionOfClasses(FEE_CREATE_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); hasSignedMarks = launchCreate != null && !launchCreate.getSignedMarks().isEmpty(); initDomainCreateFlow(); + // We can't initialize extraFlowLogic here, because the TLD has not been checked yet. } @Override @@ -181,9 +185,19 @@ public abstract class BaseDomainCreateFlow credits; + protected Optional extraFlowLogic; + @Inject DomainDeleteFlow() {} @Override protected void initResourceCreateOrMutateFlow() throws EppException { registerExtensions(SecDnsUpdateExtension.class); + extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); } @Override @@ -93,7 +98,7 @@ public class DomainDeleteFlow extends ResourceSyncDeleteFlow builder, getTargetId(), existingResource.getTld(), + getClientId(), null, - now); + now, + eppInput); extensions.add(builder.build()); } // If the TLD uses the flags extension, add it to the info response. Optional extraLogicManager = - RegistryExtraFlowLogicProxy.newInstanceForTld(existingResource.getTld()); + RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); if (extraLogicManager.isPresent()) { List flags = extraLogicManager.get().getExtensionFlags( existingResource, this.getClientId(), now); // As-of date is always now for info commands. diff --git a/java/google/registry/flows/domain/DomainRenewFlow.java b/java/google/registry/flows/domain/DomainRenewFlow.java index ff9775194..9d16f43c6 100644 --- a/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/java/google/registry/flows/domain/DomainRenewFlow.java @@ -28,6 +28,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost; import static google.registry.util.DateTimeUtils.leapSafeAddYears; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.Key; @@ -84,6 +85,8 @@ public class DomainRenewFlow extends OwnedResourceMutateFlow extraFlowLogic; + @Inject DomainRenewFlow() {} @Override @@ -96,6 +99,7 @@ public class DomainRenewFlow extends OwnedResourceMutateFlowentities(explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage); return existingResource.asBuilder() .setRegistrationExpirationTime(newExpirationTime) @@ -160,6 +171,14 @@ public class DomainRenewFlow extends OwnedResourceMutateFlow extraFlowLogic; @Inject DomainRestoreRequestFlow() {} @@ -82,6 +84,7 @@ public class DomainRestoreRequestFlow extends OwnedResourceMutateFlowentities(restoreEvent, autorenewEvent, autorenewPollMessage, renewEvent); + + // Handle extra flow logic, if any. + if (extraFlowLogic.isPresent()) { + extraFlowLogic.get().performAdditionalDomainRestoreLogic( + existingResource, getClientId(), now, eppInput); + } + return existingResource.asBuilder() .setRegistrationExpirationTime(newExpirationTime) .setDeletionTime(END_OF_TIME) @@ -171,6 +181,10 @@ public class DomainRestoreRequestFlow extends OwnedResourceMutateFlow extraFlowLogic; /** * An optional extension from the client specifying how much they think the transfer should cost. @@ -101,7 +106,8 @@ public class DomainTransferRequestFlow } @Override - protected final void initResourceTransferRequestFlow() { + protected final void initResourceTransferRequestFlow() throws EppException { + registerExtensions(FlagsTransferCommandExtension.class); registerExtensions(FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); feeTransfer = eppInput.getFirstExtensionOfClasses( FEE_TRANSFER_COMMAND_EXTENSIONS_IN_PREFERENCE_ORDER); @@ -146,6 +152,7 @@ public class DomainTransferRequestFlow .setMsg("Domain was auto-renewed.") .setParent(historyEntry) .build(); + extraFlowLogic = RegistryExtraFlowLogicProxy.newInstanceForDomain(existingResource); } @Override @@ -174,12 +181,18 @@ public class DomainTransferRequestFlow } @Override - protected void setTransferDataProperties(TransferData.Builder builder) { + protected void setTransferDataProperties(TransferData.Builder builder) throws EppException { builder .setServerApproveBillingEvent(Key.create(transferBillingEvent)) .setServerApproveAutorenewEvent(Key.create(gainingClientAutorenewEvent)) .setServerApproveAutorenewPollMessage(Key.create(gainingClientAutorenewPollMessage)) .setExtendedRegistrationYears(command.getPeriod().getValue()); + + // Handle extra flow logic, if any. + if (extraFlowLogic.isPresent()) { + extraFlowLogic.get().performAdditionalDomainTransferLogic( + existingResource, getClientId(), now, command.getPeriod().getValue(), eppInput); + } } /** @@ -233,6 +246,10 @@ public class DomainTransferRequestFlow // transfer occurs, then the logic in cloneProjectedAtTime() will move the // serverApproveAutoRenewEvent into the autoRenewEvent field. updateAutorenewRecurrenceEndTime(existingResource, automaticTransferTime); + + if (extraFlowLogic.isPresent()) { + extraFlowLogic.get().commitAdditionalLogicChanges(); + } } @Override diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index c1991f825..f8f22e6ef 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -138,7 +138,7 @@ public class DomainUpdateFlow extends BaseDomainUpdateFlow getExtensionFlags( DomainResource domainResource, String clientIdentifier, DateTime asOfDate); + /** Computes the expected creation fee, for use in fee challenges and the like. */ + public BaseFee getCreateFeeOrCredit( + String domainName, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException; + /** - * Add and remove flags passed via the EPP flags extension. Any changes should not be persisted to - * Datastore until commitAdditionalDomainUpdates is called. Name suggested by Benjamin McIlwain. + * Performs additional tasks required for a create command. Any changes should not be persisted to + * Datastore until commitAdditionalLogicChanges is called. */ - public void performAdditionalDomainUpdateLogic( - DomainResource domainResource, + public void performAdditionalDomainCreateLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException; + + /** + * Performs additional tasks required for a delete command. Any changes should not be persisted to + * Datastore until commitAdditionalLogicChanges is called. + */ + public void performAdditionalDomainDeleteLogic( + DomainResource domain, String clientIdentifier, DateTime asOfDate, EppInput eppInput) throws EppException; - /** Commit any changes made as a result of a call to performAdditionalDomainUpdateLogic(). */ - public void commitAdditionalDomainUpdates(); + /** Computes the expected renewal fee, for use in fee challenges and the like. */ + public BaseFee getRenewFeeOrCredit( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException; + + /** + * Performs additional tasks required for a renew command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + public void performAdditionalDomainRenewLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException; + + /** + * Performs additional tasks required for a restore command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + public void performAdditionalDomainRestoreLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException; + + /** + * Performs additional tasks required for a transfer command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + public void performAdditionalDomainTransferLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException; + + /** Computes the expected update fee, for use in fee challenges and the like. */ + public BaseFee getUpdateFeeOrCredit( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException; + + /** + * Performs additional tasks required for an update command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + public void performAdditionalDomainUpdateLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException; + + /** Commits any changes made as a result of a call to one of the performXXX methods. */ + public void commitAdditionalLogicChanges(); } diff --git a/java/google/registry/flows/domain/RegistryExtraFlowLogicProxy.java b/java/google/registry/flows/domain/RegistryExtraFlowLogicProxy.java index 1f5a7f148..b1f7f815e 100644 --- a/java/google/registry/flows/domain/RegistryExtraFlowLogicProxy.java +++ b/java/google/registry/flows/domain/RegistryExtraFlowLogicProxy.java @@ -15,8 +15,12 @@ package google.registry.flows.domain; import com.google.common.base.Optional; +import google.registry.flows.EppException; +import google.registry.flows.EppException.CommandFailedException; +import google.registry.model.domain.DomainBase; import google.registry.model.registry.Registry; import java.util.HashMap; +import javax.annotation.Nullable; /** * Static class to return the correct {@link RegistryExtraFlowLogic} for a particular TLD. @@ -36,12 +40,23 @@ public class RegistryExtraFlowLogicProxy { extraLogicOverrideMap.put(tld, extraLogicClass); } - public static Optional newInstanceForTld(String tld) { + public static Optional + newInstanceForDomain(@Nullable D domain) throws EppException { + if (domain == null) { + return Optional.absent(); + } else { + return newInstanceForTld(domain.getTld()); + } + } + + public static Optional + newInstanceForTld(String tld) throws EppException { if (extraLogicOverrideMap.containsKey(tld)) { try { - return Optional.of(extraLogicOverrideMap.get(tld).newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - return Optional.absent(); + return Optional.of( + extraLogicOverrideMap.get(tld).getConstructor().newInstance()); + } catch (ReflectiveOperationException ex) { + throw new CommandFailedException(); } } return Optional.absent(); diff --git a/java/google/registry/flows/domain/TldSpecificLogicProxy.java b/java/google/registry/flows/domain/TldSpecificLogicProxy.java new file mode 100644 index 000000000..ee7ae0629 --- /dev/null +++ b/java/google/registry/flows/domain/TldSpecificLogicProxy.java @@ -0,0 +1,297 @@ +// Copyright 2016 The Domain Registry 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.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static google.registry.model.EppResourceUtils.loadByUniqueId; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; +import static google.registry.util.CollectionUtils.nullToEmpty; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; +import google.registry.flows.EppException; +import google.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException; +import google.registry.model.ImmutableObject; +import google.registry.model.domain.DomainCommand.Create; +import google.registry.model.domain.DomainResource; +import google.registry.model.domain.LrpToken; +import google.registry.model.domain.fee.BaseFee; +import google.registry.model.domain.fee.BaseFee.FeeType; +import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.EapFee; +import google.registry.model.domain.fee.Fee; +import google.registry.model.eppinput.EppInput; +import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; +import google.registry.model.registry.Registry; +import java.util.List; +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; +import org.joda.time.DateTime; + +/** + * Provides pricing, billing, and update logic, with call-outs that can be customized by providing + * implementations on a per-TLD basis. + */ +public final class TldSpecificLogicProxy { + /** A collection of fees and credits for a specific EPP transform. */ + public static final class EppCommandOperations extends ImmutableObject { + private final CurrencyUnit currency; + private final ImmutableList fees; + private final ImmutableList credits; + + /** Constructs an EppCommandOperations object using separate lists of fees and credits. */ + EppCommandOperations( + CurrencyUnit currency, ImmutableList fees, ImmutableList credits) { + this.currency = checkArgumentNotNull( + currency, "Currency may not be null in EppCommandOperations."); + checkArgument(!fees.isEmpty(), "You must specify one or more fees."); + this.fees = checkArgumentNotNull(fees, "Fees may not be null in EppCommandOperations."); + this.credits = + checkArgumentNotNull(credits, "Credits may not be null in EppCommandOperations."); + } + + /** + * Constructs an EppCommandOperations object. The arguments are sorted into fees and credits. + */ + EppCommandOperations(CurrencyUnit currency, BaseFee... feesAndCredits) { + this.currency = checkArgumentNotNull( + currency, "Currency may not be null in EppCommandOperations."); + ImmutableList.Builder feeBuilder = new ImmutableList.Builder<>(); + ImmutableList.Builder creditBuilder = new ImmutableList.Builder<>(); + for (BaseFee feeOrCredit : feesAndCredits) { + if (feeOrCredit instanceof Credit) { + creditBuilder.add((Credit) feeOrCredit); + } else { + feeBuilder.add((Fee) feeOrCredit); + } + } + this.fees = feeBuilder.build(); + this.credits = creditBuilder.build(); + } + + private Money getTotalCostForType(FeeType type) { + Money result = Money.zero(currency); + checkArgumentNotNull(type); + for (Fee fee : fees) { + if (fee.getType() == type) { + result = result.plus(fee.getCost()); + } + } + return result; + } + + /** Returns the total cost of all fees and credits for the event. */ + public Money getTotalCost() { + Money result = Money.zero(currency); + for (Fee fee : fees) { + result = result.plus(fee.getCost()); + } + for (Credit credit : credits) { + result = result.plus(credit.getCost()); + } + return result; + } + + /** Returns the create cost for the event. */ + public Money getCreateCost() { + return getTotalCostForType(FeeType.CREATE); + } + + /** Returns the EAP cost for the event. */ + public Money getEapCost() { + return getTotalCostForType(FeeType.EAP); + } + + /** Returns the list of fees for the event. */ + public ImmutableList getFees() { + return fees; + } + + /** Returns the list of credits for the event. */ + public List getCredits() { + return nullToEmpty(credits); + } + + /** Returns the currency for all fees in the event. */ + public final CurrencyUnit getCurrency() { + return currency; + } + } + + private TldSpecificLogicProxy() {} + + /** Returns a new create price for the Pricer. */ + public static EppCommandOperations getCreatePrice( + Registry registry, + String domainName, + String clientIdentifier, + DateTime date, + int years, + EppInput eppInput) throws EppException { + CurrencyUnit currency = registry.getCurrency(); + + // Get the create cost, either from the extra flow logic or straight from PricingEngineProxy. + BaseFee createFeeOrCredit; + Optional extraFlowLogic = + RegistryExtraFlowLogicProxy.newInstanceForTld(registry.getTldStr()); + if (extraFlowLogic.isPresent()) { + createFeeOrCredit = extraFlowLogic.get() + .getCreateFeeOrCredit(domainName, clientIdentifier, date, years, eppInput); + } else { + DomainPrices prices = getPricesForDomainName(domainName, date); + createFeeOrCredit = + Fee.create(prices.getCreateCost().multipliedBy(years).getAmount(), FeeType.CREATE); + } + + // Create fees for the cost and the EAP fee, if any. + EapFee eapFee = registry.getEapFeeFor(date); + Money eapFeeCost = eapFee.getCost(); + checkState(eapFeeCost.getCurrencyUnit().equals(currency)); + if (!eapFeeCost.getAmount().equals(Money.zero(currency).getAmount())) { + return new EppCommandOperations( + currency, + createFeeOrCredit, + Fee.create(eapFeeCost.getAmount(), FeeType.EAP, eapFee.getPeriod().upperEndpoint())); + } else { + return new EppCommandOperations(currency, createFeeOrCredit); + } + } + + /** + * Computes the renew fee or credit. This is called by other methods which use the renew fee + * (renew, restore, etc). + */ + static BaseFee getRenewFeeOrCredit( + Registry registry, + String domainName, + String clientIdentifier, + DateTime date, + int years, + EppInput eppInput) throws EppException { + Optional extraFlowLogic = + RegistryExtraFlowLogicProxy.newInstanceForTld(registry.getTldStr()); + if (extraFlowLogic.isPresent()) { + // TODO: Consider changing the method definition to have the domain passed in to begin with. + DomainResource domain = loadByUniqueId(DomainResource.class, domainName, date); + if (domain == null) { + throw new ResourceToMutateDoesNotExistException(DomainResource.class, domainName); + } + return + extraFlowLogic.get().getRenewFeeOrCredit(domain, clientIdentifier, date, years, eppInput); + } else { + DomainPrices prices = getPricesForDomainName(domainName, date); + return Fee.create(prices.getRenewCost().multipliedBy(years).getAmount(), FeeType.RENEW); + } + } + + /** Returns a new renew price for the pricer. */ + public static EppCommandOperations getRenewPrice( + Registry registry, + String domainName, + String clientIdentifier, + DateTime date, + int years, + EppInput eppInput) throws EppException { + return new EppCommandOperations( + registry.getCurrency(), + getRenewFeeOrCredit(registry, domainName, clientIdentifier, date, years, eppInput)); + } + + /** Returns a new restore price for the pricer. */ + public static EppCommandOperations getRestorePrice( + Registry registry, + String domainName, + String clientIdentifier, + DateTime date, + EppInput eppInput) throws EppException { + return new EppCommandOperations( + registry.getCurrency(), + getRenewFeeOrCredit(registry, domainName, clientIdentifier, date, 1, eppInput), + Fee.create(registry.getStandardRestoreCost().getAmount(), FeeType.RESTORE)); + } + + /** Returns a new transfer price for the pricer. */ + public static EppCommandOperations getTransferPrice( + Registry registry, + String domainName, + String clientIdentifier, + DateTime transferDate, + int years, + EppInput eppInput) throws EppException { + // Currently, all transfer prices = renew prices, so just pass through. + return getRenewPrice( + registry, domainName, clientIdentifier, transferDate, years, eppInput); + } + + /** Returns a new update price for the pricer. */ + public static EppCommandOperations getUpdatePrice( + Registry registry, + String domainName, + String clientIdentifier, + DateTime date, + EppInput eppInput) throws EppException { + CurrencyUnit currency = registry.getCurrency(); + + // If there is extra flow logic, it may specify an update price. Otherwise, there is none. + BaseFee feeOrCredit; + Optional extraFlowLogic = + RegistryExtraFlowLogicProxy.newInstanceForTld(registry.getTldStr()); + if (extraFlowLogic.isPresent()) { + // TODO: Consider changing the method definition to have the domain passed in to begin with. + DomainResource domain = loadByUniqueId(DomainResource.class, domainName, date); + if (domain == null) { + throw new ResourceToMutateDoesNotExistException(DomainResource.class, domainName); + } + feeOrCredit = + extraFlowLogic.get().getUpdateFeeOrCredit(domain, clientIdentifier, date, eppInput); + } else { + feeOrCredit = Fee.create(Money.zero(registry.getCurrency()).getAmount(), FeeType.UPDATE); + } + + return new EppCommandOperations(currency, feeOrCredit); + } + + /** Returns the fee class for a given domain and date. */ + public static Optional getFeeClass(String domainName, DateTime date) { + return getPricesForDomainName(domainName, date).getFeeClass(); + } + + /** + * Checks whether a {@link Create} command has a valid {@link LrpToken} for a particular TLD, and + * return that token (wrapped in an {@link Optional}) if one exists. + * + *

This method has no knowledge of whether or not an auth code (interpreted here as an LRP + * token) has already been checked against the reserved list for QLP (anchor tenant), as auth + * codes are used for both types of registrations. + */ + public static Optional getMatchingLrpToken(Create createCommand, String tld) { + // Note that until the actual per-TLD logic is built out, what's being done here is a basic + // domain-name-to-assignee match. + String lrpToken = createCommand.getAuthInfo().getPw().getValue(); + LrpToken token = ofy().load().key(Key.create(LrpToken.class, lrpToken)).now(); + if (token != null) { + if (token.getAssignee().equalsIgnoreCase(createCommand.getFullyQualifiedDomainName()) + && token.getRedemptionHistoryEntry() == null + && token.getValidTlds().contains(tld)) { + return Optional.of(token); + } + } + return Optional.absent(); + } +} diff --git a/java/google/registry/model/domain/fee/BaseFee.java b/java/google/registry/model/domain/fee/BaseFee.java index 5b10e888e..bdee83ab7 100644 --- a/java/google/registry/model/domain/fee/BaseFee.java +++ b/java/google/registry/model/domain/fee/BaseFee.java @@ -45,7 +45,9 @@ public abstract class BaseFee extends ImmutableObject { CREATE("create"), EAP("Early Access Period, fee expires: %s"), RENEW("renew"), - RESTORE("restore"); + RESTORE("restore"), + UPDATE("update"), + CREDIT("%s credit"); private final String formatString; diff --git a/java/google/registry/model/domain/fee/Credit.java b/java/google/registry/model/domain/fee/Credit.java index ce64e0aac..aa34291b3 100644 --- a/java/google/registry/model/domain/fee/Credit.java +++ b/java/google/registry/model/domain/fee/Credit.java @@ -21,11 +21,12 @@ import java.math.BigDecimal; /** A credit, in currency units specified elsewhere in the xml, and with an optional description. */ public class Credit extends BaseFee { - public static Credit create(BigDecimal cost, String description) { + public static Credit create(BigDecimal cost, FeeType type, Object... descriptionArgs) { Credit instance = new Credit(); instance.cost = checkNotNull(cost); checkArgument(instance.cost.signum() < 0); - instance.description = description; + instance.type = checkNotNull(type); + instance.generateDescription(descriptionArgs); return instance; } } diff --git a/java/google/registry/model/domain/fee/FeeTransformCommandExtensionImpl.java b/java/google/registry/model/domain/fee/FeeTransformCommandExtensionImpl.java index 10204d8c5..9d7b76561 100644 --- a/java/google/registry/model/domain/fee/FeeTransformCommandExtensionImpl.java +++ b/java/google/registry/model/domain/fee/FeeTransformCommandExtensionImpl.java @@ -14,6 +14,8 @@ package google.registry.model.domain.fee; +import static google.registry.util.CollectionUtils.nullToEmpty; + import google.registry.model.ImmutableObject; import java.util.List; import javax.xml.bind.annotation.XmlElement; @@ -51,6 +53,6 @@ public abstract class FeeTransformCommandExtensionImpl @Override public List getCredits() { - return credits; + return nullToEmpty(credits); } } diff --git a/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java b/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java index c75b208f9..ead5e687f 100644 --- a/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java +++ b/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java @@ -14,8 +14,8 @@ package google.registry.model.domain.fee; -import com.google.common.collect.ImmutableList; import google.registry.model.eppoutput.EppResponse.ResponseExtension; +import java.util.List; import org.joda.money.CurrencyUnit; /** Interface for fee extensions in Create, Renew, Transfer and Update responses. */ @@ -24,8 +24,8 @@ public interface FeeTransformResponseExtension extends ResponseExtension { /** Builder for {@link FeeTransformResponseExtension}. */ public interface Builder { Builder setCurrency(CurrencyUnit currency); - Builder setFees(ImmutableList fees); - Builder setCredits(ImmutableList credits); + Builder setFees(List fees); + Builder setCredits(List credits); FeeTransformResponseExtension build(); } } diff --git a/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImpl.java b/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImpl.java index 8c443c25b..855877292 100644 --- a/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImpl.java +++ b/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImpl.java @@ -14,7 +14,8 @@ package google.registry.model.domain.fee; -import com.google.common.collect.ImmutableList; +import static google.registry.util.CollectionUtils.forceEmptyToNull; + import google.registry.model.Buildable.GenericBuilder; import google.registry.model.ImmutableObject; import java.util.List; @@ -53,14 +54,14 @@ public class FeeTransformResponseExtensionImpl extends ImmutableObject } @Override - public B setFees(ImmutableList fees) { + public B setFees(List fees) { getInstance().fees = fees; return thisCastToDerived(); } @Override - public B setCredits(ImmutableList credits) { - getInstance().credits = credits; + public B setCredits(List credits) { + getInstance().credits = forceEmptyToNull(credits); return thisCastToDerived(); } } diff --git a/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImplNoCredits.java b/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImplNoCredits.java index c5f67f285..99f901057 100644 --- a/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImplNoCredits.java +++ b/java/google/registry/model/domain/fee/FeeTransformResponseExtensionImplNoCredits.java @@ -14,7 +14,6 @@ package google.registry.model.domain.fee; -import com.google.common.collect.ImmutableList; import google.registry.model.Buildable.GenericBuilder; import google.registry.model.ImmutableObject; import java.util.List; @@ -54,13 +53,13 @@ public class FeeTransformResponseExtensionImplNoCredits extends ImmutableObject } @Override - public B setFees(ImmutableList fees) { + public B setFees(List fees) { getInstance().fees = fees; return thisCastToDerived(); } @Override - public B setCredits(ImmutableList credits) { + public B setCredits(List credits) { return thisCastToDerived(); } } diff --git a/java/google/registry/model/domain/flags/FlagsCreateCommandExtension.java b/java/google/registry/model/domain/flags/FlagsCreateCommandExtension.java index fadf94107..e73a618c2 100644 --- a/java/google/registry/model/domain/flags/FlagsCreateCommandExtension.java +++ b/java/google/registry/model/domain/flags/FlagsCreateCommandExtension.java @@ -30,4 +30,8 @@ import javax.xml.bind.annotation.XmlRootElement; public class FlagsCreateCommandExtension implements CommandExtension { @XmlElement(name = "flag") List flags; + + public List getFlags() { + return flags; + } } diff --git a/java/google/registry/model/domain/flags/FlagsTransferCommandExtension.java b/java/google/registry/model/domain/flags/FlagsTransferCommandExtension.java index c55ed796a..84088c365 100644 --- a/java/google/registry/model/domain/flags/FlagsTransferCommandExtension.java +++ b/java/google/registry/model/domain/flags/FlagsTransferCommandExtension.java @@ -30,4 +30,12 @@ import javax.xml.bind.annotation.XmlType; public class FlagsTransferCommandExtension implements CommandExtension { FlagsList add; // list of flags to be added (turned on) FlagsList rem; // list of flags to be removed (turned off) + + public FlagsList getAddFlags() { + return add; + } + + public FlagsList getRemoveFlags() { + return rem; + } } diff --git a/java/google/registry/pricing/TldSpecificLogicProxy.java b/java/google/registry/pricing/TldSpecificLogicProxy.java deleted file mode 100644 index 21d344822..000000000 --- a/java/google/registry/pricing/TldSpecificLogicProxy.java +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2016 The Domain Registry 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.pricing; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; -import com.googlecode.objectify.Key; -import google.registry.model.ImmutableObject; -import google.registry.model.domain.DomainCommand.Create; -import google.registry.model.domain.LrpToken; -import google.registry.model.domain.fee.BaseFee.FeeType; -import google.registry.model.domain.fee.EapFee; -import google.registry.model.domain.fee.Fee; -import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; -import google.registry.model.registry.Registry; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** - * Provides pricing, billing, and update logic, with call-outs that can be customized by providing - * implementations on a per-TLD basis. - */ -public final class TldSpecificLogicProxy { - /** - * A collection of fees for a specific event. - */ - public static final class EppCommandOperations extends ImmutableObject { - private final CurrencyUnit currency; - private final ImmutableList fees; - - EppCommandOperations(CurrencyUnit currency, ImmutableList fees) { - this.currency = checkArgumentNotNull( - currency, "Currency may not be null in EppCommandOperations."); - checkArgument(!fees.isEmpty(), "You must specify one or more fees."); - this.fees = checkArgumentNotNull(fees, "Fees may not be null in EppCommandOperations."); - } - - private Money getTotalCostForType(FeeType type) { - Money result = Money.zero(currency); - checkArgumentNotNull(type); - for (Fee fee : fees) { - if (fee.getType() == type) { - result = result.plus(fee.getCost()); - } - } - return result; - } - - /** Returns the total cost of all fees for the event. */ - public Money getTotalCost() { - Money result = Money.zero(currency); - for (Fee fee : fees) { - result = result.plus(fee.getCost()); - } - return result; - } - - /** Returns the create cost for the event. */ - public Money getCreateCost() { - return getTotalCostForType(FeeType.CREATE); - } - - /** Returns the EAP cost for the event. */ - public Money getEapCost() { - return getTotalCostForType(FeeType.EAP); - } - - /** - * Returns all costs for the event as a list of fees. - */ - public ImmutableList getFees() { - return fees; - } - - /** - * Returns the currency for all fees in the event. - */ - public final CurrencyUnit getCurrency() { - return currency; - } - } - - private TldSpecificLogicProxy() {} - - /** - * Returns a new "create" price for the Pricer. - */ - public static EppCommandOperations getCreatePrice( - Registry registry, String domainName, DateTime date, int years) { - DomainPrices prices = getPricesForDomainName(domainName, date); - CurrencyUnit currency = registry.getCurrency(); - ImmutableList.Builder feeBuilder = new ImmutableList.Builder<>(); - - // Add Create cost. - feeBuilder.add( - Fee.create(prices.getCreateCost().multipliedBy(years).getAmount(), FeeType.CREATE)); - - // Add EAP Fee. - EapFee eapFee = registry.getEapFeeFor(date); - Money eapFeeCost = eapFee.getCost(); - checkState(eapFeeCost.getCurrencyUnit().equals(currency)); - if (!eapFeeCost.getAmount().equals(Money.zero(currency).getAmount())) { - feeBuilder.add( - Fee.create( - eapFeeCost.getAmount(), FeeType.EAP, eapFee.getPeriod().upperEndpoint())); - } - - return new EppCommandOperations(currency, feeBuilder.build()); - } - - /** - * Returns a new renew price for the pricer. - */ - public static EppCommandOperations getRenewPrice( - Registry registry, String domainName, DateTime date, int years) { - DomainPrices prices = getPricesForDomainName(domainName, date); - return new EppCommandOperations( - registry.getCurrency(), - ImmutableList.of( - Fee.create( - prices.getRenewCost().multipliedBy(years).getAmount(), FeeType.RENEW))); - } - - /** - * Returns a new restore price for the pricer. - */ - public static EppCommandOperations getRestorePrice( - Registry registry, String domainName, DateTime date, int years) { - DomainPrices prices = getPricesForDomainName(domainName, date); - return new EppCommandOperations( - registry.getCurrency(), - ImmutableList.of( - Fee.create( - prices.getRenewCost().multipliedBy(years).getAmount(), FeeType.RENEW), - Fee.create(registry.getStandardRestoreCost().getAmount(), FeeType.RESTORE))); - } - - /** - * Returns a new transfer price for the pricer. - */ - public static EppCommandOperations getTransferPrice( - Registry registry, String domainName, DateTime transferDate, int additionalYears) { - // Currently, all transfer prices = renew prices, so just pass through. - return getRenewPrice(registry, domainName, transferDate, additionalYears); - } - - /** - * Returns the fee class for a given domain and date. - */ - public static Optional getFeeClass(String domainName, DateTime date) { - return getPricesForDomainName(domainName, date).getFeeClass(); - } - - /** - * Checks whether a {@link Create} command has a valid {@link LrpToken} for a particular TLD, and - * return that token (wrapped in an {@link Optional}) if one exists. - * - *

This method has no knowledge of whether or not an auth code (interpreted here as an LRP - * token) has already been checked against the reserved list for QLP (anchor tenant), as auth - * codes are used for both types of registrations. - */ - public static Optional getMatchingLrpToken(Create createCommand, String tld) { - // Note that until the actual per-TLD logic is built out, what's being done here is a basic - // domain-name-to-assignee match. - String lrpToken = createCommand.getAuthInfo().getPw().getValue(); - LrpToken token = ofy().load().key(Key.create(LrpToken.class, lrpToken)).now(); - if (token != null) { - if (token.getAssignee().equalsIgnoreCase(createCommand.getFullyQualifiedDomainName()) - && token.getRedemptionHistoryEntry() == null - && token.getValidTlds().contains(tld)) { - return Optional.of(token); - } - } - return Optional.absent(); - } -} diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index 00205428b..cf260dda2 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -21,6 +21,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName; import static google.registry.testing.DatastoreHelper.assertBillingEvents; import static google.registry.testing.DatastoreHelper.createTld; +import static google.registry.testing.DatastoreHelper.createTlds; import static google.registry.testing.DatastoreHelper.deleteTld; import static google.registry.testing.DatastoreHelper.getHistoryEntries; import static google.registry.testing.DatastoreHelper.newContactResource; @@ -104,6 +105,7 @@ import google.registry.model.billing.BillingEvent.Reason; import google.registry.model.domain.DomainResource; import google.registry.model.domain.GracePeriod; import google.registry.model.domain.LrpToken; +import google.registry.model.domain.TestExtraLogicManager; import google.registry.model.domain.launch.ApplicationStatus; import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.rgp.GracePeriodStatus; @@ -134,7 +136,7 @@ public class DomainCreateFlowTest extends ResourceFlowTestCaseUSD update 1 - 11.00 + 0.00 diff --git a/javatests/google/registry/flows/domain/testdata/domain_check_fee_multiple_commands_response_v12.xml b/javatests/google/registry/flows/domain/testdata/domain_check_fee_multiple_commands_response_v12.xml index 110835458..0f105a47c 100644 --- a/javatests/google/registry/flows/domain/testdata/domain_check_fee_multiple_commands_response_v12.xml +++ b/javatests/google/registry/flows/domain/testdata/domain_check_fee_multiple_commands_response_v12.xml @@ -56,7 +56,7 @@ 1 - 11.00 + 0.00 diff --git a/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v11_update.xml b/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v11_update.xml index 6c99d8075..47572fef6 100644 --- a/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v11_update.xml +++ b/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v11_update.xml @@ -20,7 +20,7 @@ update USD 1 - 100.00 + 0.00 premium diff --git a/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v12.xml b/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v12.xml index c07f6ba64..eafbd5301 100644 --- a/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v12.xml +++ b/javatests/google/registry/flows/domain/testdata/domain_check_fee_premium_response_v12.xml @@ -60,7 +60,7 @@ 1 - 100.00 + 0.00 premium diff --git a/javatests/google/registry/flows/domain/testdata/domain_create_flags.xml b/javatests/google/registry/flows/domain/testdata/domain_create_flags.xml new file mode 100644 index 000000000..ff08adda7 --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_create_flags.xml @@ -0,0 +1,32 @@ + + + + + create-42.flags + 2 + + ns1.example.net + ns2.example.net + + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + USD + %FEE% + + + flag1 + flag2 + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_delete_flags.xml b/javatests/google/registry/flows/domain/testdata/domain_delete_flags.xml new file mode 100644 index 000000000..fc32d3224 --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_delete_flags.xml @@ -0,0 +1,11 @@ + + + + + example.flags + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_renew_flags.xml b/javatests/google/registry/flows/domain/testdata/domain_renew_flags.xml new file mode 100644 index 000000000..2784dec1b --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_renew_flags.xml @@ -0,0 +1,13 @@ + + + + + example.flags + 2000-04-03 + 5 + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_transfer_request_flags.xml b/javatests/google/registry/flows/domain/testdata/domain_transfer_request_flags.xml new file mode 100644 index 000000000..cae3be4a7 --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_transfer_request_flags.xml @@ -0,0 +1,27 @@ + + + + + example.flags + 1 + + 2fooBAR + + + + + + + flag1 + flag2 + + + flag3 + flag4 + + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_update_restore_request_flags.xml b/javatests/google/registry/flows/domain/testdata/domain_update_restore_request_flags.xml new file mode 100644 index 000000000..0927462e4 --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_update_restore_request_flags.xml @@ -0,0 +1,17 @@ + + + + + example.flags + + + + + + + + + ABC-12345 + + diff --git a/javatests/google/registry/model/domain/TestExtraLogicManager.java b/javatests/google/registry/model/domain/TestExtraLogicManager.java index 146162364..fbfa4fbdd 100644 --- a/javatests/google/registry/model/domain/TestExtraLogicManager.java +++ b/javatests/google/registry/model/domain/TestExtraLogicManager.java @@ -14,13 +14,24 @@ package google.registry.model.domain; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Ascii; +import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import google.registry.flows.EppException; -import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.domain.RegistryExtraFlowLogic; +import google.registry.model.domain.fee.BaseFee; +import google.registry.model.domain.fee.BaseFee.FeeType; +import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; +import google.registry.model.domain.flags.FlagsCreateCommandExtension; +import google.registry.model.domain.flags.FlagsTransferCommandExtension; import google.registry.model.domain.flags.FlagsUpdateCommandExtension; import google.registry.model.eppinput.EppInput; +import java.math.BigDecimal; import java.util.List; import org.joda.time.DateTime; @@ -29,6 +40,8 @@ import org.joda.time.DateTime; */ public class TestExtraLogicManager implements RegistryExtraFlowLogic { + private String messageToThrow = null; + @Override public List getExtensionFlags( DomainResource domainResource, String clientIdentifier, DateTime asOfDate) { @@ -41,23 +54,166 @@ public class TestExtraLogicManager implements RegistryExtraFlowLogic { return components.subList(1, components.size()); } + BaseFee domainNameToFeeOrCredit(String domainName) { + // The second-level domain should be of the form "description-price", where description is the + // description string of the fee or credit, and price is the price (credit if negative, fee + // otherwise). To make sure this is a valid domain name, don't use any spaces, and limit prices + // to integers. Don't use a two-character description for credits, since it is illegal to have + // both the third and fourth characters of a domain name label be hyphens. + List components = + Splitter.on('-').limit(2).splitToList( + Iterables.getFirst(Splitter.on('.').split(domainName), "")); + checkArgument(components.size() == 2, "Domain name must be of the form description-price.tld"); + int price = Integer.parseInt(components.get(1)); + if (price < 0) { + return Credit.create( + new BigDecimal(price), FeeType.valueOf(Ascii.toUpperCase(components.get(0)))); + } else { + return Fee.create( + new BigDecimal(price), FeeType.valueOf(Ascii.toUpperCase(components.get(0)))); + } + } + + /** Computes the expected create cost, for use in fee challenges and the like. */ + @Override + public BaseFee getCreateFeeOrCredit( + String domainName, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException { + return domainNameToFeeOrCredit(domainName); + } + + /** + * Performs additional tasks required for a create command. Any changes should not be persisted to + * Datastore until commitAdditionalLogicChanges is called. + */ + @Override + public void performAdditionalDomainCreateLogic( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException { + FlagsCreateCommandExtension flags = + eppInput.getSingleExtension(FlagsCreateCommandExtension.class); + if (flags == null) { + return; + } + messageToThrow = Joiner.on(',').join(flags.getFlags()); + } + + /** + * Performs additional tasks required for a delete command. Any changes should not be persisted to + * Datastore until commitAdditionalLogicChanges is called. + */ + @Override + public void performAdditionalDomainDeleteLogic( + DomainResource domainResource, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException { + messageToThrow = "deleted"; + } + + /** Computes the expected renewal cost, for use in fee challenges and the like. */ + @Override + public BaseFee getRenewFeeOrCredit( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException { + return domainNameToFeeOrCredit(domain.getFullyQualifiedDomainName()); + } + + /** + * Performs additional tasks required for a renew command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + @Override + public void performAdditionalDomainRenewLogic( + DomainResource domainResource, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException { + messageToThrow = "renewed"; + } + + /** + * Performs additional tasks required for a restore command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + @Override + public void performAdditionalDomainRestoreLogic( + DomainResource domainResource, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException { + messageToThrow = "restored"; + } + + /** + * Performs additional tasks required for a transfer command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ + @Override + public void performAdditionalDomainTransferLogic( + DomainResource domainResource, + String clientIdentifier, + DateTime asOfDate, + int years, + EppInput eppInput) throws EppException { + FlagsTransferCommandExtension flags = + eppInput.getSingleExtension(FlagsTransferCommandExtension.class); + if (flags == null) { + return; + } + messageToThrow = + "add:" + + Joiner.on(',').join(flags.getAddFlags().getFlags()) + + ";remove:" + + Joiner.on(',').join(flags.getRemoveFlags().getFlags()); + } + + /** Computes the expected update cost, for use in fee challenges and the like. */ + @Override + public BaseFee getUpdateFeeOrCredit( + DomainResource domain, + String clientIdentifier, + DateTime asOfDate, + EppInput eppInput) throws EppException { + return domainNameToFeeOrCredit(domain.getFullyQualifiedDomainName()); + } + + /** + * Performs additional tasks required for an update command. Any changes should not be persisted + * to Datastore until commitAdditionalLogicChanges is called. + */ @Override public void performAdditionalDomainUpdateLogic( DomainResource domainResource, String clientIdentifier, DateTime asOfDate, EppInput eppInput) throws EppException { - FlagsUpdateCommandExtension updateFlags = + FlagsUpdateCommandExtension flags = eppInput.getSingleExtension(FlagsUpdateCommandExtension.class); - if (updateFlags == null) { + if (flags == null) { return; } - // Throw this exception as a signal to the test that we got this far. - throw new UnimplementedExtensionException(); + messageToThrow = + "add:" + + Joiner.on(',').join(flags.getAddFlags().getFlags()) + + ";remove:" + + Joiner.on(',').join(flags.getRemoveFlags().getFlags()); } @Override - public void commitAdditionalDomainUpdates() { - return; + public void commitAdditionalLogicChanges() { + checkNotNull(messageToThrow); + // Throw a specific exception as a signal to the test code that we made it through to here. + throw new IllegalArgumentException(messageToThrow); } } diff --git a/javatests/google/registry/pricing/TldSpecificLogicProxyTest.java b/javatests/google/registry/pricing/TldSpecificLogicProxyTest.java deleted file mode 100644 index aaa0cf8e6..000000000 --- a/javatests/google/registry/pricing/TldSpecificLogicProxyTest.java +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2016 The Domain Registry 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.pricing; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static org.joda.money.CurrencyUnit.USD; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSortedMap; -import google.registry.model.domain.fee.BaseFee.FeeType; -import google.registry.model.domain.fee.Fee; -import google.registry.model.ofy.Ofy; -import google.registry.model.registry.Registry; -import google.registry.testing.AppEngineRule; -import google.registry.testing.FakeClock; -import google.registry.testing.InjectRule; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class TldSpecificLogicProxyTest { - - @Rule - public final InjectRule inject = new InjectRule(); - - @Rule - public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); - - final FakeClock clock = new FakeClock(DateTime.parse("2010-01-01T10:00:00Z")); - - Money basicCreateCost; - - @Before - public void before() throws Exception { - inject.setStaticField(Ofy.class, "clock", clock); - createTld("tld"); - - createTld("eap"); - DateTime a = clock.nowUtc().minusDays(1); - DateTime b = clock.nowUtc().plusDays(1); - persistResource( - Registry.get("eap") - .asBuilder() - .setEapFeeSchedule( - ImmutableSortedMap.of( - START_OF_TIME, Money.of(USD, 0), - a, Money.of(USD, 100), - b, Money.of(USD, 50))) - .build()); - - basicCreateCost = - PricingEngineProxy.getPricesForDomainName("example.tld", clock.nowUtc()).getCreateCost(); - } - - @Test - public void testTldSpecificLogicEngine() { - TldSpecificLogicProxy.EppCommandOperations createPrice = - TldSpecificLogicProxy.getCreatePrice( - Registry.get("tld"), "example.tld", clock.nowUtc(), 1); - assertThat(createPrice.getTotalCost()).isEqualTo(basicCreateCost); - assertThat(createPrice.getFees()).hasSize(1); - } - - @Test - public void testTldSpecificLogicEngineEap() { - TldSpecificLogicProxy.EppCommandOperations createPrice = - TldSpecificLogicProxy.getCreatePrice( - Registry.get("eap"), "example.eap", clock.nowUtc(), 1); - assertThat(createPrice.getTotalCost()).isEqualTo(basicCreateCost.plus(Money.of(USD, 100))); - assertThat(createPrice.getCurrency()).isEqualTo(USD); - assertThat(createPrice.getFees().get(0)) - .isEqualTo(Fee.create(basicCreateCost.getAmount(), FeeType.CREATE)); - assertThat(createPrice.getFees().get(1)) - .isEqualTo( - Fee.create( - Money.of(USD, 100).getAmount(), FeeType.EAP, clock.nowUtc().plusDays(1))); - assertThat(createPrice.getFees()) - .isEqualTo( - ImmutableList.of( - Fee.create(basicCreateCost.getAmount(), FeeType.CREATE), - Fee.create( - Money.of(USD, 100).getAmount(), - FeeType.EAP, - clock.nowUtc().plusDays(1)))); - } -}