mirror of
https://github.com/google/nomulus.git
synced 2025-07-27 21:16:25 +02:00
Update renew flow to accept and process REMOVEPACKAGE token (#1768)
This commit is contained in:
parent
f623da9948
commit
17a21f3326
4 changed files with 80 additions and 3 deletions
|
@ -30,6 +30,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
|
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrationPeriod;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
|
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
|
||||||
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
import static google.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||||
|
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeApplyPackageRemovalToken;
|
||||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
|
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
|
||||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
|
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
@ -180,6 +181,10 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
now,
|
now,
|
||||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||||
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
||||||
|
|
||||||
|
// If client passed an applicable static token this updates the domain
|
||||||
|
existingDomain = maybeApplyPackageRemovalToken(existingDomain, allocationToken);
|
||||||
|
|
||||||
int years = command.getPeriod().getValue();
|
int years = command.getPeriod().getValue();
|
||||||
DateTime newExpirationTime =
|
DateTime newExpirationTime =
|
||||||
leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
|
leapSafeAddYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
|
||||||
|
@ -315,14 +320,14 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
throws EppException {
|
throws EppException {
|
||||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||||
verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES);
|
verifyNoDisallowedStatuses(existingDomain, RENEW_DISALLOWED_STATUSES);
|
||||||
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
|
|
||||||
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
|
||||||
if (!isSuperuser) {
|
if (!isSuperuser) {
|
||||||
verifyResourceOwnership(registrarId, existingDomain);
|
verifyResourceOwnership(registrarId, existingDomain);
|
||||||
checkAllowedAccessToTld(registrarId, existingDomain.getTld());
|
checkAllowedAccessToTld(registrarId, existingDomain.getTld());
|
||||||
checkHasBillingAccount(registrarId, existingDomain.getTld());
|
checkHasBillingAccount(registrarId, existingDomain.getTld());
|
||||||
}
|
}
|
||||||
verifyUnitIsYears(command.getPeriod());
|
verifyUnitIsYears(command.getPeriod());
|
||||||
|
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
|
||||||
|
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||||
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
||||||
if (!command.getCurrentExpirationDate().equals(
|
if (!command.getCurrentExpirationDate().equals(
|
||||||
existingDomain.getRegistrationExpirationTime().toLocalDate())) {
|
existingDomain.getRegistrationExpirationTime().toLocalDate())) {
|
||||||
|
@ -344,7 +349,11 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||||
.setPeriodYears(years)
|
.setPeriodYears(years)
|
||||||
.setCost(renewCost)
|
.setCost(renewCost)
|
||||||
.setEventTime(now)
|
.setEventTime(now)
|
||||||
.setAllocationToken(allocationToken.map(AllocationToken::createVKey).orElse(null))
|
.setAllocationToken(
|
||||||
|
allocationToken
|
||||||
|
.filter(t -> AllocationToken.TokenBehavior.DEFAULT.equals(t.getTokenBehavior()))
|
||||||
|
.map(AllocationToken::createVKey)
|
||||||
|
.orElse(null))
|
||||||
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
|
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
|
||||||
.setDomainHistoryId(
|
.setDomainHistoryId(
|
||||||
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
new DomainHistoryId(domainHistoryKey.getParent().getName(), domainHistoryKey.getId()))
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package google.registry.flows.domain.token;
|
package google.registry.flows.domain.token;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
@ -26,6 +27,8 @@ import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||||
|
import google.registry.model.billing.BillingEvent.Recurring;
|
||||||
|
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||||
import google.registry.model.domain.Domain;
|
import google.registry.model.domain.Domain;
|
||||||
import google.registry.model.domain.DomainCommand;
|
import google.registry.model.domain.DomainCommand;
|
||||||
import google.registry.model.domain.token.AllocationToken;
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
@ -214,6 +217,34 @@ public class AllocationTokenFlowUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Domain maybeApplyPackageRemovalToken(
|
||||||
|
Domain domain, Optional<AllocationToken> allocationToken) {
|
||||||
|
if (!allocationToken.isPresent()
|
||||||
|
|| !TokenBehavior.REMOVE_PACKAGE.equals(allocationToken.get().getTokenBehavior())) {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
Recurring newRecurringBillingEvent =
|
||||||
|
tm().loadByKey(domain.getAutorenewBillingEvent())
|
||||||
|
.asBuilder()
|
||||||
|
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||||
|
.setRenewalPrice(null)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// the Recurring billing event is reloaded later in the renew flow, so we synchronize changed
|
||||||
|
// RecurringBillingEvent with storage manually
|
||||||
|
tm().put(newRecurringBillingEvent);
|
||||||
|
jpaTm().getEntityManager().flush();
|
||||||
|
jpaTm().getEntityManager().clear();
|
||||||
|
|
||||||
|
// Remove current package token
|
||||||
|
return domain
|
||||||
|
.asBuilder()
|
||||||
|
.setCurrentPackageToken(null)
|
||||||
|
.setAutorenewBillingEvent(newRecurringBillingEvent.createVKey())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
// Note: exception messages should be <= 32 characters long for domain check results
|
// Note: exception messages should be <= 32 characters long for domain check results
|
||||||
|
|
||||||
/** The allocation token is not currently valid. */
|
/** The allocation token is not currently valid. */
|
||||||
|
|
|
@ -292,6 +292,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VKey<AllocationToken> createVKey() {
|
public VKey<AllocationToken> createVKey() {
|
||||||
|
if (!AllocationToken.TokenBehavior.DEFAULT.equals(getTokenBehavior())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("%s tokens are not stored in the database", getTokenBehavior()));
|
||||||
|
}
|
||||||
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
|
import com.google.common.truth.Truth8;
|
||||||
import com.googlecode.objectify.Key;
|
import com.googlecode.objectify.Key;
|
||||||
import google.registry.flows.EppException;
|
import google.registry.flows.EppException;
|
||||||
import google.registry.flows.EppRequestSource;
|
import google.registry.flows.EppRequestSource;
|
||||||
|
@ -1261,4 +1262,36 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||||
assertThrows(RemovePackageTokenOnNonPackageDomainException.class, this::runFlow);
|
assertThrows(RemovePackageTokenOnNonPackageDomainException.class, this::runFlow);
|
||||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccesfullyAppliesRemovePackageToken() throws Exception {
|
||||||
|
AllocationToken token =
|
||||||
|
persistResource(
|
||||||
|
new AllocationToken.Builder()
|
||||||
|
.setToken("abc123")
|
||||||
|
.setTokenType(PACKAGE)
|
||||||
|
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||||
|
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||||
|
.setRenewalPriceBehavior(SPECIFIED)
|
||||||
|
.build());
|
||||||
|
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||||
|
persistResource(
|
||||||
|
reloadResourceByForeignKey()
|
||||||
|
.asBuilder()
|
||||||
|
.setCurrentPackageToken(token.createVKey())
|
||||||
|
.build());
|
||||||
|
setEppInput(
|
||||||
|
"domain_renew_allocationtoken.xml",
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEPACKAGE__"));
|
||||||
|
|
||||||
|
doSuccessfulTest(
|
||||||
|
"domain_renew_response.xml",
|
||||||
|
2,
|
||||||
|
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00Z"));
|
||||||
|
|
||||||
|
// We still need to verify that package token is removed as it's not being tested as a part of
|
||||||
|
// doSuccessfulTest
|
||||||
|
Domain domain = reloadResourceByForeignKey();
|
||||||
|
Truth8.assertThat(domain.getCurrentPackageToken()).isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue