Compare commits

...
Author SHA1 Message Date
gbrodmanandGitHub 77ee124374 Add SQL change for per-TLD IDN tables (#1975) 2023-03-28 17:03:22 -04:00
Lai JiangandGitHub b9742adc0b Delete cron.xml (#1965)
We've successfully migrated to using Cloud Scheduler.
2023-03-23 14:29:06 -04:00
sarahcaseybotandGitHub d4cd25c4ae Add pricing logic for allocation tokens in domain renew (#1961)
* Add pricing logic for allocation tokens in domain renew

* Add clarifying comment

* Several fixes

* Add test for renewalPriceBehavior not changing
2023-03-23 14:00:36 -04:00
sarahcaseybotandGitHub 8b7e938ed6 Add TTL configs to Registry object (#1968)
* Add TTL configs to Registry object

* Change A and AAAA records TTL field name
2023-03-22 13:56:11 -04:00
Pavlo TkachandGitHub c216c874b4 Remove app engine deps from Lock flyway change (#1911) 2023-03-20 12:25:12 -04:00
Pavlo TkachandGitHub 0ab9471c8d Make cloud scheduler deployment part of gradle deploy (alpha, qa and crash only) (#1969) 2023-03-20 11:10:00 -04:00
sarahcaseybotandGitHub d482754f66 Implement default tokens for the fee extension in domain check flow (#1950)
* Implement default tokens for the fee extension in domain check

* Add test for expired token

* Add test for alloc token and default token

* Fix formatting

* Always check for default tokens

* Change transaction time to passed in DateTime
2023-03-17 15:41:17 -04:00
sarahcaseybotandGitHub fe086b43f5 Add TTL columns to the Tld table (#1964)
* Add TTL columns to Tld table

* Change A and AAAA records column name
2023-03-17 11:54:14 -04:00
39 changed files with 4066 additions and 2750 deletions
+1
View File
@@ -103,6 +103,7 @@ explodeWar.doLast {
file("${it.explodedAppDirectory}/WEB-INF/lib/tools.jar").setWritable(true)
}
appengineDeployAll.finalizedBy ':cloudSchedulerDeployer'
rootProject.deploy.dependsOn appengineDeployAll
rootProject.stage.dependsOn appengineStage
tasks['war'].dependsOn ':console-webapp:buildConsoleWebappProd'
+15
View File
@@ -558,6 +558,21 @@ task coreDev {
javadocDependentTasks.each { tasks.javadoc.dependsOn(it) }
// Runs the script, which deploys cloud scheduler tasks based on the config
task cloudSchedulerDeployer {
doLast {
def env = environment
if (!prodOrSandboxEnv) {
exec {
commandLine 'go', 'run',
"${rootDir}/release/builder/cloudSchedulerDeployer.go",
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
"domain-registry-${env}"
}
}
}
}
// disable javadoc in subprojects, these will break because they don't have
// the correct classpath (see above).
gradle.taskGraph.whenReady { graph ->
+1
View File
@@ -1028,6 +1028,7 @@ test {
// TODO(weiminyu): Remove dependency on sqlIntegrationTest
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest)
// When we override tests, we also break the cleanTest command.
cleanTest.dependsOn(cleanFragileTest, cleanOutcastTest, cleanStandardTest,
cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
@@ -36,6 +36,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.flows.custom.CustomLogicFactoryModule;
import google.registry.flows.custom.CustomLogicModule;
import google.registry.flows.domain.DomainPricingLogic;
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent.Cancellation;
import google.registry.model.billing.BillingEvent.Flag;
@@ -53,6 +54,7 @@ import google.registry.util.Clock;
import google.registry.util.SystemClock;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Optional;
import java.util.Set;
import javax.inject.Singleton;
import org.apache.beam.sdk.Pipeline;
@@ -383,24 +385,31 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
// It is OK to always create a OneTime, even though the domain might be deleted or transferred
// later during autorenew grace period, as a cancellation will always be written out in those
// instances.
OneTime oneTime =
new OneTime.Builder()
.setBillingTime(billingTime)
.setRegistrarId(recurring.getRegistrarId())
// Determine the cost for a one-year renewal.
.setCost(
domainPricingLogic
.getRenewPrice(tld, recurring.getTargetId(), eventTime, 1, recurring)
.getRenewCost())
.setEventTime(eventTime)
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
.setDomainHistory(historyEntry)
.setPeriodYears(1)
.setReason(recurring.getReason())
.setSyntheticCreationTime(endTime)
.setCancellationMatchingBillingEvent(recurring)
.setTargetId(recurring.getTargetId())
.build();
OneTime oneTime = null;
try {
oneTime =
new OneTime.Builder()
.setBillingTime(billingTime)
.setRegistrarId(recurring.getRegistrarId())
// Determine the cost for a one-year renewal.
.setCost(
domainPricingLogic
.getRenewPrice(
tld, recurring.getTargetId(), eventTime, 1, recurring, Optional.empty())
.getRenewCost())
.setEventTime(eventTime)
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
.setDomainHistory(historyEntry)
.setPeriodYears(1)
.setReason(recurring.getReason())
.setSyntheticCreationTime(endTime)
.setCancellationMatchingBillingEvent(recurring)
.setTargetId(recurring.getTargetId())
.build();
} catch (AllocationTokenInvalidForPremiumNameException e) {
// This should not be reached since we are not using an allocation token
return;
}
results.add(oneTime);
}
results.add(
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
<cronentries>
</cronentries>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
<cronentries>
</cronentries>
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
<cronentries>
</cronentries>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
<cronentries>
</cronentries>
@@ -11,9 +11,6 @@
<!--
This only needs to run once per day, but we launch additional jobs in case the
cursor is lagging behind, so it'll catch up to the current date eventually.
See <a href="../../../production/default/WEB-INF/cron.xml">production config</a> for an
explanation of job starting times.
-->
<schedule>7 */12 * * *</schedule>
</task>
@@ -38,6 +38,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException;
@@ -116,6 +117,7 @@ import org.joda.time.DateTime;
@ReportingSpec(ActivityReportField.DOMAIN_CHECK)
public final class DomainCheckFlow implements Flow {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject ResourceCommand resourceCommand;
@Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@@ -271,6 +273,12 @@ public final class DomainCheckFlow implements Flow {
for (FeeCheckCommandExtensionItem feeCheckItem : feeCheck.getItems()) {
for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) {
Optional<AllocationToken> defaultToken =
DomainFlowUtils.checkForDefaultToken(
Registry.get(InternetDomainName.from(domainName).parent().toString()),
domainName,
registrarId,
now);
FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder();
Optional<Domain> domain = Optional.ofNullable(domainObjs.get(domainName));
handleFeeRequest(
@@ -281,7 +289,7 @@ public final class DomainCheckFlow implements Flow {
feeCheck.getCurrency(),
now,
pricingLogic,
allocationToken,
allocationToken.isPresent() ? allocationToken : defaultToken,
availableDomains.contains(domainName),
recurrences.getOrDefault(domainName, null));
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
@@ -15,8 +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 com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
@@ -54,7 +52,6 @@ import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD;
import static google.registry.model.tld.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@@ -64,7 +61,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName;
import google.registry.dns.DnsUtils;
import google.registry.flows.EppException;
import google.registry.flows.EppException.AssociationProhibitsOperationException;
import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
import google.registry.flows.ExtensionManager;
@@ -121,9 +117,7 @@ import google.registry.model.tld.Registry.TldType;
import google.registry.model.tld.label.ReservationType;
import google.registry.model.tmch.ClaimsList;
import google.registry.model.tmch.ClaimsListDao;
import google.registry.persistence.VKey;
import google.registry.tmch.LordnTaskUtils.LordnPhase;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -271,6 +265,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
validateLaunchCreateNotice(launchCreate.get().getNotice(), domainLabel, isSuperuser, now);
}
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
// TODO(sarahbot@): Add check for valid EPP actions on the token
Optional<AllocationToken> allocationToken =
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
command,
@@ -280,7 +275,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
eppInput.getSingleExtension(AllocationTokenExtension.class));
boolean defaultTokenUsed = false;
if (!allocationToken.isPresent() && !registry.getDefaultPromoTokens().isEmpty()) {
allocationToken = checkForDefaultToken(registry, command);
allocationToken =
DomainFlowUtils.checkForDefaultToken(registry, command.getDomainName(), registrarId, now);
if (allocationToken.isPresent()) {
defaultTokenUsed = true;
}
@@ -454,36 +450,6 @@ public final class DomainCreateFlow implements TransactionalFlow {
.build();
}
private Optional<AllocationToken> checkForDefaultToken(
Registry registry, DomainCommand.Create command) throws EppException {
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
AllocationToken.getAll(registry.getDefaultPromoTokens());
ImmutableList<Optional<AllocationToken>> tokenList =
registry.getDefaultPromoTokens().stream()
.map(tokens::get)
.filter(Optional::isPresent)
.collect(toImmutableList());
checkState(
!isNullOrEmpty(tokenList),
"Failure while loading default TLD promotions from the database");
// Check if any of the tokens are valid for this domain registration
for (Optional<AllocationToken> token : tokenList) {
try {
AllocationTokenFlowUtils.validateToken(
InternetDomainName.from(command.getDomainName()),
token.get(),
registrarId,
tm().getTransactionTime());
} catch (AssociationProhibitsOperationException e) {
// Allocation token was not valid for this registration, continue to check the next token in
// the list
continue;
}
// Only use the first valid token in the list
return token;
}
return Optional.empty();
}
/**
* Verifies that signed marks are only sent during sunrise.
*
@@ -39,6 +39,7 @@ import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_ANCHO
import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
@@ -63,6 +64,7 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException;
import google.registry.flows.EppException.AssociationProhibitsOperationException;
import google.registry.flows.EppException.AuthorizationErrorException;
import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.ObjectDoesNotExistException;
@@ -72,6 +74,7 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException;
import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.flows.EppException.UnimplementedOptionException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
import google.registry.model.EppResource;
import google.registry.model.billing.BillingEvent;
@@ -671,6 +674,8 @@ public class DomainFlowUtils {
String feeClass = null;
ImmutableList<Fee> fees = ImmutableList.of();
switch (feeRequest.getCommandName()) {
// TODO(sarahbot@): Add check of valid EPP actions on token before passing the token to the
// fee request.
case CREATE:
// Don't return a create price for reserved names.
if (isReserved(domainName, isSunrise) && !isAvailable) {
@@ -695,7 +700,8 @@ public class DomainFlowUtils {
builder.setAvailIfSupported(true);
fees =
pricingLogic
.getRenewPrice(registry, domainNameString, now, years, recurringBillingEvent)
.getRenewPrice(
registry, domainNameString, now, years, recurringBillingEvent, allocationToken)
.getFees();
break;
case RESTORE:
@@ -1191,6 +1197,44 @@ public class DomainFlowUtils {
.getResultList();
}
/**
* Checks if there is a valid default token to be used for a domain create command.
*
* <p>If there is more than one valid default token for the registration, only the first valid
* token found on the TLD's default token list will be returned.
*/
public static Optional<AllocationToken> checkForDefaultToken(
Registry registry, String domainName, String registrarId, DateTime now) throws EppException {
if (isNullOrEmpty(registry.getDefaultPromoTokens())) {
return Optional.empty();
}
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
AllocationToken.getAll(registry.getDefaultPromoTokens());
ImmutableList<Optional<AllocationToken>> tokenList =
registry.getDefaultPromoTokens().stream()
.map(tokens::get)
.filter(Optional::isPresent)
.collect(toImmutableList());
checkState(
!isNullOrEmpty(tokenList),
"Failure while loading default TLD promotions from the database");
// Check if any of the tokens are valid for this domain registration
for (Optional<AllocationToken> token : tokenList) {
try {
AllocationTokenFlowUtils.validateToken(
InternetDomainName.from(domainName), token.get(), registrarId, now);
} catch (AssociationProhibitsOperationException | StatusProhibitsOperationException e) {
// Allocation token was not valid for this registration, continue to check the next token in
// the list
continue;
}
// Only use the first valid token in the list
return token;
}
// No valid default token found
return Optional.empty();
}
/** Resource linked to this domain does not exist. */
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
@@ -34,6 +34,7 @@ import google.registry.model.domain.fee.BaseFee;
import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
import google.registry.model.tld.Registry;
import java.math.RoundingMode;
@@ -112,21 +113,22 @@ public final class DomainPricingLogic {
String domainName,
DateTime dateTime,
int years,
@Nullable Recurring recurringBillingEvent) {
@Nullable Recurring recurringBillingEvent,
Optional<AllocationToken> allocationToken)
throws AllocationTokenInvalidForPremiumNameException {
checkArgument(years > 0, "Number of years must be positive");
Money renewCost;
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
boolean isRenewCostPremiumPrice;
// recurring billing event is null if the domain is still available. Billing events are created
// in the process of domain creation.
if (recurringBillingEvent == null) {
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
renewCost = domainPrices.getRenewCost().multipliedBy(years);
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
isRenewCostPremiumPrice = domainPrices.isPremium();
} else {
switch (recurringBillingEvent.getRenewalPriceBehavior()) {
case DEFAULT:
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
renewCost = domainPrices.getRenewCost().multipliedBy(years);
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
isRenewCostPremiumPrice = domainPrices.isPremium();
break;
// if the renewal price behavior is specified, then the renewal price should be the same
@@ -136,6 +138,7 @@ public final class DomainPricingLogic {
recurringBillingEvent.getRenewalPrice(),
"Unexpected behavior: renewal price cannot be null when renewal behavior is"
+ " SPECIFIED");
// Don't apply allocation token to renewal price when SPECIFIED
renewCost = recurringBillingEvent.getRenewalPrice().get().multipliedBy(years);
isRenewCostPremiumPrice = false;
break;
@@ -143,9 +146,11 @@ public final class DomainPricingLogic {
// at standard price of domains at the time, even if the domain is premium
case NONPREMIUM:
renewCost =
Registry.get(getTldFromDomainName(domainName))
.getStandardRenewCost(dateTime)
.multipliedBy(years);
getDomainCostWithDiscount(
false,
years,
allocationToken,
Registry.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
isRenewCostPremiumPrice = false;
break;
default:
@@ -202,7 +207,7 @@ public final class DomainPricingLogic {
@Nullable Recurring recurringBillingEvent)
throws EppException {
FeesAndCredits renewPrice =
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent, Optional.empty());
return customLogic.customizeTransferPrice(
TransferPriceParameters.newBuilder()
.setFeesAndCredits(
@@ -242,25 +247,40 @@ public final class DomainPricingLogic {
private Money getDomainCreateCostWithDiscount(
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
throws EppException {
return getDomainCostWithDiscount(
domainPrices.isPremium(), years, allocationToken, domainPrices.getCreateCost());
}
/** Returns the domain renew cost with allocation-token-related discounts applied. */
private Money getDomainRenewCostWithDiscount(
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
throws AllocationTokenInvalidForPremiumNameException {
return getDomainCostWithDiscount(
domainPrices.isPremium(), years, allocationToken, domainPrices.getRenewCost());
}
private Money getDomainCostWithDiscount(
boolean isPremium, int years, Optional<AllocationToken> allocationToken, Money oneYearCost)
throws AllocationTokenInvalidForPremiumNameException {
if (allocationToken.isPresent()
&& allocationToken.get().getDiscountFraction() != 0.0
&& domainPrices.isPremium()
&& isPremium
&& !allocationToken.get().shouldDiscountPremiums()) {
throw new AllocationTokenInvalidForPremiumNameException();
}
Money oneYearCreateCost = domainPrices.getCreateCost();
Money totalDomainCreateCost = oneYearCreateCost.multipliedBy(years);
Money totalDomainFlowCost = oneYearCost.multipliedBy(years);
// Apply the allocation token discount, if applicable.
if (allocationToken.isPresent()) {
if (allocationToken.isPresent()
&& allocationToken.get().getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
Money discount =
oneYearCreateCost.multipliedBy(
oneYearCost.multipliedBy(
discountedYears * allocationToken.get().getDiscountFraction(),
RoundingMode.HALF_EVEN);
totalDomainCreateCost = totalDomainCreateCost.minus(discount);
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
}
return totalDomainCreateCost;
return totalDomainFlowCost;
}
/** An allocation token was provided that is invalid for premium domains. */
@@ -172,6 +172,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
Renew command = (Renew) resourceCommand;
// Loads the target resource if it exists
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
// TODO(sarahbot@): Add check for valid EPP actions on the token
Optional<AllocationToken> allocationToken =
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
existingDomain,
@@ -198,7 +199,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
targetId,
now,
years,
existingRecurringBillingEvent);
existingRecurringBillingEvent,
allocationToken);
validateFeeChallenge(feeRenew, feesAndCredits, false);
flowCustomLogic.afterValidation(
AfterValidationParameters.newBuilder()
@@ -275,6 +275,26 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
}
}
/**
* The time to live for DNS A and AAAA records.
*
* <p>When this field is null, the "dnsDefaultATtl" value from the config file will be used.
*/
Duration dnsAPlusAaaaTtl;
/**
* The time to live for DNS NS records.
*
* <p>When this field is null, the "dnsDefaultNsTtl" value from the config file will be used.
*/
Duration dnsNsTtl;
/**
* The time to live for DNS DS records.
*
* <p>When this field is null, the "dnsDefaultDsTtl" value from the config file will be used.
*/
Duration dnsDsTtl;
/**
* The unicode-aware representation of the TLD associated with this {@link Registry}.
*
@@ -647,6 +667,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
return numDnsPublishLocks;
}
/** Returns the time to live for A and AAAA records. */
public Duration getDnsAPlusAaaaTtl() {
return dnsAPlusAaaaTtl;
}
/** Returns the time to live for NS records. */
public Duration getDnsNsTtl() {
return dnsNsTtl;
}
/** Returns the time to live for DS records. */
public Duration getDnsDsTtl() {
return dnsDsTtl;
}
public ImmutableSet<String> getAllowedRegistrantContactIds() {
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
}
@@ -737,6 +772,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
return this;
}
public Builder setDnsAPlusAaaaTtl(Duration dnsAPlusAaaaTtl) {
getInstance().dnsAPlusAaaaTtl = dnsAPlusAaaaTtl;
return this;
}
public Builder setDnsNsAtl(Duration dnsNsAtl) {
getInstance().dnsNsTtl = dnsNsAtl;
return this;
}
public Builder setDnsDsAtl(Duration dnsDsAtl) {
getInstance().dnsDsTtl = dnsDsAtl;
return this;
}
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
checkArgument(
addGracePeriodLength.isLongerThan(Duration.ZERO),
@@ -17,6 +17,7 @@ package google.registry.flows.domain;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
import static google.registry.model.eppoutput.CheckData.DomainCheck.create;
@@ -38,6 +39,7 @@ import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
@@ -350,6 +352,28 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml"));
}
@Test
void testSuccess_allocationTokenPromotion_doesNotUseValidDefaultToken_singleYear()
throws Exception {
setUpDefaultToken();
createTld("example");
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.setDiscountYears(2)
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
.put(clock.nowUtc().minusDays(1), TokenStatus.VALID)
.put(clock.nowUtc().plusDays(1), TokenStatus.ENDED)
.build())
.build());
setEppInput("domain_check_allocationtoken_fee.xml");
runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml"));
}
@Test
void testSuccess_allocationTokenPromotion_multiYearAndPremiums() throws Exception {
createTld("example");
@@ -760,6 +784,31 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_fee_response_v06.xml"));
}
private void setUpDefaultToken() {
AllocationToken defaultToken =
persistResource(
new AllocationToken.Builder()
.setToken("bbbbb")
.setTokenType(DEFAULT_PROMO)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setDiscountFraction(0.5)
.build());
persistResource(
Registry.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.build());
}
@Test
void testFeeExtension_defaultToken_v06() throws Exception {
setUpDefaultToken();
persistActiveDomain("example1.tld");
setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD"));
runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v06.xml"));
}
@Test
void testFeeExtension_multipleReservations() throws Exception {
persistResource(
@@ -780,6 +829,14 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_fee_response_v11.xml"));
}
@Test
void testFeeExtension_defaultToken_v11() throws Exception {
setUpDefaultToken();
persistActiveDomain("example1.tld");
setEppInput("domain_check_fee_v11.xml", ImmutableMap.of("CURRENCY", "USD"));
runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v11.xml"));
}
@Test
void testFeeExtension_v12() throws Exception {
persistActiveDomain("example1.tld");
@@ -787,6 +844,14 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml"));
}
@Test
void testFeeExtension_defaultToken_v12() throws Exception {
setUpDefaultToken();
persistActiveDomain("example1.tld");
setEppInput("domain_check_fee_v12.xml", ImmutableMap.of("CURRENCY", "USD"));
runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v12.xml"));
}
@Test
void testSuccess_thirtyDomains_restoreFees() throws Exception {
// Note that 30 is more than 25, which is the maximum # of entity groups you can enlist in a
@@ -810,6 +875,14 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v06.xml"));
}
@Test
void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v06() throws Exception {
setUpDefaultToken();
setEppInput("domain_check_fee_multiple_commands_v06.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_multiple_commands_default_token_response_v06.xml"));
}
// Version 11 cannot have multiple commands.
@Test
@@ -818,6 +891,80 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v12.xml"));
}
@Test
void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v12() throws Exception {
setUpDefaultToken();
setEppInput("domain_check_fee_multiple_commands_v12.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_multiple_commands_default_token_response_v12.xml"));
}
void testFeeExtension_defaultToken_notValidForAllLabels_v06() throws Exception {
createTld("example");
AllocationToken defaultToken =
persistResource(
new AllocationToken.Builder()
.setToken("bbbbb")
.setTokenType(DEFAULT_PROMO)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("example"))
.setDiscountPremiums(false)
.setDiscountFraction(0.5)
.build());
persistResource(
Registry.get("example")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.build());
setEppInput("domain_check_fee_default_token_multiple_names_v06.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_default_token_multiple_names_response_v06.xml"));
}
void testFeeExtension_defaultToken_notValidForAllLabels_v11() throws Exception {
createTld("example");
AllocationToken defaultToken =
persistResource(
new AllocationToken.Builder()
.setToken("bbbbb")
.setTokenType(DEFAULT_PROMO)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("example"))
.setDiscountPremiums(false)
.setDiscountFraction(0.5)
.build());
persistResource(
Registry.get("example")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.build());
setEppInput("domain_check_fee_default_token_multiple_names_v11.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_default_token_multiple_names_response_v11.xml"));
}
void testFeeExtension_defaultToken_notValidForAllLabels_v12() throws Exception {
createTld("example");
AllocationToken defaultToken =
persistResource(
new AllocationToken.Builder()
.setToken("bbbbb")
.setTokenType(DEFAULT_PROMO)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("example"))
.setDiscountPremiums(false)
.setDiscountFraction(0.5)
.build());
persistResource(
Registry.get("example")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.build());
setEppInput("domain_check_fee_default_token_multiple_names_v12.xml");
runFlowAssertResponse(
loadFile("domain_check_fee_default_token_multiple_names_response_v12.xml"));
}
/** Test the same as {@link #testFeeExtension_multipleCommands_v06} with premium labels. */
@Test
void testFeeExtension_premiumLabels_v06() throws Exception {
@@ -1577,7 +1577,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
}
@@ -1627,7 +1627,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.put("EXDATE", "2004-04-03T22:00:00.0Z")
.build()));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getCost()).isEqualTo(expectedPrice);
}
@@ -1660,7 +1660,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
// 1yr @ $100 + 2yrs @ $100 * (1 - 0.98) = $104
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 104.00));
@@ -1693,7 +1693,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
// 2yrs @ $100 + 1yr @ $100 * (1 - 0.95555) = $204.44
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 204.44));
@@ -1862,7 +1862,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
@@ -2012,6 +2012,41 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5)));
}
@Test
void testSuccess_skipsOverExpiredDefaultToken() throws Exception {
persistContactsAndHosts();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
AllocationToken defaultToken2 =
persistResource(
new AllocationToken.Builder()
.setToken("bbbbb")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED)
.put(clock.nowUtc().minusDays(2), TokenStatus.VALID)
.put(clock.nowUtc().minusDays(1), TokenStatus.ENDED)
.build())
.build());
persistResource(
Registry.get("tld")
.asBuilder()
.setDefaultPromoTokens(
ImmutableList.of(defaultToken1.createVKey(), defaultToken2.createVKey()))
.build());
doSuccessfulTest();
}
BillingEvent.OneTime runTest_defaultToken(String token) throws Exception {
setEppInput("domain_create.xml", ImmutableMap.of("DOMAIN", "example.tld"));
runFlowAssertResponse(
@@ -2023,7 +2058,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.put("EXDATE", "2001-04-03T22:00:00.0Z")
.build()));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(tm().transact(() -> tm().loadAllOf(BillingEvent.OneTime.class)));
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo(token);
return billingEvent;
@@ -20,6 +20,7 @@ import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.DE
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingEvent.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.domain.fee.BaseFee.FeeType.RENEW;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
@@ -32,11 +33,13 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import google.registry.flows.EppException;
import google.registry.flows.FlowMetadata;
import google.registry.flows.HttpSessionMetadata;
import google.registry.flows.SessionMetadata;
import google.registry.flows.custom.DomainPricingCustomLogic;
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.billing.BillingEvent.Recurring;
@@ -44,6 +47,7 @@ import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.fee.Fee;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppinput.EppInput;
import google.registry.model.tld.Registry;
import google.registry.persistence.transaction.JpaTestExtensions;
@@ -133,7 +137,8 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_oneYear_standardDomain_noBilling_isStandardPrice()
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(registry, "standard.example", clock.nowUtc(), 1, null))
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -145,7 +150,8 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_multiYear_standardDomain_noBilling_isStandardPrice()
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(registry, "standard.example", clock.nowUtc(), 5, null))
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -157,7 +163,8 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_oneYear_premiumDomain_noBilling_isPremiumPrice()
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(registry, "premium.example", clock.nowUtc(), 1, null))
domainPricingLogic.getRenewPrice(
registry, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -169,7 +176,8 @@ public class DomainPricingLogicTest {
void testGetDomainRenewPrice_multiYear_premiumDomain_noBilling_isPremiumPrice()
throws EppException {
assertThat(
domainPricingLogic.getRenewPrice(registry, "premium.example", clock.nowUtc(), 5, null))
domainPricingLogic.getRenewPrice(
registry, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -186,7 +194,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty())))
"premium.example", DEFAULT, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -194,6 +203,58 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void testGetDomainRenewPrice_oneYear_premiumDomain_default_withToken_isPremiumPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(true)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 50).getAmount(), RENEW, true))
.build());
}
@Test
void
testGetDomainRenewPrice_oneYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.build());
assertThrows(
AllocationTokenInvalidForPremiumNameException.class,
() ->
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)));
}
@Test
void testGetDomainRenewPrice_multiYear_premiumDomain_default_isPremiumCost() throws EppException {
assertThat(
@@ -203,7 +264,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty())))
"premium.example", DEFAULT, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -211,6 +273,60 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void testGetDomainRenewPrice_multiYear_premiumDomain_default_withToken_isPremiumPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(true)
.setDiscountYears(2)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 400).getAmount(), RENEW, true))
.build());
}
@Test
void
testGetDomainRenewPrice_multiYear_premiumDomain_default_withTokenNotValidForPremiums_throwsException()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.setDiscountYears(2)
.build());
assertThrows(
AllocationTokenInvalidForPremiumNameException.class,
() ->
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)));
}
@Test
void testGetDomainRenewPrice_oneYear_standardDomain_default_isNonPremiumPrice()
throws EppException {
@@ -221,7 +337,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", DEFAULT, Optional.empty())))
"standard.example", DEFAULT, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -229,6 +346,33 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void testGetDomainRenewPrice_oneYear_standardDomain_default_withToken_isDiscountedPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"standard.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"standard.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
.build());
}
@Test
void testGetDomainRenewPrice_multiYear_standardDomain_default_isNonPremiumCost()
throws EppException {
@@ -239,7 +383,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"standard.example", DEFAULT, Optional.empty())))
"standard.example", DEFAULT, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -247,6 +392,34 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void testGetDomainRenewPrice_multiYear_standardDomain_default_withToken_isDiscountedPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.setDiscountYears(2)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"standard.example",
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"standard.example", DEFAULT, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
.build());
}
@Test
void testGetDomainRenewPrice_oneYear_premiumDomain_anchorTenant_isNonPremiumPrice()
throws EppException {
@@ -257,7 +430,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", NONPREMIUM, Optional.empty())))
"premium.example", NONPREMIUM, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -265,6 +439,34 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void
testGetDomainRenewPrice_oneYear_premiumDomain_anchorTenant__withToken_isDiscountedNonPremiumPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", NONPREMIUM, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
.build());
}
@Test
void testGetDomainRenewPrice_multiYear_premiumDomain_anchorTenant_isNonPremiumCost()
throws EppException {
@@ -275,7 +477,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", NONPREMIUM, Optional.empty())))
"premium.example", NONPREMIUM, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -283,6 +486,35 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void
testGetDomainRenewPrice_multiYear_premiumDomain_anchorTenant__withToken_isDiscountedNonPremiumPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.setDiscountYears(2)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"premium.example",
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", NONPREMIUM, Optional.empty()),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 40).getAmount(), RENEW, false))
.build());
}
@Test
void testGetDomainRenewPrice_oneYear_standardDomain_anchorTenant_isNonPremiumPrice()
throws EppException {
@@ -293,7 +525,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"standard.example", NONPREMIUM, Optional.empty())))
"standard.example", NONPREMIUM, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -311,7 +544,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"standard.example", NONPREMIUM, Optional.empty())))
"standard.example", NONPREMIUM, Optional.empty()),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -329,7 +563,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1)))))
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -337,6 +572,71 @@ public class DomainPricingLogicTest {
.build());
}
@Test
void
testGetDomainRenewPrice_oneYear_standardDomain_internalRegistration_withToken_isSpecifiedPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"standard.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
Optional.of(allocationToken)))
// The allocation token should not discount the speicifed price
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
.build());
}
@Test
void
testGetDomainRenewPrice_oneYear_standardDomain_internalRegistration_withToken_doesNotChangePriceBehavior()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setRenewalPriceBehavior(DEFAULT)
.setDiscountPremiums(false)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"standard.example",
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
Optional.of(allocationToken)))
// The allocation token should not discount the speicifed price
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 1).getAmount(), RENEW, false))
.build());
assertThat(
Iterables.getLast(DatabaseHelper.loadAllOf(BillingEvent.Recurring.class))
.getRenewalPriceBehavior())
.isEqualTo(SPECIFIED);
}
@Test
void testGetDomainRenewPrice_multiYear_standardDomain_internalRegistration_isSpecifiedPrice()
throws EppException {
@@ -347,7 +647,36 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1)))))
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(Money.of(USD, 5).getAmount(), RENEW, false))
.build());
}
@Test
void
testGetDomainRenewPrice_multiYear_standardDomain_internalRegistration_withToken_isSpecifiedPrice()
throws EppException {
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDiscountFraction(0.5)
.setDiscountPremiums(false)
.build());
assertThat(
domainPricingLogic.getRenewPrice(
registry,
"standard.example",
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"standard.example", SPECIFIED, Optional.of(Money.of(USD, 1))),
Optional.of(allocationToken)))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -365,7 +694,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
1,
persistDomainAndSetRecurringBillingEvent(
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17)))))
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17))),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -383,7 +713,8 @@ public class DomainPricingLogicTest {
clock.nowUtc(),
5,
persistDomainAndSetRecurringBillingEvent(
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17)))))
"premium.example", SPECIFIED, Optional.of(Money.of(USD, 17))),
Optional.empty()))
.isEqualTo(
new FeesAndCredits.Builder()
.setCurrency(USD)
@@ -398,7 +729,7 @@ public class DomainPricingLogicTest {
IllegalArgumentException.class,
() ->
domainPricingLogic.getRenewPrice(
registry, "standard.example", clock.nowUtc(), -1, null));
registry, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
assertThat(thrown).hasMessageThat().isEqualTo("Number of years must be positive");
}
@@ -49,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.truth.Truth8;
import google.registry.flows.EppException;
import google.registry.flows.EppRequestSource;
@@ -604,12 +605,21 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example.tld")
.setDiscountFraction(0.5)
.setDiscountYears(1)
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryId()).isPresent();
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getAllocationToken().get().getKey())
.isEqualTo(allocationToken.getToken());
// Price is 50% off the first year only. Non-discounted price is $11.
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
}
@Test
@@ -619,11 +629,20 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
persistResource(
new AllocationToken.Builder().setToken("abc123").setTokenType(UNLIMITED_USE).build());
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5)
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
clock.advanceOneMilli();
setEppInput(
"domain_renew_allocationtoken.xml",
@@ -633,6 +652,38 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
loadFile(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "other-example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
billingEvent = Iterables.getLast(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("other-example.tld");
assertThat(billingEvent.getAllocationToken().get().getKey()).isEqualTo("abc123");
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 16.5));
}
@Test
void testSuccess_allocationTokenMultiYearDiscount() throws Exception {
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
persistDomain();
AllocationToken allocationToken =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setDomainName("example.tld")
.setDiscountFraction(0.5)
.setDiscountYears(10)
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
assertThat(DatabaseHelper.loadByEntity(allocationToken).getRedemptionHistoryId()).isPresent();
BillingEvent.OneTime billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.OneTime.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getAllocationToken().get().getKey())
.isEqualTo(allocationToken.getToken());
assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 11));
}
@Test
@@ -750,6 +801,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRedemptionHistoryId(historyEntryId)
.setDiscountFraction(0.5)
.build());
clock.advanceOneMilli();
EppException thrown =
@@ -0,0 +1,54 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">rich.example</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>rich.example</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example2.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">4.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example3.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">2</fee:period>
<fee:fee description="create">12.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,70 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">rich.example</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.11"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd avail="1">
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>example2.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>example3.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,71 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">rich.example</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.12"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:object>
<domain:name>rich.example</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:class>premium</fee:class>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example2.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example3.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,33 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>rich.example</domain:name>
<domain:name>example1.example</domain:name>
<domain:name>example2.example</domain:name>
<domain:name>example3.example</domain:name>
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:domain>
<fee:name>rich.example</fee:name>
<fee:command>create</fee:command>
</fee:domain>
<fee:domain>
<fee:name>example2.tld</fee:name>
<fee:currency>%CURRENCY%</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
</fee:domain>
<fee:domain>
<fee:name>example3.tld</fee:name>
<fee:currency>%CURRENCY%</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">2</fee:period>
</fee:domain>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -0,0 +1,24 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>rich.example</domain:name>
<domain:name>example1.tld</domain:name>
<domain:name>example2.tld</domain:name>
<domain:name>example3.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.11">
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:class>premium</fee:class>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -0,0 +1,25 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>rich.example</domain:name>
<domain:name>example1.tld</domain:name>
<domain:name>example2.tld</domain:name>
<domain:name>example3.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
<fee:currency>USD</fee:currency>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:class>premium</fee:class>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -0,0 +1,57 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">example1.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>renew</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">5.50</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>transfer</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>restore</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example1.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>update</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="update">0.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,68 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">example1.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.12"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">5.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="transfer">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="update">
<fee:period unit="y">1</fee:period>
<fee:fee description="update">0.00</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,43 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example2.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example3.tld</fee:name>
<fee:currency>USD</fee:currency>
<fee:command>create</fee:command>
<fee:period unit="y">2</fee:period>
<fee:fee description="create">19.50</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,57 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.11"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:cd avail="1">
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>example2.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
<domain:name>example3.tld</domain:name>
</fee:object>
<fee:command>create</fee:command>
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,58 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="0">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="1">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="1">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:fee-0.12"
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:object>
<domain:name>example1.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example2.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:object>
<domain:name>example3.tld</domain:name>
</fee:object>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -261,11 +261,11 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2023-03-14 17:02:14.207181</td>
<td class="property_value">2023-03-28 19:28:38.164363</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
<td id="lastFlywayFile" class="property_value">V140__rename_process_time_column_in_dns_refresh_request_table.sql</td>
<td id="lastFlywayFile" class="property_value">V143__idn_per_tld.sql</td>
</tr>
</tbody>
</table>
@@ -284,7 +284,7 @@ td.section {
generated on
</text>
<text text-anchor="start" x="3835.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
2023-03-14 17:02:14.207181
2023-03-28 19:28:38.164363
</text>
<polygon fill="none" stroke="#888888" points="3748,-4 3748,-44 4013,-44 4013,-4 3748,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
File diff suppressed because it is too large Load Diff
+3
View File
@@ -138,3 +138,6 @@ V137__add_process_time_column.sql
V138__drop_dns_refresh_request_time_column.sql
V139__add_allowed_epp_actions_column.sql
V140__rename_process_time_column_in_dns_refresh_request_table.sql
V141__add_ttl_columns_to_tld.sql
V142__drop_request_log_id.sql
V143__idn_per_tld.sql
@@ -0,0 +1,18 @@
-- Copyright 2023 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "Tld"
ADD COLUMN dns_a_plus_aaaa_ttl interval,
ADD COLUMN dns_ds_ttl interval,
ADD COLUMN dns_ns_ttl interval;
@@ -0,0 +1,16 @@
-- Copyright 2022 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "Lock" DROP COLUMN request_log_id;
@@ -0,0 +1,15 @@
-- Copyright 2023 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "Tld" ADD COLUMN IF NOT EXISTS idn_tables text[];
@@ -716,6 +716,9 @@
creation_time timestamptz not null,
currency text not null,
default_promo_tokens text[],
dns_a_plus_aaaa_ttl interval,
dns_ds_ttl interval,
dns_ns_ttl interval,
dns_paused boolean not null,
dns_writers text[] not null,
drive_folder_id text,
@@ -674,8 +674,7 @@ CREATE TABLE public."Lock" (
resource_name text NOT NULL,
scope text NOT NULL,
acquired_time timestamp with time zone NOT NULL,
expiration_time timestamp with time zone NOT NULL,
request_log_id text
expiration_time timestamp with time zone NOT NULL
);
@@ -1092,7 +1091,11 @@ CREATE TABLE public."Tld" (
tld_type text NOT NULL,
tld_unicode text NOT NULL,
transfer_grace_period_length interval NOT NULL,
default_promo_tokens text[]
default_promo_tokens text[],
dns_a_plus_aaaa_ttl interval,
dns_ds_ttl interval,
dns_ns_ttl interval,
idn_tables text[]
);
+1 -1
View File
@@ -59,7 +59,7 @@ steps:
else
project_id="domain-registry-${_ENV}"
fi
for filename in cron dispatch queue; do
for filename in dispatch queue; do
gcloud -q --project $project_id app deploy \
default/WEB-INF/appengine-generated/$filename.yaml
done