diff --git a/java/google/registry/flows/domain/TldSpecificLogicProxy.java b/java/google/registry/flows/domain/TldSpecificLogicProxy.java index ee7ae0629..476043c04 100644 --- a/java/google/registry/flows/domain/TldSpecificLogicProxy.java +++ b/java/google/registry/flows/domain/TldSpecificLogicProxy.java @@ -15,7 +15,6 @@ 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; @@ -34,7 +33,6 @@ 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; @@ -160,14 +158,9 @@ public final class TldSpecificLogicProxy { } // 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())); + Fee eapFee = registry.getEapFeeFor(date); + if (!eapFee.hasZeroCost()) { + return new EppCommandOperations(currency, createFeeOrCredit, eapFee); } else { return new EppCommandOperations(currency, createFeeOrCredit); } diff --git a/java/google/registry/model/domain/fee/BaseFee.java b/java/google/registry/model/domain/fee/BaseFee.java index bdee83ab7..2967209ea 100644 --- a/java/google/registry/model/domain/fee/BaseFee.java +++ b/java/google/registry/model/domain/fee/BaseFee.java @@ -17,6 +17,7 @@ package google.registry.model.domain.fee; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkState; +import com.google.common.collect.Range; import google.registry.model.ImmutableObject; import google.registry.xml.PeriodAdapter; import java.math.BigDecimal; @@ -25,6 +26,7 @@ import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlValue; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.joda.time.DateTime; import org.joda.time.Period; /** Base class for the fee and credit types. */ @@ -75,9 +77,12 @@ public abstract class BaseFee extends ImmutableObject { @XmlValue BigDecimal cost; - + @XmlTransient FeeType type; + + @XmlTransient + Range validDateRange; public String getDescription() { return description; @@ -109,11 +114,19 @@ public abstract class BaseFee extends ImmutableObject { public FeeType getType() { return type; } + + public boolean hasValidDateRange() { + return validDateRange != null; + } protected void generateDescription(Object... args) { checkState(type != null); description = type.renderDescription(args); } + + public boolean hasZeroCost() { + return cost.signum() == 0; + } public boolean hasDefaultAttributes() { return getGracePeriod().equals(Period.ZERO) diff --git a/java/google/registry/model/domain/fee/EapFee.java b/java/google/registry/model/domain/fee/EapFee.java deleted file mode 100644 index 608ba41e5..000000000 --- a/java/google/registry/model/domain/fee/EapFee.java +++ /dev/null @@ -1,50 +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.model.domain.fee; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import com.google.common.collect.Range; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** - * An object representing an EAP fee (as a {@link Money}) along with the interval for which the - * fee applies. - */ -public class EapFee { - - /** The EAP fee (as applied on top of the domain registration cost). */ - Money cost; - - /** The time period for which the fee applies. */ - Range period; - - public Money getCost() { - return cost; - } - - public Range getPeriod() { - return period; - } - - public static EapFee create(Money cost, Range period) { - EapFee instance = new EapFee(); - instance.cost = checkArgumentNotNull(cost, "EAP fee cost cannot be null."); - instance.period = checkArgumentNotNull(period, "EAP fee period cannot be null."); - return instance; - } -} - diff --git a/java/google/registry/model/domain/fee/Fee.java b/java/google/registry/model/domain/fee/Fee.java index de17cea53..e41297f49 100644 --- a/java/google/registry/model/domain/fee/Fee.java +++ b/java/google/registry/model/domain/fee/Fee.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Range; import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06; import google.registry.model.domain.fee06.FeeCreateCommandExtensionV06; import google.registry.model.domain.fee06.FeeRenewCommandExtensionV06; @@ -36,6 +37,7 @@ import google.registry.model.domain.fee12.FeeTransferCommandExtensionV12; import google.registry.model.domain.fee12.FeeUpdateCommandExtensionV12; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import java.math.BigDecimal; +import org.joda.time.DateTime; /** * A fee, in currency units specified elsewhere in the xml, with type of the fee an optional fee @@ -51,6 +53,13 @@ public class Fee extends BaseFee { return instance; } + public static Fee create( + BigDecimal cost, FeeType type, Range validDateRange, Object... descriptionArgs) { + Fee instance = create(cost, type, descriptionArgs); + instance.validDateRange = validDateRange; + return instance; + } + public static final ImmutableList< Class>>> @@ -91,10 +100,9 @@ public class Fee extends BaseFee { FeeUpdateCommandExtensionV11.class, FeeUpdateCommandExtensionV06.class); - public static final ImmutableSet - FEE_EXTENSION_URIS = - ImmutableSet.of( - ServiceExtension.FEE_0_12.getUri(), - ServiceExtension.FEE_0_11.getUri(), - ServiceExtension.FEE_0_6.getUri()); + public static final ImmutableSet FEE_EXTENSION_URIS = + ImmutableSet.of( + ServiceExtension.FEE_0_12.getUri(), + ServiceExtension.FEE_0_11.getUri(), + ServiceExtension.FEE_0_6.getUri()); } diff --git a/java/google/registry/model/registry/Registry.java b/java/google/registry/model/registry/Registry.java index f80f3a75a..f9d17e848 100644 --- a/java/google/registry/model/registry/Registry.java +++ b/java/google/registry/model/registry/Registry.java @@ -56,7 +56,8 @@ import google.registry.model.ImmutableObject; import google.registry.model.common.EntityGroupRoot; import google.registry.model.common.TimedTransitionProperty; import google.registry.model.common.TimedTransitionProperty.TimedTransition; -import google.registry.model.domain.fee.EapFee; +import google.registry.model.domain.fee.BaseFee.FeeType; +import google.registry.model.domain.fee.Fee; import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.ReservedList; import google.registry.util.Idn; @@ -518,15 +519,19 @@ public class Registry extends ImmutableObject implements Buildable { /** * Returns the EAP fee for the registry at the given time. */ - public EapFee getEapFeeFor(DateTime now) { + public Fee getEapFeeFor(DateTime now) { ImmutableSortedMap valueMap = eapFeeSchedule.toValueMap(); DateTime periodStart = valueMap.floorKey(now); DateTime periodEnd = valueMap.ceilingKey(now); - return EapFee.create( - eapFeeSchedule.getValueAtTime(now), - Range.closedOpen( - periodStart != null ? periodStart : START_OF_TIME, - periodEnd != null ? periodEnd : END_OF_TIME)); + // NOTE: assuming END_OF_TIME would never be reached... + Range validPeriod = Range.closedOpen( + periodStart != null ? periodStart : START_OF_TIME, + periodEnd != null ? periodEnd : END_OF_TIME); + return Fee.create( + eapFeeSchedule.getValueAtTime(now).getAmount(), + FeeType.EAP, + validPeriod, + validPeriod.upperEndpoint()); } public String getLordnUsername() { diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index cf260dda2..7c99b4c97 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -171,7 +171,8 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase eapValidPeriod = + Range.closedOpen(clock.nowUtc().minusDays(1), clock.nowUtc().plusDays(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))); + Fee.create( + new BigDecimal("100.00"), + FeeType.EAP, + eapValidPeriod, + clock.nowUtc().plusDays(1))); assertThat(createPrice.getFees()) .containsExactly( Fee.create(basicCreateCost.getAmount(), FeeType.CREATE), - Fee.create(Money.of(USD, 100).getAmount(), FeeType.EAP, clock.nowUtc().plusDays(1))) + Fee.create( + new BigDecimal("100.00"), + FeeType.EAP, + eapValidPeriod, + clock.nowUtc().plusDays(1))) .inOrder(); } - + @Test public void test_extraLogic_createPrice() throws Exception { TldSpecificLogicProxy.EppCommandOperations price = @@ -128,7 +136,7 @@ public class TldSpecificLogicProxyTest extends ShardableTestCase { assertThat(price.getFees().get(0).getDescription()).isEqualTo("create"); assertThat(price.getCredits()).isEmpty(); } - + @Test public void test_extraLogic_renewPrice() throws Exception { persistActiveDomain("renew--13.test"); @@ -141,34 +149,40 @@ public class TldSpecificLogicProxyTest extends ShardableTestCase { assertThat(price.getCredits().get(0).getCost()).isEqualTo(new BigDecimal(-13)); assertThat(price.getCredits().get(0).getDescription()).isEqualTo("renew"); } - + @Test public void test_extraLogic_renewPrice_noDomain() throws Exception { thrown.expect(ResourceToMutateDoesNotExistException.class); TldSpecificLogicProxy.getRenewPrice( Registry.get("test"), "renew--13.test", "clientIdentifier", clock.nowUtc(), 1, null); } - + void persistPendingDeleteDomain(String domainName, DateTime now) throws Exception { DomainResource domain = newDomainResource(domainName); - HistoryEntry historyEntry = persistResource( - new HistoryEntry.Builder() - .setType(HistoryEntry.Type.DOMAIN_DELETE) - .setParent(domain) - .build()); - domain = persistResource(domain.asBuilder() - .setRegistrationExpirationTime(now.plusYears(5).plusDays(45)) - .setDeletionTime(now.plusDays(35)) - .addGracePeriod(GracePeriod.create( - GracePeriodStatus.REDEMPTION, now.plusDays(1), "foo", null)) - .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE)) - .setDeletePollMessage(Key.create(persistResource( - new PollMessage.OneTime.Builder() - .setClientId("TheRegistrar") - .setEventTime(now.plusDays(5)) - .setParent(historyEntry) - .build()))) - .build()); + HistoryEntry historyEntry = + persistResource( + new HistoryEntry.Builder() + .setType(HistoryEntry.Type.DOMAIN_DELETE) + .setParent(domain) + .build()); + domain = + persistResource( + domain + .asBuilder() + .setRegistrationExpirationTime(now.plusYears(5).plusDays(45)) + .setDeletionTime(now.plusDays(35)) + .addGracePeriod( + GracePeriod.create(GracePeriodStatus.REDEMPTION, now.plusDays(1), "foo", null)) + .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE)) + .setDeletePollMessage( + Key.create( + persistResource( + new PollMessage.OneTime.Builder() + .setClientId("TheRegistrar") + .setEventTime(now.plusDays(5)) + .setParent(historyEntry) + .build()))) + .build()); clock.advanceOneMilli(); } @@ -185,14 +199,14 @@ public class TldSpecificLogicProxyTest extends ShardableTestCase { assertThat(price.getFees().get(1).getDescription()).isEqualTo("restore"); assertThat(price.getCredits()).isEmpty(); } - + @Test public void test_extraLogic_restorePrice_noDomain() throws Exception { thrown.expect(ResourceToMutateDoesNotExistException.class); TldSpecificLogicProxy.getRestorePrice( Registry.get("test"), "renew-13.test", "clientIdentifier", clock.nowUtc(), null); } - + @Test public void test_extraLogic_transferPrice() throws Exception { persistActiveDomain("renew-26.test"); @@ -205,7 +219,7 @@ public class TldSpecificLogicProxyTest extends ShardableTestCase { assertThat(price.getFees().get(0).getDescription()).isEqualTo("renew"); assertThat(price.getCredits()).isEmpty(); } - + @Test public void test_extraLogic_updatePrice() throws Exception { persistActiveDomain("update-13.test"); diff --git a/javatests/google/registry/model/registry/RegistryTest.java b/javatests/google/registry/model/registry/RegistryTest.java index dcc2ef3fd..9d53f0b58 100644 --- a/javatests/google/registry/model/registry/RegistryTest.java +++ b/javatests/google/registry/model/registry/RegistryTest.java @@ -38,6 +38,7 @@ import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.ReservedList; import google.registry.testing.ExceptionRule; +import java.math.BigDecimal; import org.joda.money.Money; import org.joda.time.DateTime; import org.junit.Before; @@ -414,7 +415,7 @@ public class RegistryTest extends EntityTestCase { @Test public void testEapFee_undefined() { assertThat(Registry.get("tld").getEapFeeFor(clock.nowUtc()).getCost()) - .isEqualTo(Money.of(USD, 0)); + .isEqualTo(BigDecimal.ZERO.setScale(2)); } @Test @@ -428,11 +429,12 @@ public class RegistryTest extends EntityTestCase { a, Money.of(USD, 100), b, Money.of(USD, 50))).build(); - assertThat(registry.getEapFeeFor(clock.nowUtc()).getCost()).isEqualTo(Money.of(USD, 100)); + assertThat(registry.getEapFeeFor(clock.nowUtc()).getCost()) + .isEqualTo(new BigDecimal("100.00")); assertThat(registry.getEapFeeFor(clock.nowUtc().minusDays(2)).getCost()) - .isEqualTo(Money.of(USD, 0)); + .isEqualTo(BigDecimal.ZERO.setScale(2)); assertThat(registry.getEapFeeFor(clock.nowUtc().plusDays(2)).getCost()) - .isEqualTo(Money.of(USD, 50)); + .isEqualTo(new BigDecimal("50.00")); } @Test diff --git a/javatests/google/registry/tools/CreateTldCommandTest.java b/javatests/google/registry/tools/CreateTldCommandTest.java index 2bd8a8b0f..79c961534 100644 --- a/javatests/google/registry/tools/CreateTldCommandTest.java +++ b/javatests/google/registry/tools/CreateTldCommandTest.java @@ -32,6 +32,7 @@ import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.math.BigDecimal; import org.joda.money.Money; import org.joda.time.DateTime; import org.junit.Before; @@ -103,10 +104,12 @@ public class CreateTldCommandTest extends CommandTestCase { "xn--q9jyb4c"); Registry registry = Registry.get("xn--q9jyb4c"); - assertThat(registry.getEapFeeFor(now.minusHours(1)).getCost()).isEqualTo(Money.zero(USD)); - assertThat(registry.getEapFeeFor(now.plusHours(1)).getCost()).isEqualTo(Money.of(USD, 50)); + assertThat(registry.getEapFeeFor(now.minusHours(1)).getCost()) + .isEqualTo(BigDecimal.ZERO.setScale(2)); + assertThat(registry.getEapFeeFor(now.plusHours(1)).getCost()) + .isEqualTo(new BigDecimal("50.00")); assertThat(registry.getEapFeeFor(now.plusDays(1).plusHours(1)).getCost()) - .isEqualTo(Money.of(USD, 10)); + .isEqualTo(new BigDecimal("10.00")); } @Test @@ -155,8 +158,7 @@ public class CreateTldCommandTest extends CommandTestCase { @Test public void testSuccess_createBillingCostFlag() throws Exception { - runCommandForced( - "--create_billing_cost=\"USD 42.42\"", "--roid_suffix=Q9JYB4C", "xn--q9jyb4c"); + runCommandForced("--create_billing_cost=\"USD 42.42\"", "--roid_suffix=Q9JYB4C", "xn--q9jyb4c"); assertThat(Registry.get("xn--q9jyb4c").getStandardCreateCost()).isEqualTo(Money.of(USD, 42.42)); } @@ -218,8 +220,7 @@ public class CreateTldCommandTest extends CommandTestCase { "--initial_tld_state=SUNRISE", "--roid_suffix=Q9JYB4C", "xn--q9jyb4c"); - assertThat(Registry.get("xn--q9jyb4c").getLrpTldStates()) - .containsExactly(TldState.SUNRISE); + assertThat(Registry.get("xn--q9jyb4c").getLrpTldStates()).containsExactly(TldState.SUNRISE); } @Test @@ -272,7 +273,7 @@ public class CreateTldCommandTest extends CommandTestCase { String.format("--tld_state_transitions=%s=PREDELEGATION,%s=SUNRISE", now, now.plus(1)), "--initial_tld_state=GENERAL_AVAILABILITY", "xn--q9jyb4c"); - } + } @Test public void testFailure_negativeInitialRenewBillingCost() throws Exception { @@ -286,8 +287,7 @@ public class CreateTldCommandTest extends CommandTestCase { thrown.expect(IllegalArgumentException.class); runCommandForced( String.format( - "--eap_fee_schedule=\"%s=JPY 123456\"", - START_OF_TIME.toString(DATETIME_FORMAT)), + "--eap_fee_schedule=\"%s=JPY 123456\"", START_OF_TIME.toString(DATETIME_FORMAT)), "--roid_suffix=Q9JYB4C", "xn--q9jyb4c"); } @@ -351,7 +351,8 @@ public class CreateTldCommandTest extends CommandTestCase { @Test public void testFailure_setReservedListFromOtherTld() throws Exception { - runFailureReservedListsTest("tld_banned", + runFailureReservedListsTest( + "tld_banned", IllegalArgumentException.class, "The reserved list(s) tld_banned cannot be applied to the tld xn--q9jyb4c"); } @@ -363,7 +364,8 @@ public class CreateTldCommandTest extends CommandTestCase { @Test public void testFailure_setCommonAndReservedListFromOtherTld() throws Exception { - runFailureReservedListsTest("common_abuse,tld_banned", + runFailureReservedListsTest( + "common_abuse,tld_banned", IllegalArgumentException.class, "The reserved list(s) tld_banned cannot be applied to the tld xn--q9jyb4c"); } @@ -381,7 +383,8 @@ public class CreateTldCommandTest extends CommandTestCase { @Test public void testFailure_setMultipleReservedListsFromOtherTld() throws Exception { - runFailureReservedListsTest("tld_banned,soy_expurgated", + runFailureReservedListsTest( + "tld_banned,soy_expurgated", IllegalArgumentException.class, "The reserved list(s) tld_banned, soy_expurgated cannot be applied to the tld xn--q9jyb4c"); } @@ -393,7 +396,8 @@ public class CreateTldCommandTest extends CommandTestCase { @Test public void testFailure_setNonExistentReservedLists() throws Exception { - runFailureReservedListsTest("xn--q9jyb4c_asdf,common_asdsdgh", + runFailureReservedListsTest( + "xn--q9jyb4c_asdf,common_asdsdgh", IllegalStateException.class, "Could not find reserved list xn--q9jyb4c_asdf to add to the tld"); } @@ -458,7 +462,8 @@ public class CreateTldCommandTest extends CommandTestCase { } private void runReservedListsTestOverride(String reservedLists) throws Exception { - runCommandForced("--override_reserved_list_rules", + runCommandForced( + "--override_reserved_list_rules", "--reserved_lists", reservedLists, "--roid_suffix=Q9JYB4C", @@ -466,9 +471,8 @@ public class CreateTldCommandTest extends CommandTestCase { } private void runFailureReservedListsTest( - String reservedLists, - Class errorClass, - String errorMsg) throws Exception { + String reservedLists, Class errorClass, String errorMsg) + throws Exception { thrown.expect(errorClass, errorMsg); runCommandForced("--reserved_lists", reservedLists, "--roid_suffix=Q9JYB4C", "xn--q9jyb4c"); }