mirror of
https://github.com/google/nomulus.git
synced 2025-05-19 18:59:35 +02:00
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:
parent
87961fbb12
commit
047bbf186e
32 changed files with 659 additions and 200 deletions
31
java/google/registry/model/pricing/PricingEngine.java
Normal file
31
java/google/registry/model/pricing/PricingEngine.java
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.ofy.ObjectifyService.ofy;
|
||||
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.DateTimeUtils.END_OF_TIME;
|
||||
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.Id;
|
||||
import com.googlecode.objectify.annotation.Mapify;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.annotation.OnSave;
|
||||
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.TimedTransitionProperty;
|
||||
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.ReservedList;
|
||||
import google.registry.util.Idn;
|
||||
|
@ -238,6 +240,27 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
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}.
|
||||
* <p>
|
||||
|
@ -271,7 +294,7 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
return nullToEmptyImmutableCopy(reservedLists);
|
||||
}
|
||||
|
||||
/** The {@link PremiumList} for this TLD. */
|
||||
/** The static {@link PremiumList} for this TLD, if there is one. */
|
||||
Key<PremiumList> premiumList;
|
||||
|
||||
/** Should RDE upload a nightly escrow deposit for this TLD? */
|
||||
|
@ -442,7 +465,10 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
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
|
||||
public Money getStandardCreateCost() {
|
||||
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
|
||||
* derived costs (i.e. autorenews, transfers, and the per-domain part of a restore cost).
|
||||
* Use <code>PricingUtils.getDomainRenewCost</code> instead of this to find the cost for a domain
|
||||
* renewal, and all derived costs (i.e. autorenews, transfers, and the per-domain part of a
|
||||
* restore cost).
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public Money getStandardRenewCost(DateTime now) {
|
||||
|
@ -480,47 +507,6 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
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() {
|
||||
return lordnUsername;
|
||||
}
|
||||
|
@ -529,6 +515,10 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
return claimsPeriodEnd;
|
||||
}
|
||||
|
||||
public String getPricingEngineClassName() {
|
||||
return pricingEngineClassName;
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getAllowedRegistrantContactIds() {
|
||||
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
|
||||
}
|
||||
|
@ -595,6 +585,12 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setPricingEngineClass(Class<? extends PricingEngine> pricingEngineClass) {
|
||||
getInstance().pricingEngineClassName =
|
||||
checkArgumentNotNull(pricingEngineClass).getCanonicalName();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
|
||||
checkArgument(addGracePeriodLength.isLongerThan(Duration.ZERO),
|
||||
"addGracePeriodLength must be non-zero");
|
||||
|
@ -800,6 +796,8 @@ public class Registry extends ImmutableObject implements Buildable {
|
|||
return money.getCurrencyUnit().equals(instance.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.tldUnicode = Idn.toUnicode(tldName);
|
||||
return super.build();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue