Extract premium pricing to a PricingEngine interface

This refactors the existing premium list functionality into the new
class StaticPremiumListPricingEngine, which implements PricingEngine.
A backfill @OnLoad is provided to default existing Registry entities
into the static implementation.  For now there is just this one
implementation.  Dagger map multibinding is used to generate the total
set of allowed pricing engines, and allows other parties to plug in
their own implementations.

The pricing engine is a required field on the Registry object.  If you
don't want a particular Registry to actually have a premium list, then
use the static pricing engine but don't actually set a premium list.

A subsequent CL will refactor the Key<PremiumList> field on the
Registry entity class to be handled solely by the
StaticPremiumListPricingEngine implementation.  Going forward, all
configuration and implementation details that are specific to a given
pricing engine should be handled by that pricing engine, and not as
fields on the Registry object.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121850176
This commit is contained in:
mcilwain 2016-05-09 10:30:13 -07:00 committed by Justine Tunney
parent 87961fbb12
commit 047bbf186e
32 changed files with 659 additions and 200 deletions

View file

@ -40,6 +40,7 @@ java_library(
"//java/google/registry/mapreduce/inputs", "//java/google/registry/mapreduce/inputs",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/monitoring/whitebox", "//java/google/registry/monitoring/whitebox",
"//java/google/registry/pricing",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/security:servlets", "//java/google/registry/security:servlets",
"//java/google/registry/tldconfig/idn", "//java/google/registry/tldconfig/idn",

View file

@ -35,6 +35,7 @@ import static google.registry.model.EppResourceUtils.loadByUniqueId;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName; import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation; import static google.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
@ -176,8 +177,7 @@ public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Build
tldState = registry.getTldState(now); tldState = registry.getTldState(now);
checkRegistryStateForTld(tld); checkRegistryStateForTld(tld);
domainLabel = domainName.parts().get(0); domainLabel = domainName.parts().get(0);
createCost = createCost = getDomainCreateCost(targetId, now, getClientId(), command.getPeriod().getValue());
registry.getDomainCreateCost(targetId, now, getClientId(), command.getPeriod().getValue());
// The TLD should always be the parent of the requested domain name. // The TLD should always be the parent of the requested domain name.
isAnchorTenantViaReservation = matchesAnchorTenantReservation( isAnchorTenantViaReservation = matchesAnchorTenantReservation(
domainLabel, tld, command.getAuthInfo().getPw().getValue()); domainLabel, tld, command.getAuthInfo().getPw().getValue());
@ -202,7 +202,7 @@ public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Build
} else if (isClaimsCreate) { } else if (isClaimsCreate) {
throw new ClaimsPeriodEndedException(tld); throw new ClaimsPeriodEndedException(tld);
} }
verifyPremiumNameIsNotBlocked(targetId, now, getClientId(), tld); verifyPremiumNameIsNotBlocked(targetId, now, getClientId());
} }
verifyUnitIsYears(command.getPeriod()); verifyUnitIsYears(command.getPeriod());
verifyNotInPendingDelete( verifyNotInPendingDelete(

View file

@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.flows.domain.DomainFlowUtils.getReservationType; import static google.registry.flows.domain.DomainFlowUtils.getReservationType;
import static google.registry.model.EppResourceUtils.loadByUniqueId; import static google.registry.model.EppResourceUtils.loadByUniqueId;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -103,15 +104,19 @@ public class DomainAllocateFlow extends DomainCreateOrAllocateFlow {
.setFlags(billingFlagsBuilder.add(Flag.ALLOCATION).build()) .setFlags(billingFlagsBuilder.add(Flag.ALLOCATION).build())
.setTargetId(targetId) .setTargetId(targetId)
.setClientId(getClientId()) .setClientId(getClientId())
.setCost(registry // Note that the cost is calculated as of now, i.e. the event time, not the billing time,
.getDomainCreateCost(targetId, now, getClientId(), command.getPeriod().getValue())) // which may be some additional days into the future.
.setCost(
getDomainCreateCost(targetId, now, getClientId(), command.getPeriod().getValue()))
.setPeriodYears(command.getPeriod().getValue()) .setPeriodYears(command.getPeriod().getValue())
.setEventTime(now) .setEventTime(now)
// If there are no nameservers on the domain, then they get the benefit of the sunrush add // If there are no nameservers on the domain, then they get the benefit of the sunrush
// grace period, which is longer than the standard add grace period. // add grace period, which is longer than the standard add grace period.
.setBillingTime(now.plus(sunrushAddGracePeriod .setBillingTime(
? registry.getSunrushAddGracePeriodLength() now.plus(
: registry.getAddGracePeriodLength())) sunrushAddGracePeriod
? registry.getSunrushAddGracePeriodLength()
: registry.getAddGracePeriodLength()))
.setParent(historyEntry) .setParent(historyEntry)
.build(); .build();
ReservationType reservationType = getReservationType(domainName); ReservationType reservationType = getReservationType(domainName);

View file

@ -18,6 +18,7 @@ import static google.registry.flows.domain.DomainFlowUtils.getReservationType;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest; import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.checkResourcesExist;
import static google.registry.model.registry.label.ReservationType.UNRESERVED; import static google.registry.model.registry.label.ReservationType.UNRESERVED;
import static google.registry.pricing.PricingEngineProxy.isPremiumName;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DomainNameUtils.getTldFromSld; import static google.registry.util.DomainNameUtils.getTldFromSld;
@ -77,7 +78,7 @@ public class DomainCheckFlow extends BaseDomainCheckFlow {
ReservationType reservationType = getReservationType(domainName); ReservationType reservationType = getReservationType(domainName);
Registry registry = Registry.get(domainName.parent().toString()); Registry registry = Registry.get(domainName.parent().toString());
if (reservationType == UNRESERVED if (reservationType == UNRESERVED
&& registry.isPremiumName(domainName, now, getClientId()) && isPremiumName(domainName, now, getClientId())
&& registry.getPremiumPriceAckRequired() && registry.getPremiumPriceAckRequired()
&& !nullToEmpty(sessionMetadata.getServiceExtensionUris()).contains( && !nullToEmpty(sessionMetadata.getServiceExtensionUris()).contains(
ServiceExtension.FEE_0_6.getUri())) { ServiceExtension.FEE_0_6.getUri())) {

View file

@ -20,6 +20,7 @@ import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurr
import static google.registry.model.eppoutput.Result.Code.Success; import static google.registry.model.eppoutput.Result.Code.Success;
import static google.registry.model.eppoutput.Result.Code.SuccessWithActionPending; import static google.registry.model.eppoutput.Result.Code.SuccessWithActionPending;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -139,8 +140,7 @@ public class DomainDeleteFlow extends ResourceSyncDeleteFlow<DomainResource, Bui
TimeOfYear recurrenceTimeOfYear = TimeOfYear recurrenceTimeOfYear =
checkNotNull(gracePeriod.getRecurringBillingEvent()).get().getRecurrenceTimeOfYear(); checkNotNull(gracePeriod.getRecurringBillingEvent()).get().getRecurrenceTimeOfYear();
DateTime autoRenewTime = recurrenceTimeOfYear.getLastInstanceBeforeOrAt(now); DateTime autoRenewTime = recurrenceTimeOfYear.getLastInstanceBeforeOrAt(now);
cost = Registry.get(existingResource.getTld()) cost = getDomainRenewCost(targetId, autoRenewTime, getClientId(), 1);
.getDomainRenewCost(targetId, autoRenewTime, getClientId(), 1);
} else { } else {
cost = checkNotNull(gracePeriod.getOneTimeBillingEvent()).get().getCost(); cost = checkNotNull(gracePeriod.getOneTimeBillingEvent()).get().getCost();
} }

View file

@ -24,6 +24,9 @@ import static google.registry.flows.EppXmlTransformer.unmarshal;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName; import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.label.ReservedList.getReservation; import static google.registry.model.registry.label.ReservedList.getReservation;
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.pricing.PricingEngineProxy.isPremiumName;
import static google.registry.tldconfig.idn.IdnLabelValidator.findValidIdnTableForTld; import static google.registry.tldconfig.idn.IdnLabelValidator.findValidIdnTableForTld;
import static google.registry.util.CollectionUtils.nullToEmpty; import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.isAtOrAfter; import static google.registry.util.DateTimeUtils.isAtOrAfter;
@ -385,11 +388,11 @@ public class DomainFlowUtils {
* this registrar. * this registrar.
*/ */
static void verifyPremiumNameIsNotBlocked( static void verifyPremiumNameIsNotBlocked(
String domainName, DateTime priceTime, String clientId, String tld) throws EppException { String domainName, DateTime priceTime, String clientIdentifier) throws EppException {
if (Registry.get(tld).isPremiumName(domainName, priceTime, clientId)) { if (isPremiumName(domainName, priceTime, clientIdentifier)) {
// NB: The load of the Registar object is transactionless, which means that it should hit // NB: The load of the Registar object is transactionless, which means that it should hit
// memcache most of the time. // memcache most of the time.
if (Registrar.loadByClientId(clientId).getBlockPremiumNames()) { if (Registrar.loadByClientId(clientIdentifier).getBlockPremiumNames()) {
throw new PremiumNameBlockedException(); throw new PremiumNameBlockedException();
} }
} }
@ -585,7 +588,7 @@ public class DomainFlowUtils {
.setPeriod(feeRequest.getPeriod()) .setPeriod(feeRequest.getPeriod())
// Choose from four classes: premium, premium-collision, collision, or null (standard case). // Choose from four classes: premium, premium-collision, collision, or null (standard case).
.setClass(emptyToNull(Joiner.on('-').skipNulls().join( .setClass(emptyToNull(Joiner.on('-').skipNulls().join(
registry.isPremiumName(domainName, now, clientIdentifier) ? "premium" : null, isPremiumName(domainName, now, clientIdentifier) ? "premium" : null,
isNameCollisionInSunrise ? "collision" : null))); isNameCollisionInSunrise ? "collision" : null)));
switch (feeCommand.getCommand()) { switch (feeCommand.getCommand()) {
@ -597,9 +600,7 @@ public class DomainFlowUtils {
} else { } else {
builder.setFee( builder.setFee(
Fee.create( Fee.create(
registry getDomainCreateCost(domainName, now, clientIdentifier, years).getAmount(),
.getDomainCreateCost(domainName, now, clientIdentifier, years)
.getAmount(),
"create")); "create"));
} }
break; break;
@ -610,7 +611,7 @@ public class DomainFlowUtils {
// Restores have a "renew" and a "restore" fee. // Restores have a "renew" and a "restore" fee.
builder.setFee( builder.setFee(
Fee.create( Fee.create(
registry.getDomainRenewCost(domainName, now, clientIdentifier, years).getAmount(), getDomainRenewCost(domainName, now, clientIdentifier, years).getAmount(),
"renew"), "renew"),
Fee.create(registry.getStandardRestoreCost().getAmount(), "restore")); Fee.create(registry.getStandardRestoreCost().getAmount(), "restore"));
break; break;
@ -618,7 +619,7 @@ public class DomainFlowUtils {
// Anything else (transfer|renew) will have a "renew" fee. // Anything else (transfer|renew) will have a "renew" fee.
builder.setFee( builder.setFee(
Fee.create( Fee.create(
registry.getDomainRenewCost(domainName, now, clientIdentifier, years).getAmount(), getDomainRenewCost(domainName, now, clientIdentifier, years).getAmount(),
"renew")); "renew"));
} }
} }
@ -634,7 +635,7 @@ public class DomainFlowUtils {
throws EppException { throws EppException {
Registry registry = Registry.get(tld); Registry registry = Registry.get(tld);
if (registry.getPremiumPriceAckRequired() if (registry.getPremiumPriceAckRequired()
&& registry.isPremiumName(domainName, priceTime, clientIdentifier) && isPremiumName(domainName, priceTime, clientIdentifier)
&& feeCommand == null) { && feeCommand == null) {
throw new FeesRequiredForPremiumNameException(); throw new FeesRequiredForPremiumNameException();
} }

View file

@ -24,6 +24,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS; import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS;
import static google.registry.model.eppoutput.Result.Code.Success; import static google.registry.model.eppoutput.Result.Code.Success;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.DateTimeUtils.leapSafeAddYears; import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -109,8 +110,7 @@ public class DomainRenewFlow extends OwnedResourceMutateFlow<DomainResource, Ren
existingResource.getRegistrationExpirationTime().toLocalDate())) { existingResource.getRegistrationExpirationTime().toLocalDate())) {
throw new IncorrectCurrentExpirationDateException(); throw new IncorrectCurrentExpirationDateException();
} }
renewCost = Registry.get(existingResource.getTld()) renewCost = getDomainRenewCost(targetId, now, getClientId(), command.getPeriod().getValue());
.getDomainRenewCost(targetId, now, getClientId(), command.getPeriod().getValue());
validateFeeChallenge( validateFeeChallenge(
targetId, existingResource.getTld(), now, getClientId(), feeRenew, renewCost); targetId, existingResource.getTld(), now, getClientId(), feeRenew, renewCost);
} }

View file

@ -22,6 +22,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved;
import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked; import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
import static google.registry.model.eppoutput.Result.Code.Success; import static google.registry.model.eppoutput.Result.Code.Success;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -98,11 +99,11 @@ public class DomainRestoreRequestFlow extends OwnedResourceMutateFlow<DomainReso
checkAllowedAccessToTld(getAllowedTlds(), tld); checkAllowedAccessToTld(getAllowedTlds(), tld);
if (!superuser) { if (!superuser) {
verifyNotReserved(InternetDomainName.from(targetId), false); verifyNotReserved(InternetDomainName.from(targetId), false);
verifyPremiumNameIsNotBlocked(targetId, now, getClientId(), tld); verifyPremiumNameIsNotBlocked(targetId, now, getClientId());
} }
feeUpdate = eppInput.getSingleExtension(FeeUpdateExtension.class); feeUpdate = eppInput.getSingleExtension(FeeUpdateExtension.class);
restoreCost = Registry.get(tld).getStandardRestoreCost(); restoreCost = Registry.get(tld).getStandardRestoreCost();
renewCost = Registry.get(tld).getDomainRenewCost(targetId, now, getClientId(), 1); renewCost = getDomainRenewCost(targetId, now, getClientId(), 1);
validateFeeChallenge(targetId, tld, now, getClientId(), feeUpdate, restoreCost, renewCost); validateFeeChallenge(targetId, tld, now, getClientId(), feeUpdate, restoreCost, renewCost);
} }

View file

@ -18,6 +18,7 @@ import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToT
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime; import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -76,10 +77,10 @@ public class DomainTransferApproveFlow extends
.setTargetId(targetId) .setTargetId(targetId)
.setClientId(gainingClientId) .setClientId(gainingClientId)
.setPeriodYears(extraYears) .setPeriodYears(extraYears)
.setCost(Registry.get(tld).getDomainRenewCost( .setCost(getDomainRenewCost(
targetId, targetId,
transferData.getTransferRequestTime(), transferData.getTransferRequestTime(),
getClientId(), transferData.getGainingClientId(),
extraYears)) extraYears))
.setEventTime(now) .setEventTime(now)
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength())) .setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))

View file

@ -22,6 +22,7 @@ import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNo
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears; import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
import static google.registry.model.domain.DomainResource.extendRegistrationWithCap; import static google.registry.model.domain.DomainResource.extendRegistrationWithCap;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -113,8 +114,8 @@ public class DomainTransferRequestFlow
} }
Registry registry = Registry.get(existingResource.getTld()); Registry registry = Registry.get(existingResource.getTld());
automaticTransferTime = now.plus(registry.getAutomaticTransferLength()); automaticTransferTime = now.plus(registry.getAutomaticTransferLength());
renewCost = registry // Note that the gaining registrar is used to calculate the cost of the renewal.
.getDomainRenewCost(targetId, now, getClientId(), command.getPeriod().getValue()); renewCost = getDomainRenewCost(targetId, now, getClientId(), command.getPeriod().getValue());
transferBillingEvent = new BillingEvent.OneTime.Builder() transferBillingEvent = new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER) .setReason(Reason.TRANSFER)
.setTargetId(targetId) .setTargetId(targetId)
@ -152,7 +153,7 @@ public class DomainTransferRequestFlow
protected final void verifyTransferRequestIsAllowed() throws EppException { protected final void verifyTransferRequestIsAllowed() throws EppException {
verifyUnitIsYears(command.getPeriod()); verifyUnitIsYears(command.getPeriod());
if (!superuser) { if (!superuser) {
verifyPremiumNameIsNotBlocked(targetId, now, getClientId(), existingResource.getTld()); verifyPremiumNameIsNotBlocked(targetId, now, getClientId());
} }
validateFeeChallenge( validateFeeChallenge(
targetId, existingResource.getTld(), now, getClientId(), feeTransfer, renewCost); targetId, existingResource.getTld(), now, getClientId(), feeTransfer, renewCost);

View file

@ -0,0 +1,31 @@
// 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.pricing;
import com.google.common.base.Optional;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** A plugin interface for premium pricing engines. */
public interface PricingEngine {
/**
* Returns the premium price for the given second-level domain name at the given time for the
* given registrar, or absent if the domain name isn't premium.
*/
public Optional<Money> getPremiumPrice(
String secondLevelDomainName, DateTime priceTime, String clientIdentifier);
}

View file

@ -0,0 +1,53 @@
// 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.pricing;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.util.DomainNameUtils.getTldFromSld;
import com.google.common.base.Optional;
import com.google.common.net.InternetDomainName;
import google.registry.model.registry.Registry;
import google.registry.model.registry.label.PremiumList;
import org.joda.money.Money;
import org.joda.time.DateTime;
import javax.inject.Inject;
/** A premium list pricing engine that stores static pricing information in Datastore entities. */
public final class StaticPremiumListPricingEngine implements PricingEngine {
@Inject StaticPremiumListPricingEngine() {}
@Override
public Optional<Money> getPremiumPrice(
String secondLevelDomainName, DateTime priceTime, String clientIdentifier) {
// Note that clientIdentifier and priceTime are not used for determining premium pricing for
// static premium lists.
String tld = getTldFromSld(secondLevelDomainName);
Registry registry = Registry.get(checkNotNull(tld, "tld"));
if (registry.getPremiumList() == null) {
return Optional.<Money>absent();
}
String listName = registry.getPremiumList().getName();
Optional<PremiumList> premiumList = PremiumList.get(listName);
checkState(premiumList.isPresent(), "Could not load premium list: %s", listName);
String label = InternetDomainName.from(secondLevelDomainName).parts().get(0);
return premiumList.get().getPremiumPrice(label);
}
}

View file

@ -21,7 +21,6 @@ import static com.google.common.base.Predicates.not;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION; import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION;
import static google.registry.model.registry.label.PremiumList.getPremiumPrice;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@ -47,6 +46,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Mapify; import com.googlecode.objectify.annotation.Mapify;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.OnSave; import com.googlecode.objectify.annotation.OnSave;
import com.googlecode.objectify.annotation.Parent; import com.googlecode.objectify.annotation.Parent;
@ -57,6 +57,8 @@ import google.registry.model.ImmutableObject;
import google.registry.model.common.EntityGroupRoot; import google.registry.model.common.EntityGroupRoot;
import google.registry.model.common.TimedTransitionProperty; import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.common.TimedTransitionProperty.TimedTransition; import google.registry.model.common.TimedTransitionProperty.TimedTransition;
import google.registry.model.pricing.PricingEngine;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.ReservedList; import google.registry.model.registry.label.ReservedList;
import google.registry.util.Idn; import google.registry.util.Idn;
@ -238,6 +240,27 @@ public class Registry extends ImmutableObject implements Buildable {
CACHE.invalidate(tldStr); CACHE.invalidate(tldStr);
} }
/**
* Backfill the Registry entities that were saved before this field was added.
*
* <p>Note that this defaults to the {@link StaticPremiumListPricingEngine}.
*/
// TODO(b/26901539): Remove this backfill once it is populated on all Registry entities.
@OnLoad
void backfillPricingEngine() {
if (pricingEngineClassName == null) {
pricingEngineClassName = StaticPremiumListPricingEngine.class.getCanonicalName();
}
}
/**
* The fully qualified canonical classname of the pricing engine that this TLD uses.
*
* <p>This must be a valid key for the map of pricing engines injected by
* <code>@Inject Map<String, PricingEngine></code>
*/
String pricingEngineClassName;
/** /**
* The unicode-aware representation of the TLD associated with this {@link Registry}. * The unicode-aware representation of the TLD associated with this {@link Registry}.
* <p> * <p>
@ -271,7 +294,7 @@ public class Registry extends ImmutableObject implements Buildable {
return nullToEmptyImmutableCopy(reservedLists); return nullToEmptyImmutableCopy(reservedLists);
} }
/** The {@link PremiumList} for this TLD. */ /** The static {@link PremiumList} for this TLD, if there is one. */
Key<PremiumList> premiumList; Key<PremiumList> premiumList;
/** Should RDE upload a nightly escrow deposit for this TLD? */ /** Should RDE upload a nightly escrow deposit for this TLD? */
@ -442,7 +465,10 @@ public class Registry extends ImmutableObject implements Buildable {
return currency; return currency;
} }
/** Use {@link #getDomainCreateCost} instead of this to find the cost for a domain create. */ /**
* Use <code>PricingUtils.getDomainCreateCost</code> instead of this to find the cost for a
* domain create.
*/
@VisibleForTesting @VisibleForTesting
public Money getStandardCreateCost() { public Money getStandardCreateCost() {
return createBillingCost; return createBillingCost;
@ -457,8 +483,9 @@ public class Registry extends ImmutableObject implements Buildable {
} }
/** /**
* Use {@link #getDomainRenewCost} instead of this to find the cost for a domain renew, and all * Use <code>PricingUtils.getDomainRenewCost</code> instead of this to find the cost for a domain
* derived costs (i.e. autorenews, transfers, and the per-domain part of a restore cost). * renewal, and all derived costs (i.e. autorenews, transfers, and the per-domain part of a
* restore cost).
*/ */
@VisibleForTesting @VisibleForTesting
public Money getStandardRenewCost(DateTime now) { public Money getStandardRenewCost(DateTime now) {
@ -480,47 +507,6 @@ public class Registry extends ImmutableObject implements Buildable {
return renewBillingCostTransitions.toValueMap(); return renewBillingCostTransitions.toValueMap();
} }
private Optional<Money> getPremiumPriceForSld(String sldName) {
return getPremiumPriceForSld(InternetDomainName.from(sldName));
}
private Optional<Money> getPremiumPriceForSld(InternetDomainName domainName) {
checkArgument(getTld().equals(domainName.parent()),
"Domain name %s is not an SLD for TLD %s", domainName.toString(), tldStr);
String label = domainName.parts().get(0);
return getPremiumPrice(label, tldStr);
}
/** Returns true if the given domain name is on the premium price list. */
public boolean isPremiumName(String domainName, DateTime priceTime, String clientIdentifier) {
return isPremiumName(InternetDomainName.from(domainName), priceTime, clientIdentifier);
}
/** Returns true if the given domain name is on the premium price list. */
@SuppressWarnings("unused")
public boolean isPremiumName(
InternetDomainName domainName, DateTime priceTime, String clientIdentifier) {
return getPremiumPriceForSld(domainName).isPresent();
}
/** Returns the billing cost for registering the specified domain name for this many years. */
@SuppressWarnings("unused")
public Money getDomainCreateCost(
String domainName, DateTime priceTime, String clientIdentifier, int years) {
checkArgument(years > 0, "Number of years must be positive");
Money annualCost = getPremiumPriceForSld(domainName).or(getStandardCreateCost());
return annualCost.multipliedBy(years);
}
/** Returns the billing cost for renewing the specified domain name for this many years. */
@SuppressWarnings("unused")
public Money getDomainRenewCost(
String domainName, DateTime priceTime, String clientIdentifier, int years) {
checkArgument(years > 0, "Number of years must be positive");
Money annualCost = getPremiumPriceForSld(domainName).or(getStandardRenewCost(priceTime));
return annualCost.multipliedBy(years);
}
public String getLordnUsername() { public String getLordnUsername() {
return lordnUsername; return lordnUsername;
} }
@ -529,6 +515,10 @@ public class Registry extends ImmutableObject implements Buildable {
return claimsPeriodEnd; return claimsPeriodEnd;
} }
public String getPricingEngineClassName() {
return pricingEngineClassName;
}
public ImmutableSet<String> getAllowedRegistrantContactIds() { public ImmutableSet<String> getAllowedRegistrantContactIds() {
return nullToEmptyImmutableCopy(allowedRegistrantContactIds); return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
} }
@ -595,6 +585,12 @@ public class Registry extends ImmutableObject implements Buildable {
return this; return this;
} }
public Builder setPricingEngineClass(Class<? extends PricingEngine> pricingEngineClass) {
getInstance().pricingEngineClassName =
checkArgumentNotNull(pricingEngineClass).getCanonicalName();
return this;
}
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) { public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
checkArgument(addGracePeriodLength.isLongerThan(Duration.ZERO), checkArgument(addGracePeriodLength.isLongerThan(Duration.ZERO),
"addGracePeriodLength must be non-zero"); "addGracePeriodLength must be non-zero");
@ -800,6 +796,8 @@ public class Registry extends ImmutableObject implements Buildable {
return money.getCurrencyUnit().equals(instance.currency); return money.getCurrencyUnit().equals(instance.currency);
}}), }}),
"Renew cost must be in the registry's currency"); "Renew cost must be in the registry's currency");
checkArgumentNotNull(
instance.pricingEngineClassName, "All registries must have a configured pricing engine");
instance.tldStrId = tldName; instance.tldStrId = tldName;
instance.tldUnicode = Idn.toUnicode(tldName); instance.tldUnicode = Idn.toUnicode(tldName);
return super.build(); return super.build();

View file

@ -14,7 +14,6 @@
package google.registry.monitoring.whitebox; package google.registry.monitoring.whitebox;
import static dagger.Provides.Type.MAP;
import static google.registry.monitoring.whitebox.EntityIntegrityAlertsSchema.ENTITY_INTEGRITY_ALERTS_SCHEMA_FIELDS; import static google.registry.monitoring.whitebox.EntityIntegrityAlertsSchema.ENTITY_INTEGRITY_ALERTS_SCHEMA_FIELDS;
import static google.registry.monitoring.whitebox.EntityIntegrityAlertsSchema.TABLE_ID; import static google.registry.monitoring.whitebox.EntityIntegrityAlertsSchema.TABLE_ID;
import static google.registry.monitoring.whitebox.EppMetrics.EPPMETRICS_SCHEMA_FIELDS; import static google.registry.monitoring.whitebox.EppMetrics.EPPMETRICS_SCHEMA_FIELDS;
@ -27,6 +26,7 @@ import com.google.common.collect.ImmutableList;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey; import dagger.multibindings.StringKey;
import google.registry.request.Parameter; import google.registry.request.Parameter;
@ -41,13 +41,13 @@ import javax.servlet.http.HttpServletRequest;
@Module @Module
public class WhiteboxModule { public class WhiteboxModule {
@Provides(type = MAP) @Provides @IntoMap
@StringKey(EPPMETRICS_TABLE_ID) @StringKey(EPPMETRICS_TABLE_ID)
static ImmutableList<TableFieldSchema> provideEppMetricsSchema() { static ImmutableList<TableFieldSchema> provideEppMetricsSchema() {
return EPPMETRICS_SCHEMA_FIELDS; return EPPMETRICS_SCHEMA_FIELDS;
} }
@Provides(type = MAP) @Provides @IntoMap
@StringKey(TABLE_ID) @StringKey(TABLE_ID)
static ImmutableList<TableFieldSchema> provideEntityIntegrityAlertsSchema() { static ImmutableList<TableFieldSchema> provideEntityIntegrityAlertsSchema() {
return ENTITY_INTEGRITY_ALERTS_SCHEMA_FIELDS; return ENTITY_INTEGRITY_ALERTS_SCHEMA_FIELDS;

View file

@ -0,0 +1,28 @@
package(
default_visibility = ["//java/google/registry:registry_project"],
)
licenses(["notice"]) # Apache 2.0
java_library(
name = "pricing",
srcs = glob(["*.java"]),
deps = [
"//java/com/google/common/annotations",
"//java/com/google/common/base",
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//java/com/google/common/util/concurrent",
"//third_party/java/dagger",
"//third_party/java/joda_money",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/objectify:objectify-v4_1",
"//third_party/java/servlet/servlet_api",
"//java/google/registry/model",
"//java/google/registry/util",
],
)

View file

@ -0,0 +1,36 @@
// 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 dagger.Component;
import google.registry.model.pricing.PricingEngine;
import java.util.Map;
import javax.inject.Singleton;
/**
* Dagger component with instance lifetime for pricing engines.
*
* <p>This component only exists because the flows themselves are not yet injected. Once they are,
* this separate component can be removed, and the pricingEngines() map added to the relevant
* component used to construct the flows.
*/
@Singleton
@Component(modules = {PricingModule.class})
interface PricingComponent {
Map<Class<? extends PricingEngine>, PricingEngine> pricingEngines();
}

View file

@ -0,0 +1,107 @@
// 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.registry.Registries.assertTldExists;
import static google.registry.util.DomainNameUtils.getTldFromSld;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.InternetDomainName;
import google.registry.model.pricing.PricingEngine;
import google.registry.model.registry.Registry;
import org.joda.money.Money;
import org.joda.time.DateTime;
import java.util.Map;
/**
* A global proxy providing static methods for getting premium prices that dispatches requests
* correctly to the relevant {@link PricingEngine} implementation per TLD.
*/
public final class PricingEngineProxy {
private static final Map<Class<? extends PricingEngine>, PricingEngine> pricingEngineClasses =
DaggerPricingComponent.create().pricingEngines();
// Dagger map keys have to be provided with constant values that are known at compile time, so it
// can't be done using clazz.getCanonicalName(). So we construct the map by canonical name here,
// at runtime.
private static final ImmutableMap<String, PricingEngine> pricingEngines =
Maps.uniqueIndex(
pricingEngineClasses.values(),
new Function<PricingEngine, String>() {
@Override
public String apply(PricingEngine pricingEngine) {
return pricingEngine.getClass().getCanonicalName();
}});
/** Returns true if the given domain name is on the premium price list. */
public static boolean isPremiumName(
String domainName, DateTime priceTime, String clientIdentifier) {
return isPremiumName(InternetDomainName.from(domainName), priceTime, clientIdentifier);
}
/** Returns true if the given domain name is on the premium price list. */
public static boolean isPremiumName(
InternetDomainName domainName, DateTime priceTime, String clientIdentifier) {
return getPremiumPriceForDomainName(domainName, priceTime, clientIdentifier).isPresent();
}
/** Returns the billing cost for registering the specified domain name for this many years. */
public static Money getDomainCreateCost(
String domainName, DateTime priceTime, String clientIdentifier, int years) {
checkArgument(years > 0, "Number of years must be positive");
Optional<Money> annualCost =
getPremiumPriceForDomainName(
InternetDomainName.from(domainName), priceTime, clientIdentifier);
return annualCost
.or(Registry.get(getTldFromSld(domainName)).getStandardCreateCost())
.multipliedBy(years);
}
/** Returns the billing cost for renewing the specified domain name for this many years. */
public static Money getDomainRenewCost(
String domainName, DateTime priceTime, String clientIdentifier, int years) {
checkArgument(years > 0, "Number of years must be positive");
Optional<Money> annualCost =
getPremiumPriceForDomainName(
InternetDomainName.from(domainName), priceTime, clientIdentifier);
return annualCost
.or(Registry.get(getTldFromSld(domainName)).getStandardRenewCost(priceTime))
.multipliedBy(years);
}
/**
* Returns whether the given domain name is premium by dispatching to the appropriate
* {@link PricingEngine} based on what is configured for the TLD that the domain is under.
*/
private static Optional<Money> getPremiumPriceForDomainName(
InternetDomainName domainName, DateTime priceTime, String clientIdentifier) {
String tld = assertTldExists(getTldFromSld(domainName));
String clazz = Registry.get(tld).getPricingEngineClassName();
PricingEngine engine = pricingEngines.get(clazz);
checkState(engine != null, "Could not load pricing engine %s for TLD %s", clazz, tld);
return engine.getPremiumPrice(domainName.toString(), priceTime, clientIdentifier);
}
private PricingEngineProxy() {}
}

View file

@ -0,0 +1,47 @@
// 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 dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import google.registry.model.pricing.PricingEngine;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
/**
* Dagger module for injecting pricing engines.
*
* <p>To add a new pricing engine, create a new class that implements {@link PricingEngine}, and add
* a module that provides an instance of PricingEngine with a <code>PricingEngineClassKey</code>
* annotation with the class of the implementation and also <code>@Provides @IntoMap</code>
* annotations.
*/
@Module
public class PricingModule {
/** The annotation used for PricingEngine implementation keys. */
@MapKey
@interface PricingEngineClassKey {
Class<? extends PricingEngine> value();
}
@Provides @IntoMap
@PricingEngineClassKey(StaticPremiumListPricingEngine.class)
static PricingEngine provideStaticPremiumList(StaticPremiumListPricingEngine engine) {
return engine;
}
}

View file

@ -190,20 +190,20 @@ def domain_registry_repositories():
native.maven_jar( native.maven_jar(
name = "dagger", name = "dagger",
artifact = "com.google.dagger:dagger:2.0.2", artifact = "com.google.dagger:dagger:2.4",
sha1 = "de8416eda7b2fd7c25836b140c39e1cbf10542f6", sha1 = "6b290a792253035c9fcc912d6a4d7efb3e850211",
) )
native.maven_jar( native.maven_jar(
name = "dagger_compiler", name = "dagger_compiler",
artifact = "com.google.dagger:dagger-compiler:2.0.2", artifact = "com.google.dagger:dagger-compiler:2.4",
sha1 = "1170f75c1ce293f80755bbc9fcd60e0765022bd0", sha1 = "01053c9ef441e93088c9261c33163f6af30766b7",
) )
native.maven_jar( native.maven_jar(
name = "dagger_producers", name = "dagger_producers",
artifact = "com.google.dagger:dagger-producers:2.0-beta", artifact = "com.google.dagger:dagger-producers:2.4",
sha1 = "80276338d1c2542ebebac542b535d1ecd48a3fd7", sha1 = "f334a19afdc2ce2d8d5191f8a0fac2321bdd50fc",
) )
native.maven_jar( native.maven_jar(

View file

@ -49,6 +49,7 @@ java_library(
"//java/google/registry/flows", "//java/google/registry/flows",
"//java/google/registry/keyring/api", "//java/google/registry/keyring/api",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/pricing",
"//java/google/registry/rde", "//java/google/registry/rde",
"//java/google/registry/security", "//java/google/registry/security",
"//java/google/registry/request:modules", "//java/google/registry/request:modules",

View file

@ -17,6 +17,7 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.model.registry.Registries.findTldForNameOrThrow; import static google.registry.model.registry.Registries.findTldForNameOrThrow;
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
@ -25,7 +26,6 @@ import com.google.template.soy.data.SoyMapData;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import google.registry.model.registry.Registry;
import google.registry.tools.Command.GtechCommand; import google.registry.tools.Command.GtechCommand;
import google.registry.tools.soy.CreateAnchorTenantSoyInfo; import google.registry.tools.soy.CreateAnchorTenantSoyInfo;
@ -81,15 +81,15 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand implements
@Override @Override
protected void initMutatingEppToolCommand() { protected void initMutatingEppToolCommand() {
checkArgument(superuser, "This command must be run as a superuser."); checkArgument(superuser, "This command must be run as a superuser.");
String tld = findTldForNameOrThrow(InternetDomainName.from(domainName)).toString(); findTldForNameOrThrow(InternetDomainName.from(domainName)); // Check that the tld exists.
if (isNullOrEmpty(password)) { if (isNullOrEmpty(password)) {
password = passwordGenerator.createPassword(PASSWORD_LENGTH); password = passwordGenerator.createPassword(PASSWORD_LENGTH);
} }
Money cost = null; Money cost = null;
if (fee) { if (fee) {
cost = Registry.get(tld) cost =
.getDomainCreateCost( getDomainCreateCost(
domainName, DateTime.now(UTC), clientIdentifier, DEFAULT_ANCHOR_TENANT_PERIOD_YEARS); domainName, DateTime.now(UTC), clientIdentifier, DEFAULT_ANCHOR_TENANT_PERIOD_YEARS);
} }

View file

@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSortedMap;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registry.Registries; import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
@ -259,8 +260,13 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
"The roid suffix %s is already in use", "The roid suffix %s is already in use",
roidSuffix); roidSuffix);
} }
Registry.Builder builder = oldRegistry == null // TODO(b/26901539): Add a flag to set the pricing engine once we have more than one option.
? new Registry.Builder().setTldStr(tld) : oldRegistry.asBuilder(); Registry.Builder builder =
oldRegistry == null
? new Registry.Builder()
.setTldStr(tld)
.setPricingEngineClass(StaticPremiumListPricingEngine.class)
: oldRegistry.asBuilder();
if (escrow != null) { if (escrow != null) {
builder.setEscrowEnabled(escrow); builder.setEscrowEnabled(escrow);

View file

@ -22,13 +22,10 @@ import com.beust.jcommander.Parameters;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.tools.server.CreatePremiumListAction; import google.registry.tools.server.CreatePremiumListAction;
import javax.annotation.Nullable;
/** Command to create a {@link PremiumList} on Datastore. */ /** Command to create a {@link PremiumList} on Datastore. */
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Datastore.") @Parameters(separators = " =", commandDescription = "Create a PremiumList in Datastore.")
public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand { public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
@Nullable
@Parameter( @Parameter(
names = {"-o", "--override"}, names = {"-o", "--override"},
description = "Override restrictions on premium list naming") description = "Override restrictions on premium list naming")

View file

@ -46,6 +46,7 @@ java_library(
"//java/google/registry/mapreduce", "//java/google/registry/mapreduce",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/monitoring/whitebox", "//java/google/registry/monitoring/whitebox",
"//java/google/registry/pricing",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/security", "//java/google/registry/security",
"//java/google/registry/security:servlets", "//java/google/registry/security:servlets",

View file

@ -16,6 +16,7 @@ package google.registry.flows.domain;
import static com.google.common.io.BaseEncoding.base16; import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.pricing.PricingEngineProxy.isPremiumName;
import static google.registry.testing.DatastoreHelper.assertBillingEvents; import static google.registry.testing.DatastoreHelper.assertBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.deleteTld; import static google.registry.testing.DatastoreHelper.deleteTld;
@ -179,10 +180,9 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
.setReason(Reason.CREATE) .setReason(Reason.CREATE)
.setTargetId(getUniqueIdFromCommand()) .setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar") .setClientId("TheRegistrar")
.setCost(Registry.get(domainTld) .setCost(isPremiumName(getUniqueIdFromCommand(), clock.nowUtc(), "TheRegistrar")
.isPremiumName(getUniqueIdFromCommand(), clock.nowUtc(), "TheRegistrar") ? Money.of(USD, 200)
? Money.of(USD, 200) : Money.of(USD, 26))
: Money.of(USD, 26))
.setPeriodYears(2) .setPeriodYears(2)
.setEventTime(clock.nowUtc()) .setEventTime(clock.nowUtc())
.setBillingTime(billingTime) .setBillingTime(billingTime)

View file

@ -16,9 +16,9 @@ package google.registry.model;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.RoidSuffixes.getRoidSuffixForTld; import static google.registry.model.RoidSuffixes.getRoidSuffixForTld;
import static google.registry.testing.DatastoreHelper.newRegistry;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import google.registry.model.registry.Registry;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import org.junit.Rule; import org.junit.Rule;
@ -37,7 +37,7 @@ public class RoidSuffixesTest {
@Test @Test
public void test_newlyCreatedRegistry_isAddedToRoidSuffixesList() { public void test_newlyCreatedRegistry_isAddedToRoidSuffixesList() {
persistResource(new Registry.Builder().setTldStr("tld").setRoidSuffix("MEOW").build()); persistResource(newRegistry("tld", "MEOW"));
assertThat(getRoidSuffixForTld("tld")).isEqualTo("MEOW"); assertThat(getRoidSuffixForTld("tld")).isEqualTo("MEOW");
} }
} }

View file

@ -21,6 +21,7 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.label.ReservedListTest.GET_NAME_FUNCTION; import static google.registry.model.registry.label.ReservedListTest.GET_NAME_FUNCTION;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newRegistry;
import static google.registry.testing.DatastoreHelper.persistPremiumList; import static google.registry.testing.DatastoreHelper.persistPremiumList;
import static google.registry.testing.DatastoreHelper.persistReservedList; import static google.registry.testing.DatastoreHelper.persistReservedList;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
@ -115,9 +116,11 @@ public class RegistryTest extends EntityTestCase {
"tld-reserved16", "tld-reserved16",
"port,FULLY_BLOCKED", "port,FULLY_BLOCKED",
"manteau,FULLY_BLOCKED"); "manteau,FULLY_BLOCKED");
Registry registry1 = new Registry.Builder() Registry registry1 =
.setTldStr("propter") newRegistry("propter", "PROPTER")
.setReservedLists(ImmutableSet.of(rl15)).build(); .asBuilder()
.setReservedLists(ImmutableSet.of(rl15))
.build();
assertThat(registry1.getReservedLists()).hasSize(1); assertThat(registry1.getReservedLists()).hasSize(1);
Registry registry2 = registry1.asBuilder() Registry registry2 = registry1.asBuilder()
.setReservedLists(ImmutableSet.of(rl15, rl16)) .setReservedLists(ImmutableSet.of(rl15, rl16))
@ -128,7 +131,7 @@ public class RegistryTest extends EntityTestCase {
@Test @Test
public void testGetReservedLists_doesntReturnNullWhenUninitialized() throws Exception { public void testGetReservedLists_doesntReturnNullWhenUninitialized() throws Exception {
Registry registry = new Registry.Builder().setTldStr("foo").build(); Registry registry = newRegistry("foo", "FOO");
assertThat(registry.getReservedLists()).isNotNull(); assertThat(registry.getReservedLists()).isNotNull();
assertThat(registry.getReservedLists()).isEmpty(); assertThat(registry.getReservedLists()).isEmpty();
} }
@ -307,57 +310,6 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getStandardRenewCost(END_OF_TIME)).isEqualTo(Money.of(USD, 11)); assertThat(registry.getStandardRenewCost(END_OF_TIME)).isEqualTo(Money.of(USD, 11));
} }
@Test
public void testIsPremiumDomain() throws Exception {
createTld("example");
Registry registry = Registry.get("example");
assertThat(registry.isPremiumName("poor.example", clock.nowUtc(), "TheRegistrar")).isFalse();
assertThat(registry.isPremiumName("rich.example", clock.nowUtc(), "TheRegistrar")).isTrue();
assertThat(registry.isPremiumName("richer.example", clock.nowUtc(), "TheRegistrar")).isTrue();
}
public void testGetDomainCreateCost() throws Exception {
// The example tld has a premium price for "rich".
createTld("example");
Registry registry = Registry.get("example");
// The default value of 17 is set in createTld().
assertThat(registry.getDomainCreateCost("poor.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 13));
assertThat(registry.getDomainCreateCost("poor.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 26));
assertThat(registry.getDomainCreateCost("rich.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(registry.getDomainCreateCost("rich.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
}
@Test
public void testGetDomainRenewCost() throws Exception {
// The example tld has a premium price for "rich".
createTld("example");
Registry registry = Registry.get("example").asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 8),
clock.nowUtc(), Money.of(USD, 10)))
.build();
assertThat(registry.getDomainRenewCost("poor.example", START_OF_TIME, "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 8));
assertThat(registry.getDomainRenewCost("poor.example", START_OF_TIME, "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 16));
assertThat(registry.getDomainRenewCost("poor.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 10));
assertThat(registry.getDomainRenewCost("poor.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 20));
assertThat(registry.getDomainRenewCost("rich.example", START_OF_TIME, "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(registry.getDomainRenewCost("rich.example", START_OF_TIME, "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
assertThat(registry.getDomainRenewCost("rich.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(registry.getDomainRenewCost("rich.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
}
@Test @Test
public void testFailure_tldNeverSet() { public void testFailure_tldNeverSet() {
thrown.expect(IllegalArgumentException.class, "No registry TLD specified."); thrown.expect(IllegalArgumentException.class, "No registry TLD specified.");
@ -404,6 +356,13 @@ public class RegistryTest extends EntityTestCase {
.build(); .build();
} }
@Test
public void testFailure_pricingEngineIsRequired() {
thrown.expect(
IllegalArgumentException.class, "All registries must have a configured pricing engine");
new Registry.Builder().setTldStr("invalid").build();
}
@Test @Test
public void testFailure_negativeRenewBillingCostTransitionValue() { public void testFailure_negativeRenewBillingCostTransitionValue() {
thrown.expect(IllegalArgumentException.class, "billing cost cannot be negative"); thrown.expect(IllegalArgumentException.class, "billing cost cannot be negative");
@ -454,45 +413,4 @@ public class RegistryTest extends EntityTestCase {
thrown.expect(IllegalArgumentException.class, "cost must be in the registry's currency"); thrown.expect(IllegalArgumentException.class, "cost must be in the registry's currency");
Registry.get("tld").asBuilder().setServerStatusChangeBillingCost(Money.of(EUR, 42)).build(); Registry.get("tld").asBuilder().setServerStatusChangeBillingCost(Money.of(EUR, 42)).build();
} }
@Test
public void testFailure_isPremiumNameForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
Registry.get("tld").isPremiumName("test.example", clock.nowUtc(), "TheRegistrar");
}
@Test
public void testFailure_isPremiumNameForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
Registry.get("example").isPremiumName("rich.sld.example", clock.nowUtc(), "TheRegistrar");
}
@Test
public void testFailure_getCreateCostForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
Registry.get("tld").getDomainCreateCost("test.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getCreateCostForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
Registry.get("example")
.getDomainCreateCost("rich.sld.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getRenewCostForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
Registry.get("tld").getDomainRenewCost("test.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getRenewCostForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
Registry.get("example")
.getDomainRenewCost("rich.sld.example", clock.nowUtc(), "TheRegistrar", 1);
}
} }

View file

@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.label.PremiumList.PremiumListEntry; import google.registry.model.registry.label.PremiumList.PremiumListEntry;
import google.registry.model.registry.label.PremiumList.PremiumListRevision; import google.registry.model.registry.label.PremiumList.PremiumListRevision;
@ -70,7 +71,11 @@ public class PremiumListTest {
@Test @Test
public void testGetPremiumPrice_returnsNoPriceWhenNoPremiumListConfigured() throws Exception { public void testGetPremiumPrice_returnsNoPriceWhenNoPremiumListConfigured() throws Exception {
createTld("ghost"); createTld("ghost");
persistResource(new Registry.Builder().setTldStr("ghost").build()); persistResource(
new Registry.Builder()
.setTldStr("ghost")
.setPricingEngineClass(StaticPremiumListPricingEngine.class)
.build());
assertThat(Registry.get("ghost").getPremiumList()).isNull(); assertThat(Registry.get("ghost").getPremiumList()).isNull();
assertThat(getPremiumPrice("blah", "ghost")).isAbsent(); assertThat(getPremiumPrice("blah", "ghost")).isAbsent();
} }

View file

@ -0,0 +1,34 @@
package(
default_testonly = 1,
default_visibility = ["//java/google/registry:registry_project"],
)
licenses(["notice"]) # Apache 2.0
load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
java_library(
name = "pricing",
srcs = glob(["*Test.java"]),
deps = [
"//java/com/google/common/collect",
"//java/com/google/common/io",
"//java/com/google/common/net",
"//third_party/java/joda_money",
"//third_party/java/joda_time",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/truth",
"//java/google/registry/model",
"//java/google/registry/pricing",
"//java/google/registry/util",
"//javatests/google/registry/testing",
],
)
GenTestRules(
name = "GeneratedTestRules",
test_files = glob(["*Test.java"]),
deps = [":pricing"],
)

View file

@ -0,0 +1,182 @@
// 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.pricing.PricingEngineProxy.getDomainCreateCost;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.pricing.PricingEngineProxy.isPremiumName;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistPremiumList;
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.ImmutableSortedMap;
import google.registry.model.registry.Registry;
import google.registry.model.registry.label.PremiumList;
import google.registry.testing.AppEngineRule;
import google.registry.testing.ExceptionRule;
import google.registry.testing.FakeClock;
import google.registry.util.Clock;
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;
/** Tests for {@link PricingEngineProxy}. */
@RunWith(JUnit4.class)
public class PricingEngineProxyTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
private Clock clock;
@Before
public void before() throws Exception {
PremiumList premiumList = persistPremiumList(
"rich,USD 100",
"richer,USD 999",
"fraction,USD 20.50");
createTld("moka");
persistResource(Registry.get("moka").asBuilder().setPremiumList(premiumList).build());
clock = new FakeClock(DateTime.parse("2016-03-17T12:01:00Z"));
}
@Test
public void test_getDomainCreateCost_multipleYears() {
assertThat(getDomainCreateCost("espresso.moka", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.parse("USD 13"));
assertThat(getDomainCreateCost("espresso.moka", clock.nowUtc(), "TheRegistrar", 5))
.isEqualTo(Money.parse("USD 65"));
assertThat(getDomainCreateCost("fraction.moka", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.parse("USD 20.50"));
assertThat(getDomainCreateCost("fraction.moka", clock.nowUtc(), "TheRegistrar", 3))
.isEqualTo(Money.parse("USD 61.50"));
}
@Test
public void test_getDomainRenewCost_multipleYears() {
assertThat(getDomainRenewCost("espresso.moka", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.parse("USD 11"));
assertThat(getDomainRenewCost("espresso.moka", clock.nowUtc(), "TheRegistrar", 5))
.isEqualTo(Money.parse("USD 55"));
assertThat(getDomainRenewCost("fraction.moka", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.parse("USD 20.50"));
assertThat(getDomainRenewCost("fraction.moka", clock.nowUtc(), "TheRegistrar", 3))
.isEqualTo(Money.parse("USD 61.50"));
}
@Test
public void testIsPremiumDomain() throws Exception {
createTld("example");
assertThat(isPremiumName("poor.example", clock.nowUtc(), "TheRegistrar")).isFalse();
assertThat(isPremiumName("rich.example", clock.nowUtc(), "TheRegistrar")).isTrue();
assertThat(isPremiumName("richer.example", clock.nowUtc(), "TheRegistrar")).isTrue();
}
@Test
public void testGetDomainCreateCost() throws Exception {
// The example tld has a premium price for "rich".
createTld("example");
// The default value of 17 is set in createTld().
assertThat(getDomainCreateCost("poor.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 13));
assertThat(getDomainCreateCost("poor.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 26));
assertThat(getDomainCreateCost("rich.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(getDomainCreateCost("rich.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
}
@Test
public void testGetDomainRenewCost() throws Exception {
// The example tld has a premium price for "rich".
createTld("example");
persistResource(
Registry.get("example")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 8), clock.nowUtc(), Money.of(USD, 10)))
.build());
assertThat(getDomainRenewCost("poor.example", START_OF_TIME, "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 8));
assertThat(getDomainRenewCost("poor.example", START_OF_TIME, "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 16));
assertThat(getDomainRenewCost("poor.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 10));
assertThat(getDomainRenewCost("poor.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 20));
assertThat(getDomainRenewCost("rich.example", START_OF_TIME, "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(getDomainRenewCost("rich.example", START_OF_TIME, "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
assertThat(getDomainRenewCost("rich.example", clock.nowUtc(), "TheRegistrar", 1))
.isEqualTo(Money.of(USD, 100));
assertThat(getDomainRenewCost("rich.example", clock.nowUtc(), "TheRegistrar", 2))
.isEqualTo(Money.of(USD, 200));
}
@Test
public void testFailure_isPremiumNameForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
isPremiumName("test.example", clock.nowUtc(), "TheRegistrar");
}
@Test
public void testFailure_isPremiumNameForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
isPremiumName("rich.sld.example", clock.nowUtc(), "TheRegistrar");
}
@Test
public void testFailure_getCreateCostForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
getDomainCreateCost("test.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getCreateCostForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
getDomainCreateCost("rich.sld.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getRenewCostForSldNotUnderTld() {
thrown.expect(IllegalArgumentException.class);
getDomainRenewCost("test.example", clock.nowUtc(), "TheRegistrar", 1);
}
@Test
public void testFailure_getRenewCostForSldSubdomain() throws Exception {
createTld("example");
thrown.expect(IllegalArgumentException.class);
getDomainRenewCost("rich.sld.example", clock.nowUtc(), "TheRegistrar", 1);
}
}

View file

@ -52,6 +52,7 @@ java_library(
"//java/google/registry/dns:constants", "//java/google/registry/dns:constants",
"//java/google/registry/flows", "//java/google/registry/flows",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/pricing",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/tmch", "//java/google/registry/tmch",
"//java/google/registry/util", "//java/google/registry/util",

View file

@ -25,6 +25,7 @@ import static google.registry.model.EppResourceUtils.createContactHostRoid;
import static google.registry.model.EppResourceUtils.createDomainRoid; import static google.registry.model.EppResourceUtils.createDomainRoid;
import static google.registry.model.domain.launch.ApplicationStatus.VALIDATED; import static google.registry.model.domain.launch.ApplicationStatus.VALIDATED;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.difference; import static google.registry.util.CollectionUtils.difference;
import static google.registry.util.CollectionUtils.union; import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.END_OF_TIME;
@ -78,6 +79,7 @@ import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex; import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.ofy.ObjectifyService; import google.registry.model.ofy.ObjectifyService;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
@ -232,6 +234,7 @@ public class DatastoreHelper {
.setServerStatusChangeBillingCost(Money.of(USD, 19)) .setServerStatusChangeBillingCost(Money.of(USD, 19))
// Always set a default premium list. Tests that don't want it can delete it. // Always set a default premium list. Tests that don't want it can delete it.
.setPremiumList(persistPremiumList(tld, DEFAULT_PREMIUM_LIST_CONTENTS.get())) .setPremiumList(persistPremiumList(tld, DEFAULT_PREMIUM_LIST_CONTENTS.get()))
.setPricingEngineClass(StaticPremiumListPricingEngine.class)
.build(); .build();
} }
@ -436,7 +439,7 @@ public class DatastoreHelper {
eventTime.plus(Registry.get(domain.getTld()).getTransferGracePeriodLength())) eventTime.plus(Registry.get(domain.getTld()).getTransferGracePeriodLength()))
.setClientId("NewRegistrar") .setClientId("NewRegistrar")
.setPeriodYears(extendedRegistrationYears) .setPeriodYears(extendedRegistrationYears)
.setCost(Registry.get(domain.getTld()).getDomainRenewCost( .setCost(getDomainRenewCost(
domain.getFullyQualifiedDomainName(), domain.getFullyQualifiedDomainName(),
costLookupTime, costLookupTime,
gainingClientId, gainingClientId,