mirror of
https://github.com/google/nomulus
synced 2026-05-25 01:01:57 +00:00
Compare commits
9 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
739a15851d | ||
|
|
2c961b6283 | ||
|
|
bcb2b2c784 | ||
|
|
a91ed0f1ad | ||
|
|
da28a2021c | ||
|
|
ffd952a60e | ||
|
|
97676d1a1f | ||
|
|
1dcbc9e0cb | ||
|
|
f59c387b9c |
@@ -0,0 +1,246 @@
|
||||
// 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.
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.primitives.Ints;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Days;
|
||||
|
||||
/**
|
||||
* An action that checks all {@link BulkPricingPackage} objects for compliance with their max create
|
||||
* limit.
|
||||
*/
|
||||
@Action(
|
||||
service = Service.BACKEND,
|
||||
path = CheckBulkComplianceAction.PATH,
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class CheckBulkComplianceAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/checkBulkCompliance";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final SendEmailUtils sendEmailUtils;
|
||||
private final Clock clock;
|
||||
private final String bulkPricingPackageCreateLimitEmailSubject;
|
||||
private final String bulkPricingPackageDomainLimitWarningEmailSubject;
|
||||
private final String bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
private final String bulkPricingPackageCreateLimitEmailBody;
|
||||
private final String bulkPricingPackageDomainLimitWarningEmailBody;
|
||||
private final String bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
private final String registrySupportEmail;
|
||||
private static final int THIRTY_DAYS = 30;
|
||||
private static final int FORTY_DAYS = 40;
|
||||
|
||||
@Inject
|
||||
public CheckBulkComplianceAction(
|
||||
SendEmailUtils sendEmailUtils,
|
||||
Clock clock,
|
||||
@Config("bulkPricingPackageCreateLimitEmailSubject")
|
||||
String bulkPricingPackageCreateLimitEmailSubject,
|
||||
@Config("bulkPricingPackageDomainLimitWarningEmailSubject")
|
||||
String bulkPricingPackageDomainLimitWarningEmailSubject,
|
||||
@Config("bulkPricingPackageDomainLimitUpgradeEmailSubject")
|
||||
String bulkPricingPackageDomainLimitUpgradeEmailSubject,
|
||||
@Config("bulkPricingPackageCreateLimitEmailBody")
|
||||
String bulkPricingPackageCreateLimitEmailBody,
|
||||
@Config("bulkPricingPackageDomainLimitWarningEmailBody")
|
||||
String bulkPricingPackageDomainLimitWarningEmailBody,
|
||||
@Config("bulkPricingPackageDomainLimitUpgradeEmailBody")
|
||||
String bulkPricingPackageDomainLimitUpgradeEmailBody,
|
||||
@Config("registrySupportEmail") String registrySupportEmail) {
|
||||
this.sendEmailUtils = sendEmailUtils;
|
||||
this.clock = clock;
|
||||
this.bulkPricingPackageCreateLimitEmailSubject = bulkPricingPackageCreateLimitEmailSubject;
|
||||
this.bulkPricingPackageDomainLimitWarningEmailSubject =
|
||||
bulkPricingPackageDomainLimitWarningEmailSubject;
|
||||
this.bulkPricingPackageDomainLimitUpgradeEmailSubject =
|
||||
bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
this.bulkPricingPackageCreateLimitEmailBody = bulkPricingPackageCreateLimitEmailBody;
|
||||
this.bulkPricingPackageDomainLimitWarningEmailBody =
|
||||
bulkPricingPackageDomainLimitWarningEmailBody;
|
||||
this.bulkPricingPackageDomainLimitUpgradeEmailBody =
|
||||
bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
this.registrySupportEmail = registrySupportEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
tm().transact(this::checkBulkPackages);
|
||||
}
|
||||
|
||||
private void checkBulkPackages() {
|
||||
ImmutableList<BulkPricingPackage> bulkPricingPackages =
|
||||
tm().loadAllOf(BulkPricingPackage.class);
|
||||
ImmutableMap.Builder<BulkPricingPackage, Long> bulkPricingPackagesOverCreateLimitBuilder =
|
||||
new ImmutableMap.Builder<>();
|
||||
ImmutableMap.Builder<BulkPricingPackage, Long>
|
||||
bulkPricingPackagesOverActiveDomainsLimitBuilder = new ImmutableMap.Builder<>();
|
||||
for (BulkPricingPackage bulkPricingPackage : bulkPricingPackages) {
|
||||
Long creates =
|
||||
(Long)
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM DomainHistory WHERE current_package_token ="
|
||||
+ " :token AND modificationTime >= :lastBilling AND type ="
|
||||
+ " 'DOMAIN_CREATE'")
|
||||
.setParameter("token", bulkPricingPackage.getToken().getKey().toString())
|
||||
.setParameter(
|
||||
"lastBilling", bulkPricingPackage.getNextBillingDate().minusYears(1))
|
||||
.getSingleResult();
|
||||
if (creates > bulkPricingPackage.getMaxCreates()) {
|
||||
long overage = creates - bulkPricingPackage.getMaxCreates();
|
||||
logger.atInfo().log(
|
||||
"Bulk pricing package with bulk token %s has exceeded their max domain creation limit"
|
||||
+ " by %d name(s).",
|
||||
bulkPricingPackage.getToken().getKey(), overage);
|
||||
bulkPricingPackagesOverCreateLimitBuilder.put(bulkPricingPackage, creates);
|
||||
}
|
||||
|
||||
Long activeDomains =
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM Domain WHERE currentBulkToken = :token"
|
||||
+ " AND deletionTime = :endOfTime",
|
||||
Long.class)
|
||||
.setParameter("token", bulkPricingPackage.getToken())
|
||||
.setParameter("endOfTime", END_OF_TIME)
|
||||
.getSingleResult();
|
||||
if (activeDomains > bulkPricingPackage.getMaxDomains()) {
|
||||
int overage = Ints.saturatedCast(activeDomains) - bulkPricingPackage.getMaxDomains();
|
||||
logger.atInfo().log(
|
||||
"Bulk pricing package with bulk token %s has exceed their max active domains limit by"
|
||||
+ " %d name(s).",
|
||||
bulkPricingPackage.getToken().getKey(), overage);
|
||||
bulkPricingPackagesOverActiveDomainsLimitBuilder.put(bulkPricingPackage, activeDomains);
|
||||
}
|
||||
}
|
||||
handleBulkPricingPackageCreationOverage(bulkPricingPackagesOverCreateLimitBuilder.build());
|
||||
handleActiveDomainOverage(bulkPricingPackagesOverActiveDomainsLimitBuilder.build());
|
||||
}
|
||||
|
||||
private void handleBulkPricingPackageCreationOverage(
|
||||
ImmutableMap<BulkPricingPackage, Long> overageList) {
|
||||
if (overageList.isEmpty()) {
|
||||
logger.atInfo().log("Found no bulk pricing packages over their create limit.");
|
||||
return;
|
||||
}
|
||||
logger.atInfo().log(
|
||||
"Found %d bulk pricing packages over their create limit.", overageList.size());
|
||||
for (BulkPricingPackage bulkPricingPackage : overageList.keySet()) {
|
||||
AllocationToken bulkToken = tm().loadByKey(bulkPricingPackage.getToken());
|
||||
Optional<Registrar> registrar =
|
||||
Registrar.loadByRegistrarIdCached(
|
||||
Iterables.getOnlyElement(bulkToken.getAllowedRegistrarIds()));
|
||||
if (registrar.isPresent()) {
|
||||
String body =
|
||||
String.format(
|
||||
bulkPricingPackageCreateLimitEmailBody,
|
||||
bulkPricingPackage.getId(),
|
||||
bulkToken.getToken(),
|
||||
registrar.get().getRegistrarName(),
|
||||
bulkPricingPackage.getMaxCreates(),
|
||||
overageList.get(bulkPricingPackage));
|
||||
sendNotification(
|
||||
bulkToken, bulkPricingPackageCreateLimitEmailSubject, body, registrar.get());
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
String.format("Could not find registrar for bulk token %s", bulkToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleActiveDomainOverage(ImmutableMap<BulkPricingPackage, Long> overageList) {
|
||||
if (overageList.isEmpty()) {
|
||||
logger.atInfo().log("Found no bulk pricing packages over their active domains limit.");
|
||||
return;
|
||||
}
|
||||
logger.atInfo().log(
|
||||
"Found %d bulk pricing packages over their active domains limit.", overageList.size());
|
||||
for (BulkPricingPackage bulkPricingPackage : overageList.keySet()) {
|
||||
int daysSinceLastNotification =
|
||||
bulkPricingPackage
|
||||
.getLastNotificationSent()
|
||||
.map(sentDate -> Days.daysBetween(sentDate, clock.nowUtc()).getDays())
|
||||
.orElse(Integer.MAX_VALUE);
|
||||
if (daysSinceLastNotification < THIRTY_DAYS) {
|
||||
// Don't send an email if notification was already sent within the last 30
|
||||
// days
|
||||
continue;
|
||||
} else if (daysSinceLastNotification < FORTY_DAYS) {
|
||||
// Send an upgrade email if last email was between 30 and 40 days ago
|
||||
sendActiveDomainOverageEmail(
|
||||
/* warning= */ false, bulkPricingPackage, overageList.get(bulkPricingPackage));
|
||||
} else {
|
||||
// Send a warning email
|
||||
sendActiveDomainOverageEmail(
|
||||
/* warning= */ true, bulkPricingPackage, overageList.get(bulkPricingPackage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendActiveDomainOverageEmail(
|
||||
boolean warning, BulkPricingPackage bulkPricingPackage, long activeDomains) {
|
||||
String emailSubject =
|
||||
warning
|
||||
? bulkPricingPackageDomainLimitWarningEmailSubject
|
||||
: bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
String emailTemplate =
|
||||
warning
|
||||
? bulkPricingPackageDomainLimitWarningEmailBody
|
||||
: bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
AllocationToken bulkToken = tm().loadByKey(bulkPricingPackage.getToken());
|
||||
Optional<Registrar> registrar =
|
||||
Registrar.loadByRegistrarIdCached(
|
||||
Iterables.getOnlyElement(bulkToken.getAllowedRegistrarIds()));
|
||||
if (registrar.isPresent()) {
|
||||
String body =
|
||||
String.format(
|
||||
emailTemplate,
|
||||
bulkPricingPackage.getId(),
|
||||
bulkToken.getToken(),
|
||||
registrar.get().getRegistrarName(),
|
||||
bulkPricingPackage.getMaxDomains(),
|
||||
activeDomains);
|
||||
sendNotification(bulkToken, emailSubject, body, registrar.get());
|
||||
tm().put(bulkPricingPackage.asBuilder().setLastNotificationSent(clock.nowUtc()).build());
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
String.format("Could not find registrar for bulk token %s", bulkToken));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(
|
||||
AllocationToken bulkToken, String subject, String body, Registrar registrar) {
|
||||
logger.atInfo().log(
|
||||
String.format(
|
||||
"Compliance email sent to support regarding the %s registrar and the bulk pricing"
|
||||
+ " package with token %s.",
|
||||
registrar.getRegistrarName(), bulkToken.getToken()));
|
||||
sendEmailUtils.sendEmail(subject, body, ImmutableList.of(registrySupportEmail));
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
// 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.
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.primitives.Ints;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Days;
|
||||
|
||||
/**
|
||||
* An action that checks all {@link PackagePromotion} objects for compliance with their max create
|
||||
* limit.
|
||||
*/
|
||||
@Action(
|
||||
service = Service.BACKEND,
|
||||
path = CheckPackagesComplianceAction.PATH,
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class CheckPackagesComplianceAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/checkPackagesCompliance";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final SendEmailUtils sendEmailUtils;
|
||||
private final Clock clock;
|
||||
private final String packageCreateLimitEmailSubject;
|
||||
private final String packageDomainLimitWarningEmailSubject;
|
||||
private final String packageDomainLimitUpgradeEmailSubject;
|
||||
private final String packageCreateLimitEmailBody;
|
||||
private final String packageDomainLimitWarningEmailBody;
|
||||
private final String packageDomainLimitUpgradeEmailBody;
|
||||
private final String registrySupportEmail;
|
||||
private static final int THIRTY_DAYS = 30;
|
||||
private static final int FORTY_DAYS = 40;
|
||||
|
||||
@Inject
|
||||
public CheckPackagesComplianceAction(
|
||||
SendEmailUtils sendEmailUtils,
|
||||
Clock clock,
|
||||
@Config("packageCreateLimitEmailSubject") String packageCreateLimitEmailSubject,
|
||||
@Config("packageDomainLimitWarningEmailSubject") String packageDomainLimitWarningEmailSubject,
|
||||
@Config("packageDomainLimitUpgradeEmailSubject") String packageDomainLimitUpgradeEmailSubject,
|
||||
@Config("packageCreateLimitEmailBody") String packageCreateLimitEmailBody,
|
||||
@Config("packageDomainLimitWarningEmailBody") String packageDomainLimitWarningEmailBody,
|
||||
@Config("packageDomainLimitUpgradeEmailBody") String packageDomainLimitUpgradeEmailBody,
|
||||
@Config("registrySupportEmail") String registrySupportEmail) {
|
||||
this.sendEmailUtils = sendEmailUtils;
|
||||
this.clock = clock;
|
||||
this.packageCreateLimitEmailSubject = packageCreateLimitEmailSubject;
|
||||
this.packageDomainLimitWarningEmailSubject = packageDomainLimitWarningEmailSubject;
|
||||
this.packageDomainLimitUpgradeEmailSubject = packageDomainLimitUpgradeEmailSubject;
|
||||
this.packageCreateLimitEmailBody = packageCreateLimitEmailBody;
|
||||
this.packageDomainLimitWarningEmailBody = packageDomainLimitWarningEmailBody;
|
||||
this.packageDomainLimitUpgradeEmailBody = packageDomainLimitUpgradeEmailBody;
|
||||
this.registrySupportEmail = registrySupportEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
tm().transact(this::checkPackages);
|
||||
}
|
||||
|
||||
private void checkPackages() {
|
||||
ImmutableList<PackagePromotion> packages = tm().loadAllOf(PackagePromotion.class);
|
||||
ImmutableMap.Builder<PackagePromotion, Long> packagesOverCreateLimitBuilder =
|
||||
new ImmutableMap.Builder<>();
|
||||
ImmutableMap.Builder<PackagePromotion, Long> packagesOverActiveDomainsLimitBuilder =
|
||||
new ImmutableMap.Builder<>();
|
||||
for (PackagePromotion packagePromo : packages) {
|
||||
Long creates =
|
||||
(Long)
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM DomainHistory WHERE current_package_token ="
|
||||
+ " :token AND modificationTime >= :lastBilling AND type ="
|
||||
+ " 'DOMAIN_CREATE'")
|
||||
.setParameter("token", packagePromo.getToken().getKey().toString())
|
||||
.setParameter("lastBilling", packagePromo.getNextBillingDate().minusYears(1))
|
||||
.getSingleResult();
|
||||
if (creates > packagePromo.getMaxCreates()) {
|
||||
long overage = creates - packagePromo.getMaxCreates();
|
||||
logger.atInfo().log(
|
||||
"Package with package token %s has exceeded their max domain creation limit"
|
||||
+ " by %d name(s).",
|
||||
packagePromo.getToken().getKey(), overage);
|
||||
packagesOverCreateLimitBuilder.put(packagePromo, creates);
|
||||
}
|
||||
|
||||
Long activeDomains =
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM Domain WHERE currentPackageToken = :token"
|
||||
+ " AND deletionTime = :endOfTime",
|
||||
Long.class)
|
||||
.setParameter("token", packagePromo.getToken())
|
||||
.setParameter("endOfTime", END_OF_TIME)
|
||||
.getSingleResult();
|
||||
if (activeDomains > packagePromo.getMaxDomains()) {
|
||||
int overage = Ints.saturatedCast(activeDomains) - packagePromo.getMaxDomains();
|
||||
logger.atInfo().log(
|
||||
"Package with package token %s has exceed their max active domains limit by"
|
||||
+ " %d name(s).",
|
||||
packagePromo.getToken().getKey(), overage);
|
||||
packagesOverActiveDomainsLimitBuilder.put(packagePromo, activeDomains);
|
||||
}
|
||||
}
|
||||
handlePackageCreationOverage(packagesOverCreateLimitBuilder.build());
|
||||
handleActiveDomainOverage(packagesOverActiveDomainsLimitBuilder.build());
|
||||
}
|
||||
|
||||
private void handlePackageCreationOverage(ImmutableMap<PackagePromotion, Long> overageList) {
|
||||
if (overageList.isEmpty()) {
|
||||
logger.atInfo().log("Found no packages over their create limit.");
|
||||
return;
|
||||
}
|
||||
logger.atInfo().log("Found %d packages over their create limit.", overageList.size());
|
||||
for (PackagePromotion packagePromotion : overageList.keySet()) {
|
||||
AllocationToken packageToken = tm().loadByKey(packagePromotion.getToken());
|
||||
Optional<Registrar> registrar =
|
||||
Registrar.loadByRegistrarIdCached(
|
||||
Iterables.getOnlyElement(packageToken.getAllowedRegistrarIds()));
|
||||
if (registrar.isPresent()) {
|
||||
String body =
|
||||
String.format(
|
||||
packageCreateLimitEmailBody,
|
||||
packagePromotion.getId(),
|
||||
packageToken.getToken(),
|
||||
registrar.get().getRegistrarName(),
|
||||
packagePromotion.getMaxCreates(),
|
||||
overageList.get(packagePromotion));
|
||||
sendNotification(packageToken, packageCreateLimitEmailSubject, body, registrar.get());
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
String.format("Could not find registrar for package token %s", packageToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleActiveDomainOverage(ImmutableMap<PackagePromotion, Long> overageList) {
|
||||
if (overageList.isEmpty()) {
|
||||
logger.atInfo().log("Found no packages over their active domains limit.");
|
||||
return;
|
||||
}
|
||||
logger.atInfo().log("Found %d packages over their active domains limit.", overageList.size());
|
||||
for (PackagePromotion packagePromotion : overageList.keySet()) {
|
||||
int daysSinceLastNotification =
|
||||
packagePromotion
|
||||
.getLastNotificationSent()
|
||||
.map(sentDate -> Days.daysBetween(sentDate, clock.nowUtc()).getDays())
|
||||
.orElse(Integer.MAX_VALUE);
|
||||
if (daysSinceLastNotification < THIRTY_DAYS) {
|
||||
// Don't send an email if notification was already sent within the last 30
|
||||
// days
|
||||
continue;
|
||||
} else if (daysSinceLastNotification < FORTY_DAYS) {
|
||||
// Send an upgrade email if last email was between 30 and 40 days ago
|
||||
sendActiveDomainOverageEmail(
|
||||
/* warning= */ false, packagePromotion, overageList.get(packagePromotion));
|
||||
} else {
|
||||
// Send a warning email
|
||||
sendActiveDomainOverageEmail(
|
||||
/* warning= */ true, packagePromotion, overageList.get(packagePromotion));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendActiveDomainOverageEmail(
|
||||
boolean warning, PackagePromotion packagePromotion, long activeDomains) {
|
||||
String emailSubject =
|
||||
warning ? packageDomainLimitWarningEmailSubject : packageDomainLimitUpgradeEmailSubject;
|
||||
String emailTemplate =
|
||||
warning ? packageDomainLimitWarningEmailBody : packageDomainLimitUpgradeEmailBody;
|
||||
AllocationToken packageToken = tm().loadByKey(packagePromotion.getToken());
|
||||
Optional<Registrar> registrar =
|
||||
Registrar.loadByRegistrarIdCached(
|
||||
Iterables.getOnlyElement(packageToken.getAllowedRegistrarIds()));
|
||||
if (registrar.isPresent()) {
|
||||
String body =
|
||||
String.format(
|
||||
emailTemplate,
|
||||
packagePromotion.getId(),
|
||||
packageToken.getToken(),
|
||||
registrar.get().getRegistrarName(),
|
||||
packagePromotion.getMaxDomains(),
|
||||
activeDomains);
|
||||
sendNotification(packageToken, emailSubject, body, registrar.get());
|
||||
tm().put(packagePromotion.asBuilder().setLastNotificationSent(clock.nowUtc()).build());
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
String.format("Could not find registrar for package token %s", packageToken));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(
|
||||
AllocationToken packageToken, String subject, String body, Registrar registrar) {
|
||||
logger.atInfo().log(
|
||||
String.format(
|
||||
"Compliance email sent to support regarding the %s registrar and the package with token"
|
||||
+ " %s.",
|
||||
registrar.getRegistrarName(), packageToken.getToken()));
|
||||
sendEmailUtils.sendEmail(subject, body, ImmutableList.of(registrySupportEmail));
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
@@ -40,7 +41,6 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.AddressException;
|
||||
@@ -84,7 +84,7 @@ public class RelockDomainAction implements Runnable {
|
||||
private final InternetAddress alertRecipientAddress;
|
||||
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||
private final String supportEmail;
|
||||
private final SendEmailService sendEmailService;
|
||||
private final GmailClient gmailClient;
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final Response response;
|
||||
|
||||
@@ -92,10 +92,10 @@ public class RelockDomainAction implements Runnable {
|
||||
public RelockDomainAction(
|
||||
@Parameter(OLD_UNLOCK_REVISION_ID_PARAM) long oldUnlockRevisionId,
|
||||
@Parameter(PREVIOUS_ATTEMPTS_PARAM) int previousAttempts,
|
||||
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||
@Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||
@Config("supportEmail") String supportEmail,
|
||||
SendEmailService sendEmailService,
|
||||
GmailClient gmailClient,
|
||||
DomainLockUtils domainLockUtils,
|
||||
Response response) {
|
||||
this.oldUnlockRevisionId = oldUnlockRevisionId;
|
||||
@@ -103,7 +103,7 @@ public class RelockDomainAction implements Runnable {
|
||||
this.alertRecipientAddress = alertRecipientAddress;
|
||||
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
|
||||
this.supportEmail = supportEmail;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.gmailClient = gmailClient;
|
||||
this.domainLockUtils = domainLockUtils;
|
||||
this.response = response;
|
||||
}
|
||||
@@ -215,7 +215,7 @@ public class RelockDomainAction implements Runnable {
|
||||
oldLock.getDomainName(),
|
||||
t.getMessage(),
|
||||
supportEmail);
|
||||
sendEmailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setBody(body)
|
||||
@@ -245,7 +245,7 @@ public class RelockDomainAction implements Runnable {
|
||||
String body =
|
||||
String.format(RELOCK_SUCCESS_EMAIL_TEMPLATE, oldLock.getDomainName(), supportEmail);
|
||||
|
||||
sendEmailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setBody(body)
|
||||
@@ -264,7 +264,7 @@ public class RelockDomainAction implements Runnable {
|
||||
.addAll(getEmailRecipients(oldLock.getRegistrarId()))
|
||||
.add(alertRecipientAddress)
|
||||
.build();
|
||||
sendEmailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setBody(body)
|
||||
@@ -274,7 +274,7 @@ public class RelockDomainAction implements Runnable {
|
||||
}
|
||||
|
||||
private void sendUnknownRevisionIdAlertEmail() {
|
||||
sendEmailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setBody(String.format(RELOCK_UNKNOWN_ID_FAILURE_EMAIL_TEMPLATE, oldUnlockRevisionId))
|
||||
|
||||
@@ -274,10 +274,7 @@ public class RdeIO {
|
||||
PendingDeposit key = input.getKey();
|
||||
Tld tld = Tld.get(key.tld());
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
Cursor.createScopedVKey(key.cursor(), tld)));
|
||||
tm().loadByKeyIfPresent(Cursor.createScopedVKey(key.cursor(), tld));
|
||||
DateTime position = getCursorTimeOrStartOfTime(cursor);
|
||||
checkState(key.interval() != null, "Interval must be present");
|
||||
DateTime newPosition = key.watermark().plus(key.interval());
|
||||
|
||||
@@ -1371,41 +1371,45 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageCreateLimitEmailSubject")
|
||||
public static String providePackageCreateLimitEmailSubject(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageCreateLimitEmailSubject;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageCreateLimitEmailBody")
|
||||
public static String providePackageCreateLimitEmailBody(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageCreateLimitEmailBody;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageDomainLimitWarningEmailSubject")
|
||||
public static String providePackageDomainLimitWarningEmailSubject(
|
||||
@Config("bulkPricingPackageCreateLimitEmailSubject")
|
||||
public static String provideBulkPricingPackageCreateLimitEmailSubject(
|
||||
RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitWarningEmailSubject;
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageCreateLimitEmailSubject;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageDomainLimitWarningEmailBody")
|
||||
public static String providePackageDomainLimitWarningEmailBody(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitWarningEmailBody;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageDomainLimitUpgradeEmailSubject")
|
||||
public static String providePackageDomainLimitUpgradeEmailSubject(
|
||||
@Config("bulkPricingPackageCreateLimitEmailBody")
|
||||
public static String provideBulkPricingPackageCreateLimitEmailBody(
|
||||
RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitUpgradeEmailSubject;
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageCreateLimitEmailBody;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("packageDomainLimitUpgradeEmailBody")
|
||||
public static String providePackageDomainLimitUpgradeEmailBody(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitUpgradeEmailBody;
|
||||
@Config("bulkPricingPackageDomainLimitWarningEmailSubject")
|
||||
public static String provideBulkPricingPackageDomainLimitWarningEmailSubject(
|
||||
RegistryConfigSettings config) {
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageDomainLimitWarningEmailSubject;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bulkPricingPackageDomainLimitWarningEmailBody")
|
||||
public static String provideBulkPricingPackageDomainLimitWarningEmailBody(
|
||||
RegistryConfigSettings config) {
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageDomainLimitWarningEmailBody;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bulkPricingPackageDomainLimitUpgradeEmailSubject")
|
||||
public static String provideBulkPricingPackageDomainLimitUpgradeEmailSubject(
|
||||
RegistryConfigSettings config) {
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bulkPricingPackageDomainLimitUpgradeEmailBody")
|
||||
public static String provideBulkPricingPackageDomainLimitUpgradeEmailBody(
|
||||
RegistryConfigSettings config) {
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
|
||||
private static String formatComments(String text) {
|
||||
@@ -1547,6 +1551,11 @@ public final class RegistryConfig {
|
||||
return CONFIG_SETTINGS.get().hibernate.connectionIsolation;
|
||||
}
|
||||
|
||||
/** Returns true if per-transaction isolation level is enabled. */
|
||||
public static boolean getHibernatePerTransactionIsolationEnabled() {
|
||||
return CONFIG_SETTINGS.get().hibernate.perTransactionIsolation;
|
||||
}
|
||||
|
||||
/** Returns true if hibernate.show_sql is enabled. */
|
||||
public static String getHibernateLogSqlQueries() {
|
||||
return CONFIG_SETTINGS.get().hibernate.logSqlQueries;
|
||||
|
||||
@@ -42,7 +42,7 @@ public class RegistryConfigSettings {
|
||||
public SslCertificateValidation sslCertificateValidation;
|
||||
public ContactHistory contactHistory;
|
||||
public DnsUpdate dnsUpdate;
|
||||
public PackageMonitoring packageMonitoring;
|
||||
public BulkPricingPackageMonitoring bulkPricingPackageMonitoring;
|
||||
|
||||
/** Configuration options that apply to the entire GCP project. */
|
||||
public static class GcpProject {
|
||||
@@ -115,6 +115,7 @@ public class RegistryConfigSettings {
|
||||
|
||||
/** Configuration for Hibernate. */
|
||||
public static class Hibernate {
|
||||
public boolean perTransactionIsolation;
|
||||
public String connectionIsolation;
|
||||
public String logSqlQueries;
|
||||
public String hikariConnectionTimeout;
|
||||
@@ -252,13 +253,13 @@ public class RegistryConfigSettings {
|
||||
public String registryCcEmail;
|
||||
}
|
||||
|
||||
/** Configuration for package compliance monitoring. */
|
||||
public static class PackageMonitoring {
|
||||
public String packageCreateLimitEmailSubject;
|
||||
public String packageCreateLimitEmailBody;
|
||||
public String packageDomainLimitWarningEmailSubject;
|
||||
public String packageDomainLimitWarningEmailBody;
|
||||
public String packageDomainLimitUpgradeEmailSubject;
|
||||
public String packageDomainLimitUpgradeEmailBody;
|
||||
/** Configuration for bulk pricing package compliance monitoring. */
|
||||
public static class BulkPricingPackageMonitoring {
|
||||
public String bulkPricingPackageCreateLimitEmailSubject;
|
||||
public String bulkPricingPackageCreateLimitEmailBody;
|
||||
public String bulkPricingPackageDomainLimitWarningEmailSubject;
|
||||
public String bulkPricingPackageDomainLimitWarningEmailBody;
|
||||
public String bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
public String bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,12 @@ registryPolicy:
|
||||
sunriseDomainCreateDiscount: 0.15
|
||||
|
||||
hibernate:
|
||||
# Make it possible to specify the isolation level for each transaction. If set
|
||||
# to true, nested transactions will throw an exception. If set to false, a
|
||||
# transaction with the isolation override specified will still execute at the
|
||||
# default level (specified below).
|
||||
perTransactionIsolation: false
|
||||
|
||||
# Make 'SERIALIZABLE' the default isolation level to ensure correctness.
|
||||
#
|
||||
# Entities that are never involved in multi-table transactions may use optimistic
|
||||
@@ -479,7 +485,7 @@ keyring:
|
||||
|
||||
# Configuration options relevant to the "nomulus" registry tool.
|
||||
registryTool:
|
||||
# OAuth client Id used by the tool.
|
||||
# OAuth client ID used by the tool.
|
||||
clientId: YOUR_CLIENT_ID
|
||||
# OAuth client secret used by the tool.
|
||||
clientSecret: YOUR_CLIENT_SECRET
|
||||
@@ -553,55 +559,55 @@ sslCertificateValidation:
|
||||
- secp256r1
|
||||
- secp384r1
|
||||
|
||||
# Configuration options for the package compliance monitoring
|
||||
packageMonitoring:
|
||||
# Email subject text to notify tech support that a package has exceeded the limit for domain creates
|
||||
packageCreateLimitEmailSubject: "ACTION REQUIRED: Package needs to be upgraded"
|
||||
# Email body text template notify support that a package has exceeded the limit for domain creates
|
||||
packageCreateLimitEmailBody: >
|
||||
# Configuration options for the bulk pricing package compliance monitoring
|
||||
bulkPricingPackageMonitoring:
|
||||
# Email subject text to notify tech support that a bulk pricing package has exceeded the limit for domain creates
|
||||
bulkPricingPackageCreateLimitEmailSubject: "ACTION REQUIRED: Bulk pricing package needs to be upgraded"
|
||||
# Email body text template notify support that a bulk pricing package has exceeded the limit for domain creates
|
||||
bulkPricingPackageCreateLimitEmailBody: >
|
||||
Dear Support,
|
||||
|
||||
A package has exceeded its max create limit and needs to be upgraded to the
|
||||
A bulk pricing package has exceeded its max create limit and needs to be upgraded to the
|
||||
next tier.
|
||||
|
||||
Package ID: %1$s
|
||||
Package Token: %2$s
|
||||
Bulk Pricing ID: %1$s
|
||||
Bulk Token: %2$s
|
||||
Registrar: %3$s
|
||||
Current Max Create Limit: %4$s
|
||||
Creates Completed: %5$s
|
||||
|
||||
# Email subject text to notify support that a package has exceeded the limit
|
||||
# Email subject text to notify support that a bulk pricing package has exceeded the limit
|
||||
# for current active domains and a warning needs to be sent
|
||||
packageDomainLimitWarningEmailSubject: "ACTION REQUIRED: Package has exceeded the domain limit - send warning"
|
||||
# Email body text template to inform support that a package has exceeded the
|
||||
# limit for active domains and a warning needs to be sent that the package
|
||||
bulkPricingPackageDomainLimitWarningEmailSubject: "ACTION REQUIRED: Bulk pricing package has exceeded the domain limit - send warning"
|
||||
# Email body text template to inform support that a bulk pricing package has exceeded the
|
||||
# limit for active domains and a warning needs to be sent that the bulk pricing package
|
||||
# will be upgraded in 30 days
|
||||
packageDomainLimitWarningEmailBody: >
|
||||
bulkPricingPackageDomainLimitWarningEmailBody: >
|
||||
Dear Support,
|
||||
|
||||
A package has exceeded its max domain limit. Please send a warning to the
|
||||
registrar that their package will be upgraded to the next tier in 30 days if
|
||||
A bulk pricing package has exceeded its max domain limit. Please send a warning to the
|
||||
registrar that their bulk pricing package will be upgraded to the next tier in 30 days if
|
||||
the number of active domains does not return below the limit.
|
||||
|
||||
Package ID: %1$s
|
||||
Package Token: %2$s
|
||||
Bulk Pricing ID: %1$s
|
||||
Bulk Token: %2$s
|
||||
Registrar: %3$s
|
||||
Active Domain Limit: %4$s
|
||||
Current Active Domains: %5$s
|
||||
|
||||
# Email subject text to notify support that a package has exceeded the limit
|
||||
# Email subject text to notify support that a bulk pricing package has exceeded the limit
|
||||
# for current active domains for more than 30 days and needs to be upgraded
|
||||
packageDomainLimitUpgradeEmailSubject: "ACTION REQUIRED: Package has exceeded the domain limit - upgrade package"
|
||||
# Email body text template to inform support that a package has exceeded the
|
||||
bulkPricingPackageDomainLimitUpgradeEmailSubject: "ACTION REQUIRED: Bulk pricing package has exceeded the domain limit - upgrade package"
|
||||
# Email body text template to inform support that a bulk pricing package has exceeded the
|
||||
# limit for active domains for more than 30 days and needs to be upgraded
|
||||
packageDomainLimitUpgradeEmailBody: >
|
||||
bulkPricingPackageDomainLimitUpgradeEmailBody: >
|
||||
Dear Support,
|
||||
|
||||
A package has exceeded its max domain limit for over 30 days and needs to be
|
||||
A bulk pricing package has exceeded its max domain limit for over 30 days and needs to be
|
||||
upgraded to the next tier.
|
||||
|
||||
Package ID: %1$s
|
||||
Package Token: %2$s
|
||||
Bulk Pricing ID: %1$s
|
||||
Bulk Token: %2$s
|
||||
Registrar: %3$s
|
||||
Active Domain Limit: %4$s
|
||||
Current Active Domains: %5$s
|
||||
|
||||
@@ -26,3 +26,6 @@ gSuite:
|
||||
misc:
|
||||
# We would rather have failures than timeouts, so reduce the number of retries
|
||||
transientFailureRetries: 3
|
||||
|
||||
hibernate:
|
||||
perTransactionIsolation: true
|
||||
|
||||
@@ -35,6 +35,8 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.Result;
|
||||
import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.IsolationLevel;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Qualifier;
|
||||
@@ -135,6 +137,14 @@ public class FlowModule {
|
||||
return TransactionalFlow.class.isAssignableFrom(flowClass);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
Optional<TransactionIsolationLevel> provideIsolationLevelOverride(
|
||||
Class<? extends Flow> flowClass) {
|
||||
return Optional.ofNullable(flowClass.getAnnotation(IsolationLevel.class))
|
||||
.map(IsolationLevel::value);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FlowScope
|
||||
@Superuser
|
||||
@@ -166,7 +176,7 @@ public class FlowModule {
|
||||
@FlowScope
|
||||
@RegistrarId
|
||||
static String provideRegistrarId(SessionMetadata sessionMetadata) {
|
||||
// Treat a missing registrarId as null so we can always inject a non-null value. All we do with
|
||||
// Treat a missing registrarId as null, so we can always inject a non-null value. All we do with
|
||||
// the registrarId is log it (as "") or detect its absence, both of which work fine with empty.
|
||||
return Strings.nullToEmpty(sessionMetadata.getRegistrarId());
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import google.registry.flows.session.LoginFlow;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
@@ -42,6 +44,7 @@ public class FlowRunner {
|
||||
@Inject TransportCredentials credentials;
|
||||
@Inject EppRequestSource eppRequestSource;
|
||||
@Inject Provider<Flow> flowProvider;
|
||||
@Inject Optional<TransactionIsolationLevel> isolationLevelOverride;
|
||||
@Inject Class<? extends Flow> flowClass;
|
||||
@Inject @InputXml byte[] inputXmlBytes;
|
||||
@Inject @DryRun boolean isDryRun;
|
||||
@@ -91,7 +94,8 @@ public class FlowRunner {
|
||||
} catch (EppException e) {
|
||||
throw new EppRuntimeException(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
isolationLevelOverride.orElse(null));
|
||||
} catch (DryRunException e) {
|
||||
return e.output;
|
||||
} catch (EppRuntimeException e) {
|
||||
|
||||
@@ -152,7 +152,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
|
||||
* @error {@link DomainCreateFlow.PackageDomainRegisteredForTooManyYearsException}
|
||||
* @error {@link BulkDomainRegisteredForTooManyYearsException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
@@ -405,12 +405,11 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
: hasClaimsNotice ? LordnPhase.CLAIMS : LordnPhase.NONE);
|
||||
Domain domain = domainBuilder.build();
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenType().equals(TokenType.PACKAGE)) {
|
||||
&& allocationToken.get().getTokenType().equals(TokenType.BULK_PRICING)) {
|
||||
if (years > 1) {
|
||||
throw new PackageDomainRegisteredForTooManyYearsException(allocationToken.get().getToken());
|
||||
throw new BulkDomainRegisteredForTooManyYearsException(allocationToken.get().getToken());
|
||||
}
|
||||
domain =
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.get().createVKey()).build();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(allocationToken.get().createVKey()).build();
|
||||
}
|
||||
DomainHistory domainHistory =
|
||||
buildDomainHistory(domain, tld, now, period, tld.getAddGracePeriodLength());
|
||||
@@ -763,13 +762,12 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
/** Package domain registered for too many years. */
|
||||
static class PackageDomainRegisteredForTooManyYearsException extends CommandUseErrorException {
|
||||
public PackageDomainRegisteredForTooManyYearsException(String token) {
|
||||
/** Bulk pricing domain registered for too many years. */
|
||||
static class BulkDomainRegisteredForTooManyYearsException extends CommandUseErrorException {
|
||||
public BulkDomainRegisteredForTooManyYearsException(String token) {
|
||||
super(
|
||||
String.format(
|
||||
"The package token %s cannot be used to register names for longer than 1 year.",
|
||||
token));
|
||||
"The bulk token %s cannot be used to register names for longer than 1 year.", token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,13 +155,13 @@ public final class DomainInfoFlow implements Flow {
|
||||
if (!gracePeriodStatuses.isEmpty()) {
|
||||
extensions.add(RgpInfoExtension.create(gracePeriodStatuses));
|
||||
}
|
||||
Optional<BulkTokenExtension> packageInfo =
|
||||
Optional<BulkTokenExtension> bulkPricingInfo =
|
||||
eppInput.getSingleExtension(BulkTokenExtension.class);
|
||||
if (packageInfo.isPresent()) {
|
||||
// Package info was requested.
|
||||
if (bulkPricingInfo.isPresent()) {
|
||||
// Bulk pricing info was requested.
|
||||
if (isSuperuser || registrarId.equals(domain.getCurrentSponsorRegistrarId())) {
|
||||
// Only show package info to owning registrar or superusers
|
||||
extensions.add(BulkTokenResponseExtension.create(domain.getCurrentPackageToken()));
|
||||
// Only show bulk pricing info to owning registrar or superusers
|
||||
extensions.add(BulkTokenResponseExtension.create(domain.getCurrentBulkToken()));
|
||||
}
|
||||
}
|
||||
Optional<FeeInfoCommandExtensionV06> feeInfo =
|
||||
|
||||
@@ -30,7 +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.verifyRegistrarIsActive;
|
||||
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.maybeApplyBulkPricingRemovalToken;
|
||||
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyTokenAllowedOnDomain;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -53,8 +53,8 @@ import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeResponseRet
|
||||
import google.registry.flows.custom.DomainRenewFlowCustomLogic.BeforeSaveParameters;
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemoveDomainTokenOnPackageDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemoveDomainTokenOnNonPackageDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemoveDomainTokenOnBulkPricingDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemoveDomainTokenOnNonBulkPricingDomainException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
@@ -121,8 +121,8 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
|
||||
* @error {@link MissingRemoveDomainTokenOnPackageDomainException}
|
||||
* @error {@link RemoveDomainTokenOnNonPackageDomainException}
|
||||
* @error {@link MissingRemoveDomainTokenOnBulkPricingDomainException}
|
||||
* @error {@link RemoveDomainTokenOnNonBulkPricingDomainException}
|
||||
* @error {@link
|
||||
* google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException}
|
||||
* @error {@link
|
||||
@@ -194,7 +194,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
||||
|
||||
// If client passed an applicable static token this updates the domain
|
||||
existingDomain = maybeApplyPackageRemovalToken(existingDomain, allocationToken);
|
||||
existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken);
|
||||
|
||||
int years = command.getPeriod().getValue();
|
||||
DateTime newExpirationTime =
|
||||
@@ -328,7 +328,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
checkHasBillingAccount(registrarId, existingDomain.getTld());
|
||||
}
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
// We only allow __REMOVE_PACKAGE__ token on promo package domains for now
|
||||
// We only allow __REMOVE_PACKAGE__ token on bulk pricing domains for now
|
||||
verifyTokenAllowedOnDomain(existingDomain, allocationToken);
|
||||
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
||||
if (!command.getCurrentExpirationDate().equals(
|
||||
|
||||
@@ -154,9 +154,9 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
|
||||
boolean hasPackageToken = existingDomain.getCurrentPackageToken().isPresent();
|
||||
boolean hasBulkToken = existingDomain.getCurrentBulkToken().isPresent();
|
||||
Money renewalPrice =
|
||||
hasPackageToken ? null : existingBillingRecurrence.getRenewalPrice().orElse(null);
|
||||
hasBulkToken ? null : existingBillingRecurrence.getRenewalPrice().orElse(null);
|
||||
Optional<BillingEvent> billingEvent =
|
||||
transferData.getTransferPeriod().getValue() == 0
|
||||
? Optional.empty()
|
||||
@@ -172,10 +172,10 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
Tld.get(tldStr),
|
||||
targetId,
|
||||
transferData.getTransferRequestTime(),
|
||||
// When removing a domain from a package it should return to the
|
||||
// When removing a domain from bulk pricing it should return to the
|
||||
// default recurrence billing behavior so the existing recurrence
|
||||
// billing event should not be passed in.
|
||||
hasPackageToken ? null : existingBillingRecurrence)
|
||||
hasBulkToken ? null : existingBillingRecurrence)
|
||||
.getRenewCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(Tld.get(tldStr).getTransferGracePeriodLength()))
|
||||
@@ -213,7 +213,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.setRegistrarId(gainingRegistrarId)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRenewalPriceBehavior(
|
||||
hasPackageToken
|
||||
hasBulkToken
|
||||
? RenewalPriceBehavior.DEFAULT
|
||||
: existingBillingRecurrence.getRenewalPriceBehavior())
|
||||
.setRenewalPrice(renewalPrice)
|
||||
@@ -258,9 +258,9 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.orElseGet(ImmutableSet::of))
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateRegistrarId(registrarId)
|
||||
// Even if the existing domain had a package token, that package token should be removed
|
||||
// Even if the existing domain had a bulk token, that bulk token should be removed
|
||||
// on transfer
|
||||
.setCurrentPackageToken(null)
|
||||
.setCurrentBulkToken(null)
|
||||
.build();
|
||||
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
|
||||
@@ -201,11 +201,12 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
Optional<FeesAndCredits> feesAndCredits;
|
||||
if (period.getValue() == 0) {
|
||||
feesAndCredits = Optional.empty();
|
||||
} else if (!existingDomain.getCurrentPackageToken().isPresent()) {
|
||||
} else if (!existingDomain.getCurrentBulkToken().isPresent()) {
|
||||
feesAndCredits =
|
||||
Optional.of(pricingLogic.getTransferPrice(tld, targetId, now, existingBillingRecurrence));
|
||||
} else {
|
||||
// If existing domain is in a package, calculate the transfer price with default renewal price
|
||||
// If existing domain is in a bulk pricing package, calculate the transfer price with default
|
||||
// renewal price
|
||||
// behavior
|
||||
feesAndCredits =
|
||||
period.getValue() == 0
|
||||
|
||||
@@ -146,7 +146,7 @@ public final class DomainTransferUtils {
|
||||
return builder
|
||||
.add(
|
||||
createGainingClientAutorenewEvent(
|
||||
existingDomain.getCurrentPackageToken().isPresent()
|
||||
existingDomain.getCurrentBulkToken().isPresent()
|
||||
? existingBillingRecurrence
|
||||
.asBuilder()
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT)
|
||||
|
||||
@@ -242,19 +242,19 @@ public class AllocationTokenFlowUtils {
|
||||
public static void verifyTokenAllowedOnDomain(
|
||||
Domain domain, Optional<AllocationToken> allocationToken) throws EppException {
|
||||
|
||||
boolean domainHasPackageToken = domain.getCurrentPackageToken().isPresent();
|
||||
boolean domainHasBulkToken = domain.getCurrentBulkToken().isPresent();
|
||||
boolean hasRemoveDomainToken =
|
||||
allocationToken.isPresent()
|
||||
&& TokenBehavior.REMOVE_DOMAIN.equals(allocationToken.get().getTokenBehavior());
|
||||
|
||||
if (hasRemoveDomainToken && !domainHasPackageToken) {
|
||||
throw new RemoveDomainTokenOnNonPackageDomainException();
|
||||
} else if (!hasRemoveDomainToken && domainHasPackageToken) {
|
||||
throw new MissingRemoveDomainTokenOnPackageDomainException();
|
||||
if (hasRemoveDomainToken && !domainHasBulkToken) {
|
||||
throw new RemoveDomainTokenOnNonBulkPricingDomainException();
|
||||
} else if (!hasRemoveDomainToken && domainHasBulkToken) {
|
||||
throw new MissingRemoveDomainTokenOnBulkPricingDomainException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Domain maybeApplyPackageRemovalToken(
|
||||
public static Domain maybeApplyBulkPricingRemovalToken(
|
||||
Domain domain, Optional<AllocationToken> allocationToken) {
|
||||
if (!allocationToken.isPresent()
|
||||
|| !TokenBehavior.REMOVE_DOMAIN.equals(allocationToken.get().getTokenBehavior())) {
|
||||
@@ -274,10 +274,10 @@ public class AllocationTokenFlowUtils {
|
||||
tm().getEntityManager().flush();
|
||||
tm().getEntityManager().clear();
|
||||
|
||||
// Remove current package token
|
||||
// Remove current bulk token
|
||||
return domain
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(null)
|
||||
.setCurrentBulkToken(null)
|
||||
.setAutorenewBillingEvent(newBillingRecurrence.createVKey())
|
||||
.build();
|
||||
}
|
||||
@@ -338,19 +338,19 @@ public class AllocationTokenFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** The __REMOVEDOMAIN__ token is missing on a package domain command */
|
||||
public static class MissingRemoveDomainTokenOnPackageDomainException
|
||||
/** The __REMOVEDOMAIN__ token is missing on a bulk pricing domain command */
|
||||
public static class MissingRemoveDomainTokenOnBulkPricingDomainException
|
||||
extends AssociationProhibitsOperationException {
|
||||
MissingRemoveDomainTokenOnPackageDomainException() {
|
||||
super("Domains that are inside packages cannot be explicitly renewed or transferred");
|
||||
MissingRemoveDomainTokenOnBulkPricingDomainException() {
|
||||
super("Domains that are inside bulk pricing cannot be explicitly renewed or transferred");
|
||||
}
|
||||
}
|
||||
|
||||
/** The __REMOVEDOMAIN__ token is not allowed on non package domains */
|
||||
public static class RemoveDomainTokenOnNonPackageDomainException
|
||||
/** The __REMOVEDOMAIN__ token is not allowed on non bulk pricing domains */
|
||||
public static class RemoveDomainTokenOnNonBulkPricingDomainException
|
||||
extends AssociationProhibitsOperationException {
|
||||
RemoveDomainTokenOnNonPackageDomainException() {
|
||||
super("__REMOVEDOMAIN__ token is not allowed on non package domains");
|
||||
RemoveDomainTokenOnNonBulkPricingDomainException() {
|
||||
super("__REMOVEDOMAIN__ token is not allowed on non bulk pricing domains");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
.setStatusValues(domainBase.getStatusValues())
|
||||
.setTransferData(domainBase.getTransferData())
|
||||
.setLordnPhase(domainBase.getLordnPhase())
|
||||
.setCurrentPackageToken(domainBase.getCurrentPackageToken().orElse(null));
|
||||
.setCurrentBulkToken(domainBase.getCurrentBulkToken().orElse(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,8 +267,12 @@ public class DomainBase extends EppResource
|
||||
@Enumerated(EnumType.STRING)
|
||||
LordnPhase lordnPhase = LordnPhase.NONE;
|
||||
|
||||
/** The {@link AllocationToken} for the package this domain is currently a part of. */
|
||||
@Nullable VKey<AllocationToken> currentPackageToken;
|
||||
/**
|
||||
* The {@link AllocationToken} for the bulk pricing package this domain is currently a part of.
|
||||
*/
|
||||
@Nullable
|
||||
@Column(name = "current_package_token")
|
||||
VKey<AllocationToken> currentBulkToken;
|
||||
|
||||
public LordnPhase getLordnPhase() {
|
||||
return lordnPhase;
|
||||
@@ -302,8 +306,8 @@ public class DomainBase extends EppResource
|
||||
return smdId;
|
||||
}
|
||||
|
||||
public Optional<VKey<AllocationToken>> getCurrentPackageToken() {
|
||||
return Optional.ofNullable(currentPackageToken);
|
||||
public Optional<VKey<AllocationToken>> getCurrentBulkToken() {
|
||||
return Optional.ofNullable(currentBulkToken);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,7 +482,7 @@ public class DomainBase extends EppResource
|
||||
// events.
|
||||
.setAutorenewBillingEvent(transferData.getServerApproveAutorenewEvent())
|
||||
.setAutorenewPollMessage(transferData.getServerApproveAutorenewPollMessage())
|
||||
.setCurrentPackageToken(null);
|
||||
.setCurrentBulkToken(null);
|
||||
if (transferData.getTransferPeriod().getValue() == 1) {
|
||||
// Set the grace period using a key to the pre-scheduled transfer billing event. Not using
|
||||
// GracePeriod.forBillingEvent() here in order to avoid the actual fetch.
|
||||
@@ -910,23 +914,22 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setCurrentPackageToken(@Nullable VKey<AllocationToken> currentPackageToken) {
|
||||
if (currentPackageToken == null) {
|
||||
getInstance().currentPackageToken = currentPackageToken;
|
||||
public B setCurrentBulkToken(@Nullable VKey<AllocationToken> currentBulkToken) {
|
||||
if (currentBulkToken == null) {
|
||||
getInstance().currentBulkToken = currentBulkToken;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
AllocationToken token =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(currentPackageToken))
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(currentBulkToken))
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format(
|
||||
"The package token %s does not exist",
|
||||
currentPackageToken.getKey())));
|
||||
"The bulk token %s does not exist", currentBulkToken.getKey())));
|
||||
checkArgument(
|
||||
token.getTokenType().equals(TokenType.PACKAGE),
|
||||
"The currentPackageToken must have a PACKAGE TokenType");
|
||||
getInstance().currentPackageToken = currentPackageToken;
|
||||
token.getTokenType().equals(TokenType.BULK_PRICING),
|
||||
"The currentBulkToken must have a BULK_PRICING TokenType");
|
||||
getInstance().currentBulkToken = currentBulkToken;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,9 +119,14 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
|
||||
/** Type of the token that indicates how and where it should be used. */
|
||||
public enum TokenType {
|
||||
/** Token used for bulk pricing */
|
||||
BULK_PRICING,
|
||||
/** Token saved on a TLD to use if no other token is passed from the client */
|
||||
DEFAULT_PROMO,
|
||||
/** Token used for package pricing */
|
||||
/** This is the old name for what is now BULK_PRICING. */
|
||||
// TODO(sarahbot@): Remove this type once all tokens of this type have been scrubbed from the
|
||||
// database
|
||||
@Deprecated
|
||||
PACKAGE,
|
||||
/** Invalid after use */
|
||||
SINGLE_USE,
|
||||
@@ -137,8 +142,8 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
/** No special behavior */
|
||||
DEFAULT,
|
||||
/**
|
||||
* REMOVE_DOMAIN triggers domain removal from promotional bulk (package) pricing, bypasses
|
||||
* DEFAULT token validations.
|
||||
* REMOVE_DOMAIN triggers domain removal from a bulk pricing package, bypasses DEFAULT token
|
||||
* validations.
|
||||
*/
|
||||
REMOVE_DOMAIN
|
||||
}
|
||||
@@ -344,12 +349,13 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
checkArgumentNotNull(getInstance().tokenType, "Token type must be specified");
|
||||
checkArgument(!Strings.isNullOrEmpty(getInstance().token), "Token must not be null or empty");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE)
|
||||
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|
||||
|| getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"Package tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
"Bulk tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
checkArgument(
|
||||
!getInstance().tokenType.equals(TokenType.PACKAGE) || !getInstance().discountPremiums,
|
||||
"Package tokens cannot discount premium names");
|
||||
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|
||||
|| !getInstance().discountPremiums,
|
||||
"Bulk tokens cannot discount premium names");
|
||||
checkArgument(
|
||||
getInstance().domainName == null || TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Domain name can only be specified for SINGLE_USE tokens");
|
||||
@@ -358,10 +364,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
|| TokenType.SINGLE_USE.equals(getInstance().tokenType),
|
||||
"Redemption history entry can only be specified for SINGLE_USE tokens");
|
||||
checkArgument(
|
||||
getInstance().tokenType != TokenType.PACKAGE
|
||||
getInstance().tokenType != TokenType.BULK_PRICING
|
||||
|| (getInstance().allowedClientIds != null
|
||||
&& getInstance().allowedClientIds.size() == 1),
|
||||
"PACKAGE tokens must have exactly one allowed client registrar");
|
||||
"BULK_PRICING tokens must have exactly one allowed client registrar");
|
||||
checkArgument(
|
||||
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
|
||||
"Discount premiums can only be specified along with a discount fraction");
|
||||
|
||||
@@ -36,46 +36,53 @@ import org.hibernate.annotations.Type;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** An entity representing a package promotion. */
|
||||
@Entity
|
||||
/**
|
||||
* An entity representing a bulk pricing promotion. Note that this table is still called
|
||||
* PackagePromotion in Cloud SQL.
|
||||
*/
|
||||
@Entity(name = "PackagePromotion")
|
||||
@javax.persistence.Table(indexes = {@javax.persistence.Index(columnList = "token")})
|
||||
public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
public class BulkPricingPackage extends ImmutableObject implements Buildable {
|
||||
|
||||
/** An autogenerated identifier for the package promotion. */
|
||||
/** An autogenerated identifier for the bulk pricing promotion. */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
long packagePromotionId;
|
||||
@Column(name = "package_promotion_id")
|
||||
long bulkPricingId;
|
||||
|
||||
/** The allocation token string for the package. */
|
||||
/** The allocation token string for the bulk pricing package. */
|
||||
@Column(nullable = false)
|
||||
VKey<AllocationToken> token;
|
||||
|
||||
/** The maximum number of active domains the package allows at any given time. */
|
||||
/** The maximum number of active domains the bulk pricing package allows at any given time. */
|
||||
@Column(nullable = false)
|
||||
int maxDomains;
|
||||
|
||||
/** The maximum number of domains that can be created in the package each year. */
|
||||
/** The maximum number of domains that can be created in the bulk pricing package each year. */
|
||||
@Column(nullable = false)
|
||||
int maxCreates;
|
||||
|
||||
/** The annual price of the package. */
|
||||
/** The annual price of the bulk pricing package. */
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(
|
||||
columns = {
|
||||
@Column(name = "package_price_amount", nullable = false),
|
||||
@Column(name = "package_price_currency", nullable = false)
|
||||
})
|
||||
Money packagePrice;
|
||||
Money bulkPrice;
|
||||
|
||||
/** The next billing date of the package. */
|
||||
/** The next billing date of the bulk pricing package. */
|
||||
@Column(nullable = false)
|
||||
DateTime nextBillingDate = END_OF_TIME;
|
||||
|
||||
/** Date the last warning email was sent that the package has exceeded the maxDomains limit. */
|
||||
/**
|
||||
* Date the last warning email was sent that the bulk pricing package has exceeded the maxDomains
|
||||
* limit.
|
||||
*/
|
||||
@Nullable DateTime lastNotificationSent;
|
||||
|
||||
public long getId() {
|
||||
return packagePromotionId;
|
||||
return bulkPricingId;
|
||||
}
|
||||
|
||||
public VKey<AllocationToken> getToken() {
|
||||
@@ -90,8 +97,8 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return maxCreates;
|
||||
}
|
||||
|
||||
public Money getPackagePrice() {
|
||||
return packagePrice;
|
||||
public Money getBulkPrice() {
|
||||
return bulkPrice;
|
||||
}
|
||||
|
||||
public DateTime getNextBillingDate() {
|
||||
@@ -102,18 +109,18 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return Optional.ofNullable(lastNotificationSent);
|
||||
}
|
||||
|
||||
/** Loads and returns a PackagePromotion entity by its token string directly from Cloud SQL. */
|
||||
public static Optional<PackagePromotion> loadByTokenString(String tokenString) {
|
||||
/** Loads and returns a BulkPricingPackage entity by its token string directly from Cloud SQL. */
|
||||
public static Optional<BulkPricingPackage> loadByTokenString(String tokenString) {
|
||||
tm().assertInTransaction();
|
||||
return tm().query("FROM PackagePromotion WHERE token = :token", PackagePromotion.class)
|
||||
return tm().query("FROM PackagePromotion WHERE token = :token", BulkPricingPackage.class)
|
||||
.setParameter("token", VKey.create(AllocationToken.class, tokenString))
|
||||
.getResultStream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<PackagePromotion> createVKey() {
|
||||
return VKey.create(PackagePromotion.class, packagePromotionId);
|
||||
public VKey<BulkPricingPackage> createVKey() {
|
||||
return VKey.create(BulkPricingPackage.class, bulkPricingId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,28 +128,29 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link PackagePromotion} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<PackagePromotion> {
|
||||
/** A builder for constructing {@link BulkPricingPackage} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<BulkPricingPackage> {
|
||||
public Builder() {}
|
||||
|
||||
private Builder(PackagePromotion instance) {
|
||||
private Builder(BulkPricingPackage instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackagePromotion build() {
|
||||
public BulkPricingPackage build() {
|
||||
checkArgumentNotNull(getInstance().token, "Allocation token must be specified");
|
||||
AllocationToken allocationToken = tm().transact(() -> tm().loadByKey(getInstance().token));
|
||||
checkArgument(
|
||||
allocationToken.tokenType == TokenType.PACKAGE,
|
||||
"Allocation token must be a PACKAGE type");
|
||||
allocationToken.tokenType == TokenType.BULK_PRICING,
|
||||
"Allocation token must be a BULK_PRICING type");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setToken(AllocationToken token) {
|
||||
checkArgumentNotNull(token, "Allocation token must not be null");
|
||||
checkArgument(
|
||||
token.tokenType == TokenType.PACKAGE, "Allocation token must be a PACKAGE type");
|
||||
token.tokenType == TokenType.BULK_PRICING,
|
||||
"Allocation token must be a BULK_PRICING type");
|
||||
getInstance().token = token.createVKey();
|
||||
return this;
|
||||
}
|
||||
@@ -159,9 +167,9 @@ public class PackagePromotion extends ImmutableObject implements Buildable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPackagePrice(Money packagePrice) {
|
||||
checkArgumentNotNull(packagePrice, "Package price must not be null");
|
||||
getInstance().packagePrice = packagePrice;
|
||||
public Builder setBulkPrice(Money bulkPrice) {
|
||||
checkArgumentNotNull(bulkPrice, "Bulk price must not be null");
|
||||
getInstance().bulkPrice = bulkPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.tools.GsonUtils.GsonPostProcessable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -55,7 +56,8 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
@XmlTransient
|
||||
@Embeddable
|
||||
@MappedSuperclass
|
||||
public class Address extends ImmutableObject implements Jsonifiable, UnsafeSerializable {
|
||||
public class Address extends ImmutableObject
|
||||
implements Jsonifiable, UnsafeSerializable, GsonPostProcessable {
|
||||
|
||||
/**
|
||||
* At most three lines of addresses parsed from XML elements.
|
||||
@@ -152,6 +154,16 @@ public class Address extends ImmutableObject implements Jsonifiable, UnsafeSeria
|
||||
return new Builder<>(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess() {
|
||||
if (street == null || street.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
streetLine1 = street.get(0);
|
||||
streetLine2 = street.size() >= 2 ? street.get(1) : null;
|
||||
streetLine3 = street.size() >= 3 ? street.get(2) : null;
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Address}. */
|
||||
public static class Builder<T extends Address> extends Buildable.Builder<T> {
|
||||
|
||||
@@ -228,11 +240,6 @@ public class Address extends ImmutableObject implements Jsonifiable, UnsafeSeria
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
|
||||
if (street == null || street.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
streetLine1 = street.get(0);
|
||||
streetLine2 = street.size() >= 2 ? street.get(1) : null;
|
||||
streetLine3 = street.size() >= 3 ? street.get(2) : null;
|
||||
postProcess();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||
import static com.google.common.collect.Sets.immutableEnumSet;
|
||||
@@ -49,7 +48,6 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.model.Buildable;
|
||||
@@ -242,7 +240,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
@Expose Set<String> allowedTlds;
|
||||
|
||||
/** Host name of WHOIS server. */
|
||||
String whoisServer;
|
||||
@Expose String whoisServer;
|
||||
|
||||
/** Base URLs for the registrar's RDAP servers. */
|
||||
Set<String> rdapBaseUrls;
|
||||
@@ -326,10 +324,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
RegistrarAddress internationalizedAddress;
|
||||
|
||||
/** Voice number. */
|
||||
String phoneNumber;
|
||||
@Expose String phoneNumber;
|
||||
|
||||
/** Fax number. */
|
||||
String faxNumber;
|
||||
@Expose String faxNumber;
|
||||
|
||||
/** Email address. */
|
||||
@Expose String emailAddress;
|
||||
@@ -364,7 +362,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
@Expose @Nullable Map<CurrencyUnit, String> billingAccountMap;
|
||||
|
||||
/** URL of registrar's website. */
|
||||
String url;
|
||||
@Expose String url;
|
||||
|
||||
/**
|
||||
* ICANN referral email address.
|
||||
@@ -556,7 +554,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContacts() {
|
||||
return Streams.stream(getContactsIterable())
|
||||
return getContactPocs().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
}
|
||||
@@ -566,7 +564,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* their email address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContactsOfType(final RegistrarPoc.Type type) {
|
||||
return Streams.stream(getContactsIterable())
|
||||
return getContactPocs().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type))
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
@@ -580,13 +578,13 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
|
||||
}
|
||||
|
||||
private Iterable<RegistrarPoc> getContactsIterable() {
|
||||
private ImmutableSet<RegistrarPoc> getContactPocs() {
|
||||
return tm().transact(
|
||||
() ->
|
||||
tm().query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.getResultStream()
|
||||
.collect(toImmutableList()));
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -732,8 +730,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
.map(Tld::createVKey)
|
||||
.collect(toImmutableSet());
|
||||
Set<VKey<Tld>> missingTldKeys =
|
||||
Sets.difference(
|
||||
newTldKeys, tm().transact(() -> tm().loadByKeysIfPresent(newTldKeys)).keySet());
|
||||
Sets.difference(newTldKeys, tm().loadByKeysIfPresent(newTldKeys).keySet());
|
||||
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
|
||||
getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds);
|
||||
return this;
|
||||
|
||||
@@ -23,6 +23,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.groups.DirectoryModule;
|
||||
import google.registry.groups.GmailModule;
|
||||
import google.registry.groups.GroupsModule;
|
||||
import google.registry.groups.GroupssettingsModule;
|
||||
import google.registry.keyring.KeyringModule;
|
||||
@@ -54,6 +55,7 @@ import javax.inject.Singleton;
|
||||
DirectoryModule.class,
|
||||
DummyKeyringModule.class,
|
||||
FrontendRequestComponentModule.class,
|
||||
GmailModule.class,
|
||||
GroupsModule.class,
|
||||
GroupssettingsModule.class,
|
||||
GsonModule.class,
|
||||
|
||||
@@ -29,6 +29,7 @@ import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
import google.registry.ui.server.console.settings.SecurityAction;
|
||||
import google.registry.ui.server.console.settings.WhoisRegistrarFieldsAction;
|
||||
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
|
||||
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
|
||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||
@@ -73,6 +74,8 @@ interface FrontendRequestComponent {
|
||||
|
||||
SecurityAction securityAction();
|
||||
|
||||
WhoisRegistrarFieldsAction whoisRegistrarFieldsAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates which {@link TransactionIsolationLevel} that a {@link
|
||||
* google.registry.flows.TransactionalFlow} show run at.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface IsolationLevel {
|
||||
TransactionIsolationLevel value();
|
||||
}
|
||||
@@ -27,9 +27,9 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import dagger.BindsOptionalOf;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -65,7 +65,6 @@ import org.hibernate.cfg.Environment;
|
||||
/** Dagger module class for the persistence layer. */
|
||||
@Module
|
||||
public abstract class PersistenceModule {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// This name must be the same as the one defined in persistence.xml.
|
||||
public static final String PERSISTENCE_UNIT_NAME = "nomulus";
|
||||
@@ -166,12 +165,10 @@ public abstract class PersistenceModule {
|
||||
// Override the default minimum which is tuned for the Registry server. A worker VM should
|
||||
// release all connections if it no longer interacts with the database.
|
||||
overrides.put(HIKARI_MINIMUM_IDLE, "0");
|
||||
/**
|
||||
* Disable Hikari's maxPoolSize limit check by setting it to an absurdly large number. The
|
||||
* effective (and desirable) limit is the number of pipeline threads on the pipeline worker,
|
||||
* which can be configured using pipeline options. See {@link RegistryPipelineOptions} for more
|
||||
* information.
|
||||
*/
|
||||
// Disable Hikari's maxPoolSize limit check by setting it to an absurdly large number. The
|
||||
// effective (and desirable) limit is the number of pipeline threads on the pipeline worker,
|
||||
// which can be configured using pipeline options. See {@link RegistryPipelineOptions} for more
|
||||
// information.
|
||||
overrides.put(HIKARI_MAXIMUM_POOL_SIZE, String.valueOf(Integer.MAX_VALUE));
|
||||
instanceConnectionNameOverride
|
||||
.map(Provider::get)
|
||||
@@ -303,7 +300,7 @@ public abstract class PersistenceModule {
|
||||
|
||||
private static EntityManagerFactory create(Map<String, String> properties) {
|
||||
// If there are no annotated classes, we can create the EntityManagerFactory from the generic
|
||||
// method. Otherwise we have to use a more tailored approach. Note that this adds to the set
|
||||
// method. Otherwise, we have to use a more tailored approach. Note that this adds to the set
|
||||
// of annotated classes defined in the configuration, it does not override them.
|
||||
EntityManagerFactory emf =
|
||||
Persistence.createEntityManagerFactory(
|
||||
@@ -322,8 +319,7 @@ public abstract class PersistenceModule {
|
||||
overrides.put(Environment.USER, credential.login());
|
||||
overrides.put(Environment.PASS, credential.password());
|
||||
} catch (Throwable e) {
|
||||
// TODO(b/184631990): after SQL becomes primary, throw an exception to fail fast
|
||||
logger.atSevere().withCause(e).log("Failed to get SQL credential from Secret Manager.");
|
||||
throw new RuntimeException("Failed to get SQL credential from Secret Manager.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,11 +336,13 @@ public abstract class PersistenceModule {
|
||||
TRANSACTION_SERIALIZABLE;
|
||||
|
||||
private final int value;
|
||||
private final String mode;
|
||||
|
||||
TransactionIsolationLevel() {
|
||||
try {
|
||||
// name() is final in parent class (Enum.java), therefore safe to call in constructor.
|
||||
value = Connection.class.getField(name()).getInt(null);
|
||||
mode = name().substring(12).replace('_', ' ');
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
@@ -356,6 +354,14 @@ public abstract class PersistenceModule {
|
||||
public final int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public final String getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static TransactionIsolationLevel fromMode(String mode) {
|
||||
return valueOf(String.format("TRANSACTION_%s", Ascii.toUpperCase(mode.replace(' ', '_'))));
|
||||
}
|
||||
}
|
||||
|
||||
/** Types of {@link JpaTransactionManager JpaTransactionManagers}. */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.function.Supplier;
|
||||
import javax.persistence.EntityManager;
|
||||
@@ -64,9 +65,21 @@ public interface JpaTransactionManager extends TransactionManager {
|
||||
/** Executes the work in a transaction with no retries and returns the result. */
|
||||
<T> T transactNoRetry(Supplier<T> work);
|
||||
|
||||
/**
|
||||
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} with no
|
||||
* retries and returns the result.
|
||||
*/
|
||||
<T> T transactNoRetry(Supplier<T> work, TransactionIsolationLevel isolationLevel);
|
||||
|
||||
/** Executes the work in a transaction with no retries. */
|
||||
void transactNoRetry(Runnable work);
|
||||
|
||||
/**
|
||||
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} with no
|
||||
* retries.
|
||||
*/
|
||||
void transactNoRetry(Runnable work, TransactionIsolationLevel isolationLevel);
|
||||
|
||||
/** Deletes the entity by its id, throws exception if the entity is not deleted. */
|
||||
<T> void assertDelete(VKey<T> key);
|
||||
|
||||
@@ -84,4 +97,13 @@ public interface JpaTransactionManager extends TransactionManager {
|
||||
static Query setQueryFetchSize(Query query, int fetchSize) {
|
||||
return query.setHint("org.hibernate.fetchSize", fetchSize);
|
||||
}
|
||||
|
||||
/** Return the default {@link TransactionIsolationLevel} specified via the config file. */
|
||||
TransactionIsolationLevel getDefaultTransactionIsolationLevel();
|
||||
|
||||
/** Return the {@link TransactionIsolationLevel} used in the current transaction. */
|
||||
TransactionIsolationLevel getCurrentTransactionIsolationLevel();
|
||||
|
||||
/** Asserts that the current transaction runs at the given level. */
|
||||
void assertTransactionIsolationLevel(TransactionIsolationLevel expectedLevel);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getHibernatePerTransactionIsolationEnabled;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.util.AbstractMap.SimpleEntry;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
@@ -31,6 +32,7 @@ import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.JpaRetries;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.Retrier;
|
||||
@@ -65,6 +67,7 @@ import javax.persistence.TemporalType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.metamodel.EntityType;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Implementation of {@link JpaTransactionManager} for JPA compatible database. */
|
||||
@@ -77,9 +80,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
private final EntityManagerFactory emf;
|
||||
private final Clock clock;
|
||||
|
||||
// TODO(b/177588434): Investigate alternatives for managing transaction information. ThreadLocal
|
||||
// adds an unnecessary restriction that each request has to be processed by one thread
|
||||
// synchronously.
|
||||
private static final ThreadLocal<TransactionInfo> transactionInfo =
|
||||
ThreadLocal.withInitial(TransactionInfo::new);
|
||||
|
||||
@@ -137,17 +137,46 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Supplier<T> work) {
|
||||
// This prevents inner transaction from retrying, thus avoiding a cascade retry effect.
|
||||
if (inTransaction()) {
|
||||
return transactNoRetry(work);
|
||||
public void assertTransactionIsolationLevel(TransactionIsolationLevel expectedLevel) {
|
||||
assertInTransaction();
|
||||
TransactionIsolationLevel currentLevel = getCurrentTransactionIsolationLevel();
|
||||
if (currentLevel != expectedLevel) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Current transaction isolation level (%s) is not as expected (%s)",
|
||||
currentLevel, expectedLevel));
|
||||
}
|
||||
return retrier.callWithRetry(() -> transactNoRetry(work), JpaRetries::isFailedTxnRetriable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(Supplier<T> work) {
|
||||
public <T> T transact(Supplier<T> work, TransactionIsolationLevel isolationLevel) {
|
||||
// This prevents inner transaction from retrying, thus avoiding a cascade retry effect.
|
||||
if (inTransaction()) {
|
||||
return transactNoRetry(work, isolationLevel);
|
||||
}
|
||||
return retrier.callWithRetry(
|
||||
() -> transactNoRetry(work, isolationLevel), JpaRetries::isFailedTxnRetriable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Supplier<T> work) {
|
||||
return transact(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(
|
||||
Supplier<T> work, @Nullable TransactionIsolationLevel isolationLevel) {
|
||||
if (inTransaction()) {
|
||||
if (isolationLevel != null && getHibernatePerTransactionIsolationEnabled()) {
|
||||
TransactionIsolationLevel enclosingLevel = getCurrentTransactionIsolationLevel();
|
||||
if (isolationLevel != enclosingLevel) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Isolation level conflict detected in nested transactions.\n"
|
||||
+ "Enclosing transaction: %s\nCurrent transaction: %s",
|
||||
enclosingLevel, isolationLevel));
|
||||
}
|
||||
}
|
||||
return work.get();
|
||||
}
|
||||
TransactionInfo txnInfo = transactionInfo.get();
|
||||
@@ -156,6 +185,18 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
try {
|
||||
txn.begin();
|
||||
txnInfo.start(clock);
|
||||
if (isolationLevel != null) {
|
||||
if (getHibernatePerTransactionIsolationEnabled()) {
|
||||
getEntityManager()
|
||||
.createNativeQuery(
|
||||
String.format("SET TRANSACTION ISOLATION LEVEL %s", isolationLevel.getMode()))
|
||||
.executeUpdate();
|
||||
logger.atInfo().log("Running transaction at %s", isolationLevel);
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
"Per-transaction isolation level disabled, but %s was requested", isolationLevel);
|
||||
}
|
||||
}
|
||||
T result = work.get();
|
||||
txn.commit();
|
||||
return result;
|
||||
@@ -174,21 +215,55 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(Runnable work) {
|
||||
public <T> T transactNoRetry(Supplier<T> work) {
|
||||
return transactNoRetry(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(Runnable work, TransactionIsolationLevel isolationLevel) {
|
||||
transact(
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
},
|
||||
isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactNoRetry(Runnable work) {
|
||||
public void transact(Runnable work) {
|
||||
transact(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactNoRetry(Runnable work, TransactionIsolationLevel isolationLevel) {
|
||||
transactNoRetry(
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
},
|
||||
isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactNoRetry(Runnable work) {
|
||||
transactNoRetry(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getDefaultTransactionIsolationLevel() {
|
||||
return TransactionIsolationLevel.valueOf(
|
||||
(String) emf.getProperties().get(Environment.ISOLATION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getCurrentTransactionIsolationLevel() {
|
||||
assertInTransaction();
|
||||
String mode =
|
||||
(String)
|
||||
getEntityManager()
|
||||
.createNativeQuery("SHOW TRANSACTION ISOLATION LEVEL")
|
||||
.getSingleResult();
|
||||
return TransactionIsolationLevel.fromMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -287,7 +362,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
Integer.class)
|
||||
.setMaxResults(1);
|
||||
entityIds.forEach(entityId -> query.setParameter(entityId.name, entityId.value));
|
||||
return query.getResultList().size() > 0;
|
||||
return !query.getResultList().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -506,7 +581,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
private String getAndClause(ImmutableSet<EntityId> entityIds) {
|
||||
private static String getAndClause(ImmutableSet<EntityId> entityIds) {
|
||||
return entityIds.stream()
|
||||
.map(entityId -> String.format("%s = :%s", entityId.name, entityId.name))
|
||||
.collect(joining(" AND "));
|
||||
@@ -556,7 +631,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
try {
|
||||
getEntityManager().getMetamodel().entity(object.getClass());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// The object is not an entity. Return without detaching.
|
||||
// The object is not an entity. Return without detaching.
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -637,7 +712,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
|
||||
TypedQuery<T> delegate;
|
||||
|
||||
public DetachingTypedQuery(TypedQuery<T> delegate) {
|
||||
DetachingTypedQuery(TypedQuery<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@@ -866,7 +941,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public Optional<T> first() {
|
||||
List<T> results = buildQuery().setMaxResults(1).getResultList();
|
||||
return results.size() > 0 ? Optional.of(detach(results.get(0))) : Optional.empty();
|
||||
return !results.isEmpty() ? Optional.of(detach(results.get(0))) : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -895,7 +970,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
public ImmutableList<T> list() {
|
||||
return buildQuery().getResultList().stream()
|
||||
.map(JpaTransactionManagerImpl.this::detach)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
@@ -49,9 +50,18 @@ public interface TransactionManager {
|
||||
/** Executes the work in a transaction and returns the result. */
|
||||
<T> T transact(Supplier<T> work);
|
||||
|
||||
/**
|
||||
* Executes the work in a transaction at the given {@link TransactionIsolationLevel} and returns
|
||||
* the result.
|
||||
*/
|
||||
<T> T transact(Supplier<T> work, TransactionIsolationLevel isolationLevel);
|
||||
|
||||
/** Executes the work in a transaction. */
|
||||
void transact(Runnable work);
|
||||
|
||||
/** Executes the work in a transaction at the given {@link TransactionIsolationLevel}. */
|
||||
void transact(Runnable work, TransactionIsolationLevel isolationLevel);
|
||||
|
||||
/** Returns the time associated with the start of this particular transaction attempt. */
|
||||
DateTime getTransactionTime();
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ package google.registry.reporting.billing;
|
||||
import static google.registry.reporting.ReportingModule.PARAM_YEAR_MONTH;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.Job;
|
||||
@@ -111,7 +111,7 @@ public class PublishInvoicesAction implements Runnable {
|
||||
break;
|
||||
default:
|
||||
logger.atInfo().log("Job in non-terminal state %s, retrying:", state);
|
||||
response.setStatus(SC_NOT_MODIFIED);
|
||||
response.setStatus(SC_SERVICE_UNAVAILABLE);
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.bigquery.BigqueryJobFailureException;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
@@ -37,7 +38,6 @@ import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
@@ -83,8 +83,12 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||
@Inject Retrier retrier;
|
||||
@Inject Response response;
|
||||
@Inject @Config("gSuiteOutgoingEmailAddress") InternetAddress sender;
|
||||
@Inject @Config("alertRecipientEmailAddress") InternetAddress recipient;
|
||||
@Inject SendEmailService emailService;
|
||||
|
||||
@Inject
|
||||
@Config("newAlertRecipientEmailAddress")
|
||||
InternetAddress recipient;
|
||||
|
||||
@Inject GmailClient gmailClient;
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IcannReportingStagingAction() {}
|
||||
@@ -103,7 +107,7 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||
stager.createAndUploadManifest(subdir, manifestedFiles);
|
||||
|
||||
logger.atInfo().log("Completed staging %d report files.", manifestedFiles.size());
|
||||
emailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setSubject("ICANN Monthly report staging summary [SUCCESS]")
|
||||
.setBody(
|
||||
@@ -130,7 +134,7 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||
},
|
||||
BigqueryJobFailureException.class);
|
||||
} catch (Throwable e) {
|
||||
emailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report staging summary [FAILURE]",
|
||||
String.format(
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Tld;
|
||||
@@ -41,7 +42,6 @@ import google.registry.request.lock.LockHandler;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
@@ -87,8 +87,12 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
@Inject Retrier retrier;
|
||||
@Inject Response response;
|
||||
@Inject @Config("gSuiteOutgoingEmailAddress") InternetAddress sender;
|
||||
@Inject @Config("alertRecipientEmailAddress") InternetAddress recipient;
|
||||
@Inject SendEmailService emailService;
|
||||
|
||||
@Inject
|
||||
@Config("newAlertRecipientEmailAddress")
|
||||
InternetAddress recipient;
|
||||
|
||||
@Inject GmailClient gmailClient;
|
||||
@Inject Clock clock;
|
||||
@Inject LockHandler lockHandler;
|
||||
|
||||
@@ -293,7 +297,7 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
(e) ->
|
||||
String.format("%s - %s", e.getKey(), e.getValue() ? "SUCCESS" : "FAILURE"))
|
||||
.collect(Collectors.joining("\n")));
|
||||
emailService.sendEmail(EmailMessage.create(subject, body, recipient, sender));
|
||||
gmailClient.sendEmail(EmailMessage.create(subject, body, recipient, sender));
|
||||
}
|
||||
|
||||
private byte[] readBytesFromGcs(BlobId reportFilename) throws IOException {
|
||||
|
||||
@@ -141,8 +141,7 @@ public class GenerateSpec11ReportAction implements Runnable {
|
||||
jobId,
|
||||
ReportingModule.PARAM_DATE,
|
||||
date.toString()),
|
||||
// TODO(b/296582836): mitigating retry problem. Remove `+10` when bug is fixed.
|
||||
Duration.standardMinutes(ReportingModule.ENQUEUE_DELAY_MINUTES + 10)));
|
||||
Duration.standardMinutes(ReportingModule.ENQUEUE_DELAY_MINUTES)));
|
||||
}
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(String.format("Launched Spec11 pipeline: %s", jobId));
|
||||
|
||||
@@ -18,9 +18,9 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.reporting.ReportingModule.PARAM_DATE;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.Job;
|
||||
@@ -135,7 +135,7 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
break;
|
||||
default:
|
||||
logger.atInfo().log("Job in non-terminal state %s, retrying:", state);
|
||||
response.setStatus(SC_NOT_MODIFIED);
|
||||
response.setStatus(SC_SERVICE_UNAVAILABLE);
|
||||
break;
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
|
||||
@@ -31,28 +31,22 @@ import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.protobuf.ByteString;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.model.adapters.CurrencyJsonAdapter;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.request.lock.LockHandlerImpl;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import google.registry.util.DateTimeTypeAdapter;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
@@ -80,12 +74,7 @@ public final class RequestModule {
|
||||
@VisibleForTesting
|
||||
@Provides
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.registerTypeAdapter(CurrencyUnit.class, new CurrencyJsonAdapter())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
return GsonUtils.provideGson();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -265,7 +254,8 @@ public final class RequestModule {
|
||||
@OptionalJsonPayload
|
||||
public static Optional<JsonElement> provideJsonBody(HttpServletRequest req, Gson gson) {
|
||||
try {
|
||||
return Optional.of(gson.fromJson(req.getReader(), JsonElement.class));
|
||||
// GET requests return a null reader and thus a null JsonObject, which is fine
|
||||
return Optional.ofNullable(gson.fromJson(req.getReader(), JsonElement.class));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -16,23 +16,23 @@ package google.registry.tools;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/** Command to create a PackagePromotion */
|
||||
/** Command to create a {@link BulkPricingPackage} */
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription =
|
||||
"Create new package promotion object(s) for registrars to register multiple names under"
|
||||
+ " one contractual annual package price using a package allocation token")
|
||||
public final class CreatePackagePromotionCommand extends CreateOrUpdatePackagePromotionCommand {
|
||||
"Create new bulk pricing package object(s) for registrars to register multiple names under"
|
||||
+ " one contractual annual bulk price using a bulk pricing allocation token")
|
||||
public final class CreateBulkPricingPackageCommand extends CreateOrUpdateBulkPricingPackageCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
PackagePromotion getOldPackagePromotion(String tokenString) {
|
||||
BulkPricingPackage getOldBulkPricingPackage(String tokenString) {
|
||||
checkArgument(
|
||||
!PackagePromotion.loadByTokenString(tokenString).isPresent(),
|
||||
"PackagePromotion with token %s already exists",
|
||||
!BulkPricingPackage.loadByTokenString(tokenString).isPresent(),
|
||||
"BulkPricingPackage with token %s already exists",
|
||||
tokenString);
|
||||
return null;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.beust.jcommander.Parameter;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -29,39 +29,40 @@ import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Shared base class for commands to create or update a PackagePromotion object. */
|
||||
abstract class CreateOrUpdatePackagePromotionCommand extends MutatingCommand {
|
||||
/** Shared base class for commands to create or update a {@link BulkPricingPackage} object. */
|
||||
abstract class CreateOrUpdateBulkPricingPackageCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(description = "Allocation token String of the package token", required = true)
|
||||
@Parameter(description = "Allocation token String of the bulk token", required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-d", "--max_domains"},
|
||||
description = "Maximum concurrent active domains allowed in the package")
|
||||
description = "Maximum concurrent active domains allowed in the bulk pricing package")
|
||||
Integer maxDomains;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-c", "--max_creates"},
|
||||
description = "Maximum domain creations allowed in the package each year")
|
||||
description = "Maximum domain creations allowed in the bulk pricing package each year")
|
||||
Integer maxCreates;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-p", "--price"},
|
||||
description = "Annual price of the package")
|
||||
description = "Annual price of the bulk pricing package")
|
||||
Money price;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--next_billing_date",
|
||||
description = "The next date that the package should be billed for its annual fee")
|
||||
description =
|
||||
"The next date that the bulk pricing package should be billed for its annual fee")
|
||||
Date nextBillingDate;
|
||||
|
||||
/** Returns the existing PackagePromotion or null if it does not exist. */
|
||||
/** Returns the existing BulkPricingPackage or null if it does not exist. */
|
||||
@Nullable
|
||||
abstract PackagePromotion getOldPackagePromotion(String token);
|
||||
abstract BulkPricingPackage getOldBulkPricingPackage(String token);
|
||||
|
||||
/** Returns the allocation token object. */
|
||||
AllocationToken getAndCheckAllocationToken(String token) {
|
||||
@@ -69,12 +70,12 @@ abstract class CreateOrUpdatePackagePromotionCommand extends MutatingCommand {
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
|
||||
checkArgument(
|
||||
allocationToken.isPresent(),
|
||||
"An allocation token with the token String %s does not exist. The package token must be"
|
||||
+ " created first before it can be used to create a PackagePromotion",
|
||||
"An allocation token with the token String %s does not exist. The bulk token must be"
|
||||
+ " created first before it can be used to create a BulkPricingPackage",
|
||||
token);
|
||||
checkArgument(
|
||||
allocationToken.get().getTokenType().equals(TokenType.PACKAGE),
|
||||
"The allocation token must be of the PACKAGE token type");
|
||||
allocationToken.get().getTokenType().equals(TokenType.BULK_PRICING),
|
||||
"The allocation token must be of the BULK_PRICING token type");
|
||||
return allocationToken.get();
|
||||
}
|
||||
|
||||
@@ -88,21 +89,21 @@ abstract class CreateOrUpdatePackagePromotionCommand extends MutatingCommand {
|
||||
for (String token : mainParameters) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
PackagePromotion oldPackage = getOldPackagePromotion(token);
|
||||
BulkPricingPackage oldBulkPricingPackage = getOldBulkPricingPackage(token);
|
||||
checkArgument(
|
||||
oldPackage != null || price != null,
|
||||
"PackagePrice is required when creating a new package");
|
||||
oldBulkPricingPackage != null || price != null,
|
||||
"BulkPrice is required when creating a new bulk pricing package");
|
||||
|
||||
AllocationToken allocationToken = getAndCheckAllocationToken(token);
|
||||
|
||||
PackagePromotion.Builder builder =
|
||||
(oldPackage == null)
|
||||
? new PackagePromotion.Builder().setToken(allocationToken)
|
||||
: oldPackage.asBuilder();
|
||||
BulkPricingPackage.Builder builder =
|
||||
(oldBulkPricingPackage == null)
|
||||
? new BulkPricingPackage.Builder().setToken(allocationToken)
|
||||
: oldBulkPricingPackage.asBuilder();
|
||||
|
||||
Optional.ofNullable(maxDomains).ifPresent(builder::setMaxDomains);
|
||||
Optional.ofNullable(maxCreates).ifPresent(builder::setMaxCreates);
|
||||
Optional.ofNullable(price).ifPresent(builder::setPackagePrice);
|
||||
Optional.ofNullable(price).ifPresent(builder::setBulkPrice);
|
||||
Optional.ofNullable(nextBillingDate)
|
||||
.ifPresent(
|
||||
nextBillingDate ->
|
||||
@@ -110,8 +111,8 @@ abstract class CreateOrUpdatePackagePromotionCommand extends MutatingCommand {
|
||||
if (clearLastNotificationSent()) {
|
||||
builder.setLastNotificationSent(null);
|
||||
}
|
||||
PackagePromotion newPackage = builder.build();
|
||||
stageEntityChange(oldPackage, newPackage);
|
||||
BulkPricingPackage newBUlkPricingPackage = builder.build();
|
||||
stageEntityChange(oldBulkPricingPackage, newBUlkPricingPackage);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
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.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -279,19 +279,20 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
}
|
||||
|
||||
if (!isNullOrEmpty(tokenStatusTransitions)) {
|
||||
// Don't allow package tokens to be created with a scheduled end time since this could allow
|
||||
// future domains to be attributed to the package and never be billed. Package promotion
|
||||
// Don't allow bulk tokens to be created with a scheduled end time since this could allow
|
||||
// future domains to be attributed to the bulk pricing package and never be billed. Bulk
|
||||
// tokens should only be scheduled to end with a brief time period before the status
|
||||
// transition occurs so that no new domains are registered using that token between when the
|
||||
// status is scheduled and when the transition occurs.
|
||||
// TODO(@sarahbot): Create a cleaner way to handle ending packages once we actually have
|
||||
// customers using them
|
||||
// TODO(@sarahbot): Create a cleaner way to handle ending bulk pricing packages once we
|
||||
// actually have customers using them
|
||||
boolean hasEnding =
|
||||
tokenStatusTransitions.containsValue(TokenStatus.ENDED)
|
||||
|| tokenStatusTransitions.containsValue(TokenStatus.CANCELLED);
|
||||
checkArgument(
|
||||
!(PACKAGE.equals(tokenType) && hasEnding),
|
||||
"PACKAGE tokens should not be generated with ENDED or CANCELLED in their transition map");
|
||||
!(BULK_PRICING.equals(tokenType) && hasEnding),
|
||||
"BULK_PRICING tokens should not be generated with ENDED or CANCELLED in their transition"
|
||||
+ " map");
|
||||
}
|
||||
|
||||
if (tokenStrings != null) {
|
||||
|
||||
@@ -19,14 +19,14 @@ import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a {@link PackagePromotion} object. */
|
||||
@Parameters(separators = " =", commandDescription = "Show package promotion object(s)")
|
||||
public class GetPackagePromotionCommand extends GetEppResourceCommand {
|
||||
/** Command to show a {@link BulkPricingPackage} object. */
|
||||
@Parameters(separators = " =", commandDescription = "Show bulk pricing package object(s)")
|
||||
public class GetBulkPricingPackageCommand extends GetEppResourceCommand {
|
||||
|
||||
@Parameter(description = "Package token(s)", required = true)
|
||||
@Parameter(description = "Bulk pricing token(s)", required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
@@ -34,12 +34,12 @@ public class GetPackagePromotionCommand extends GetEppResourceCommand {
|
||||
for (String token : mainParameters) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
PackagePromotion packagePromotion =
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
checkArgumentPresent(
|
||||
PackagePromotion.loadByTokenString(token),
|
||||
"PackagePromotion with package token %s does not exist",
|
||||
BulkPricingPackage.loadByTokenString(token),
|
||||
"BulkPricingPackage with token %s does not exist",
|
||||
token);
|
||||
System.out.println(packagePromotion);
|
||||
System.out.println(bulkPricingPackage);
|
||||
});
|
||||
}
|
||||
}
|
||||
81
core/src/main/java/google/registry/tools/GsonUtils.java
Normal file
81
core/src/main/java/google/registry/tools/GsonUtils.java
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import google.registry.model.adapters.CurrencyJsonAdapter;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import google.registry.util.DateTimeTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Utility class for methods related to GSON and necessary GSON processing. */
|
||||
public class GsonUtils {
|
||||
|
||||
/** Interface to enable GSON post-processing on a particular object after deserialization. */
|
||||
public interface GsonPostProcessable {
|
||||
void postProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some objects may require post-processing after deserialization from JSON.
|
||||
*
|
||||
* <p>We do this upon deserialization in order to make sure that the object matches the format
|
||||
* that we expect to be stored in the database. See {@link
|
||||
* google.registry.model.eppcommon.Address} for an example.
|
||||
*/
|
||||
public static class GsonPostProcessableTypeAdapterFactory implements TypeAdapterFactory {
|
||||
@Override
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
TypeAdapter<T> originalAdapter = gson.getDelegateAdapter(this, type);
|
||||
if (!GsonPostProcessable.class.isAssignableFrom(type.getRawType())) {
|
||||
return originalAdapter;
|
||||
}
|
||||
return new TypeAdapter<T>() {
|
||||
@Override
|
||||
public void write(JsonWriter out, T value) throws IOException {
|
||||
originalAdapter.write(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(JsonReader in) throws IOException {
|
||||
T t = originalAdapter.read(in);
|
||||
((GsonPostProcessable) t).postProcess();
|
||||
return t;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.registerTypeAdapter(CurrencyUnit.class, new CurrencyJsonAdapter())
|
||||
.registerTypeAdapterFactory(new GsonPostProcessableTypeAdapterFactory())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
}
|
||||
|
||||
private GsonUtils() {}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public final class RegistryTool {
|
||||
.put("convert_idn", ConvertIdnCommand.class)
|
||||
.put("count_domains", CountDomainsCommand.class)
|
||||
.put("create_anchor_tenant", CreateAnchorTenantCommand.class)
|
||||
.put("create_bulk_pricing_package", CreateBulkPricingPackageCommand.class)
|
||||
.put(
|
||||
"create_cancellations_for_billing_events",
|
||||
CreateCancellationsForBillingEventsCommand.class)
|
||||
@@ -43,7 +44,6 @@ public final class RegistryTool {
|
||||
.put("create_contact", CreateContactCommand.class)
|
||||
.put("create_domain", CreateDomainCommand.class)
|
||||
.put("create_host", CreateHostCommand.class)
|
||||
.put("create_package_promotion", CreatePackagePromotionCommand.class)
|
||||
.put("create_premium_list", CreatePremiumListCommand.class)
|
||||
.put("create_registrar", CreateRegistrarCommand.class)
|
||||
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
|
||||
@@ -67,13 +67,13 @@ public final class RegistryTool {
|
||||
.put("generate_lordn", GenerateLordnCommand.class)
|
||||
.put("generate_zone_files", GenerateZoneFilesCommand.class)
|
||||
.put("get_allocation_token", GetAllocationTokenCommand.class)
|
||||
.put("get_bulk_pricing_package", GetBulkPricingPackageCommand.class)
|
||||
.put("get_claims_list", GetClaimsListCommand.class)
|
||||
.put("get_contact", GetContactCommand.class)
|
||||
.put("get_domain", GetDomainCommand.class)
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
.put("get_host", GetHostCommand.class)
|
||||
.put("get_keyring_secret", GetKeyringSecretCommand.class)
|
||||
.put("get_package_promotion", GetPackagePromotionCommand.class)
|
||||
.put("get_premium_list", GetPremiumListCommand.class)
|
||||
.put("get_registrar", GetRegistrarCommand.class)
|
||||
.put("get_reserved_list", GetReservedListCommand.class)
|
||||
@@ -104,10 +104,10 @@ public final class RegistryTool {
|
||||
.put("unlock_domain", UnlockDomainCommand.class)
|
||||
.put("unrenew_domain", UnrenewDomainCommand.class)
|
||||
.put("update_allocation_tokens", UpdateAllocationTokensCommand.class)
|
||||
.put("update_bulk_pricing_package", UpdateBulkPricingPackageCommand.class)
|
||||
.put("update_cursors", UpdateCursorsCommand.class)
|
||||
.put("update_domain", UpdateDomainCommand.class)
|
||||
.put("update_keyring_secret", UpdateKeyringSecretCommand.class)
|
||||
.put("update_package_promotion", UpdatePackagePromotionCommand.class)
|
||||
.put("update_premium_list", UpdatePremiumListCommand.class)
|
||||
.put("update_recurrence", UpdateRecurrenceCommand.class)
|
||||
.put("update_registrar", UpdateRegistrarCommand.class)
|
||||
|
||||
@@ -114,18 +114,19 @@ interface RegistryToolComponent {
|
||||
|
||||
void inject(GenerateEscrowDepositCommand command);
|
||||
|
||||
void inject(GetBulkPricingPackageCommand command);
|
||||
|
||||
void inject(GetContactCommand command);
|
||||
|
||||
void inject(GetDomainCommand command);
|
||||
|
||||
void inject(GetHostCommand command);
|
||||
|
||||
void inject(GetPackagePromotionCommand command);
|
||||
|
||||
void inject(GetKeyringSecretCommand command);
|
||||
|
||||
void inject(GetSqlCredentialCommand command);
|
||||
|
||||
void inject(GetTldCommand command);
|
||||
|
||||
void inject(GhostrydeCommand command);
|
||||
|
||||
void inject(ListCursorsCommand command);
|
||||
|
||||
@@ -169,18 +169,18 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
}
|
||||
|
||||
private AllocationToken updateToken(AllocationToken original) {
|
||||
if (endToken && original.getTokenType().equals(TokenType.PACKAGE)) {
|
||||
Long domainsInPackage =
|
||||
tm().query("SELECT COUNT(*) FROM Domain WHERE currentPackageToken = :token", Long.class)
|
||||
if (endToken && original.getTokenType().equals(TokenType.BULK_PRICING)) {
|
||||
Long domainsInBulkPackage =
|
||||
tm().query("SELECT COUNT(*) FROM Domain WHERE currentBulkToken = :token", Long.class)
|
||||
.setParameter("token", original.createVKey())
|
||||
.getSingleResult();
|
||||
|
||||
checkArgument(
|
||||
domainsInPackage == 0,
|
||||
"Package token %s can not end its promotion because it still has %s domains in the"
|
||||
+ " package",
|
||||
domainsInBulkPackage == 0,
|
||||
"Bulk token %s can not end its promotion because it still has %s domains in the"
|
||||
+ " promotion",
|
||||
original.getToken(),
|
||||
domainsInPackage);
|
||||
domainsInBulkPackage);
|
||||
}
|
||||
AllocationToken.Builder builder = original.asBuilder();
|
||||
Optional.ofNullable(allowedClientIds)
|
||||
|
||||
@@ -17,12 +17,12 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Command to update a PackagePromotion */
|
||||
@Parameters(separators = " =", commandDescription = "Update package promotion object(s)")
|
||||
public final class UpdatePackagePromotionCommand extends CreateOrUpdatePackagePromotionCommand {
|
||||
/** Command to update a {@link BulkPricingPackage} */
|
||||
@Parameters(separators = " =", commandDescription = "Update bulk pricing package object(s)")
|
||||
public final class UpdateBulkPricingPackageCommand extends CreateOrUpdateBulkPricingPackageCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--clear_last_notification_sent",
|
||||
@@ -32,10 +32,14 @@ public final class UpdatePackagePromotionCommand extends CreateOrUpdatePackagePr
|
||||
boolean clearLastNotificationSent;
|
||||
|
||||
@Override
|
||||
PackagePromotion getOldPackagePromotion(String token) {
|
||||
Optional<PackagePromotion> oldPackage = PackagePromotion.loadByTokenString(token);
|
||||
checkArgument(oldPackage.isPresent(), "PackagePromotion with token %s does not exist", token);
|
||||
return oldPackage.get();
|
||||
BulkPricingPackage getOldBulkPricingPackage(String token) {
|
||||
Optional<BulkPricingPackage> oldBulkPricingPackage =
|
||||
BulkPricingPackage.loadByTokenString(token);
|
||||
checkArgument(
|
||||
oldBulkPricingPackage.isPresent(),
|
||||
"BulkPricingPackage with token %s does not exist",
|
||||
token);
|
||||
return oldBulkPricingPackage.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,7 +131,7 @@ public class ContactAction implements JsonGetAction {
|
||||
oldContacts,
|
||||
Collections.singletonMap(
|
||||
"contacts",
|
||||
contacts.get().stream().map(c -> c.toJsonMap()).collect(toImmutableList())));
|
||||
contacts.get().stream().map(RegistrarPoc::toJsonMap).collect(toImmutableList())));
|
||||
try {
|
||||
RegistrarSettingsAction.checkContactRequirements(oldContacts, updatedContacts);
|
||||
} catch (FormException e) {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.ui.server.console.settings;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Console action for editing fields on a registrar that are visible in WHOIS/RDAP.
|
||||
*
|
||||
* <p>This doesn't cover many of the registrar fields but rather only those that are visible in
|
||||
* WHOIS/RDAP and don't have any other obvious means of edit.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = WhoisRegistrarFieldsAction.PATH,
|
||||
method = {POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class WhoisRegistrarFieldsAction implements JsonGetAction {
|
||||
|
||||
static final String PATH = "/console-api/settings/whois-fields";
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
private Optional<Registrar> registrar;
|
||||
|
||||
@Inject
|
||||
public WhoisRegistrarFieldsAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
AuthenticatedRegistrarAccessor registrarAccessor,
|
||||
@Parameter("registrar") Optional<Registrar> registrar) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
this.registrarAccessor = registrarAccessor;
|
||||
this.registrar = registrar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!registrar.isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
response.setPayload(gson.toJson("'registrar' parameter is not present"));
|
||||
return;
|
||||
}
|
||||
|
||||
User user = authResult.userAuthInfo().get().consoleUser().get();
|
||||
if (!user.getUserRoles()
|
||||
.hasPermission(
|
||||
registrar.get().getRegistrarId(), ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
tm().transact(() -> loadAndModifyRegistrar(registrar.get()));
|
||||
}
|
||||
|
||||
private void loadAndModifyRegistrar(Registrar providedRegistrar) {
|
||||
Registrar savedRegistrar;
|
||||
try {
|
||||
// reload to make sure the object has all the correct fields
|
||||
savedRegistrar = registrarAccessor.getRegistrar(providedRegistrar.getRegistrarId());
|
||||
} catch (RegistrarAccessDeniedException e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
response.setPayload(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Registrar.Builder newRegistrar = savedRegistrar.asBuilder();
|
||||
newRegistrar.setWhoisServer(providedRegistrar.getWhoisServer());
|
||||
newRegistrar.setUrl(providedRegistrar.getUrl());
|
||||
newRegistrar.setLocalizedAddress(providedRegistrar.getLocalizedAddress());
|
||||
tm().put(newRegistrar.build());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.domain.DomainFlowUtils;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
@@ -46,7 +47,6 @@ import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -83,7 +83,7 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
private final JsonActionRunner jsonActionRunner;
|
||||
private final AuthResult authResult;
|
||||
private final AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
private final SendEmailService sendEmailService;
|
||||
private final GmailClient gmailClient;
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||
|
||||
@@ -93,14 +93,14 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
JsonActionRunner jsonActionRunner,
|
||||
AuthResult authResult,
|
||||
AuthenticatedRegistrarAccessor registrarAccessor,
|
||||
SendEmailService sendEmailService,
|
||||
GmailClient gmailClient,
|
||||
DomainLockUtils domainLockUtils,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress) {
|
||||
this.req = req;
|
||||
this.jsonActionRunner = jsonActionRunner;
|
||||
this.authResult = authResult;
|
||||
this.registrarAccessor = registrarAccessor;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.gmailClient = gmailClient;
|
||||
this.domainLockUtils = domainLockUtils;
|
||||
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAc
|
||||
ImmutableList<InternetAddress> recipients =
|
||||
ImmutableList.of(new InternetAddress(userEmail, true));
|
||||
String action = isLock ? "lock" : "unlock";
|
||||
sendEmailService.sendEmail(
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setBody(body)
|
||||
.setSubject(String.format("Registry %s verification", action))
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<class>google.registry.model.domain.secdns.DomainDsData</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
|
||||
<class>google.registry.model.domain.token.AllocationToken</class>
|
||||
<class>google.registry.model.domain.token.PackagePromotion</class>
|
||||
<class>google.registry.model.domain.token.BulkPricingPackage</class>
|
||||
<class>google.registry.model.host.HostHistory</class>
|
||||
<class>google.registry.model.host.Host</class>
|
||||
<class>google.registry.model.poll.PollMessage</class>
|
||||
|
||||
@@ -32,7 +32,7 @@ import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
@@ -53,8 +53,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Unit tests for {@link CheckPackagesComplianceAction}. */
|
||||
public class CheckPackagesComplianceActionTest {
|
||||
/** Unit tests for {@link CheckBulkComplianceAction}. */
|
||||
public class CheckBulkComplianceActionTest {
|
||||
// This is the default creation time for test data.
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2012-03-25TZ"));
|
||||
private static final String CREATE_LIMIT_EMAIL_SUBJECT = "create limit subject";
|
||||
@@ -72,14 +72,14 @@ public class CheckPackagesComplianceActionTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
private CheckPackagesComplianceAction action;
|
||||
private CheckBulkComplianceAction action;
|
||||
private AllocationToken token;
|
||||
private final TestLogHandler logHandler = new TestLogHandler();
|
||||
private final Logger loggerToIntercept =
|
||||
Logger.getLogger(CheckPackagesComplianceAction.class.getCanonicalName());
|
||||
Logger.getLogger(CheckBulkComplianceAction.class.getCanonicalName());
|
||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||
private Contact contact;
|
||||
private PackagePromotion packagePromotion;
|
||||
private BulkPricingPackage bulkPricingPackage;
|
||||
private SendEmailUtils sendEmailUtils;
|
||||
private ArgumentCaptor<EmailMessage> emailCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
|
||||
@@ -94,7 +94,7 @@ public class CheckPackagesComplianceActionTest {
|
||||
emailService);
|
||||
createTld("tld");
|
||||
action =
|
||||
new CheckPackagesComplianceAction(
|
||||
new CheckBulkComplianceAction(
|
||||
sendEmailUtils,
|
||||
clock,
|
||||
CREATE_LIMIT_EMAIL_SUBJECT,
|
||||
@@ -108,19 +108,19 @@ public class CheckPackagesComplianceActionTest {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
packagePromotion =
|
||||
new PackagePromotion.Builder()
|
||||
bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setMaxDomains(3)
|
||||
.setMaxCreates(1)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build();
|
||||
@@ -134,46 +134,48 @@ public class CheckPackagesComplianceActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noPackageOverCreateLimit() {
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
void testSuccess_noBulkPackageOverCreateLimit() {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
verifyNoInteractions(emailService);
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found no packages over their create limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found no bulk pricing packages over their create limit.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_onePackageOverCreateLimit() throws Exception {
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
void testSuccess_oneBulkPackageOverCreateLimit() throws Exception {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Create limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their create limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 1 bulk pricing packages over their create limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceeded their max domain creation limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceeded their max domain creation"
|
||||
+ " limit by 1 name(s).");
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
EmailMessage emailMessage = emailCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo(CREATE_LIMIT_EMAIL_SUBJECT);
|
||||
@@ -182,92 +184,93 @@ public class CheckPackagesComplianceActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_multiplePackagesOverCreateLimit() {
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
void testSuccess_multipleBulkPricingPackagesOverCreateLimit() {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Create limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
AllocationToken token2 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion2 =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token2)
|
||||
.setMaxDomains(8)
|
||||
.setMaxCreates(1)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion2));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz2.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 2 packages over their create limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 2 bulk pricing packages over their create limit.");
|
||||
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceeded their max domain creation limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceeded their max domain creation"
|
||||
+ " limit by 1 name(s).");
|
||||
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token token has exceeded their max domain creation limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token token has exceeded their max domain creation"
|
||||
+ " limit by 1 name(s).");
|
||||
verify(emailService, times(2)).sendEmail(any(EmailMessage.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_onlyChecksCurrentBillingYear() {
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
AllocationToken token2 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion2 =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage packagePromotion2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token2)
|
||||
.setMaxDomains(8)
|
||||
.setMaxCreates(1)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2015-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion2));
|
||||
@@ -276,289 +279,300 @@ public class CheckPackagesComplianceActionTest {
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found no packages over their create limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found no bulk pricing packages over their create limit.");
|
||||
verifyNoInteractions(emailService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noPackageOverActiveDomainsLimit() {
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
void testSuccess_noBulkPricingPackageOverActiveDomainsLimit() {
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
verifyNoInteractions(emailService);
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found no packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found no bulk pricing packages over their active domains limit.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_onePackageOverActiveDomainsLimit() {
|
||||
packagePromotion = packagePromotion.asBuilder().setMaxCreates(4).setMaxDomains(1).build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
void testSuccess_oneBulkPricingPackageOverActiveDomainsLimit() {
|
||||
bulkPricingPackage = bulkPricingPackage.asBuilder().setMaxCreates(4).setMaxDomains(1).build();
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
AllocationToken token2 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion2 =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token2)
|
||||
.setMaxDomains(8)
|
||||
.setMaxCreates(4)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion2));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 1 bulk pricing packages over their active domains limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
EmailMessage emailMessage = emailCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT);
|
||||
assertThat(emailMessage.body())
|
||||
.isEqualTo(
|
||||
String.format(DOMAIN_LIMIT_WARNING_EMAIL_BODY, 1, "abc123", "The Registrar", 1, 2));
|
||||
PackagePromotion packageAfterCheck =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
|
||||
BulkPricingPackage packageAfterCheck =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString(token.getToken()).get());
|
||||
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_multiplePackagesOverActiveDomainsLimit() {
|
||||
void testSuccess_multipleBulkPricingPackagesOverActiveDomainsLimit() {
|
||||
tm().transact(
|
||||
() -> tm().put(packagePromotion.asBuilder().setMaxDomains(1).setMaxCreates(4).build()));
|
||||
() ->
|
||||
tm().put(bulkPricingPackage.asBuilder().setMaxDomains(1).setMaxCreates(4).build()));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
AllocationToken token2 =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion2 =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage2 =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token2)
|
||||
.setMaxDomains(1)
|
||||
.setMaxCreates(5)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion2));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage2));
|
||||
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo2.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz2.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token2.createVKey())
|
||||
.setCurrentBulkToken(token2.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 2 packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 2 bulk pricing packages over their active domains limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token token has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token token has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
verify(emailService, times(2)).sendEmail(any(EmailMessage.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarningEmail_DoesNotSendAgain() {
|
||||
packagePromotion =
|
||||
packagePromotion
|
||||
void
|
||||
testSuccess_bulkPricingPackageOverActiveDomainsLimitAlreadySentWarningEmail_DoesNotSendAgain() {
|
||||
bulkPricingPackage =
|
||||
bulkPricingPackage
|
||||
.asBuilder()
|
||||
.setMaxCreates(4)
|
||||
.setMaxDomains(1)
|
||||
.setLastNotificationSent(clock.nowUtc().minusDays(5))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 1 bulk pricing packages over their active domains limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
verifyNoInteractions(emailService);
|
||||
PackagePromotion packageAfterCheck =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
|
||||
BulkPricingPackage packageAfterCheck =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString(token.getToken()).get());
|
||||
assertThat(packageAfterCheck.getLastNotificationSent().get())
|
||||
.isEqualTo(clock.nowUtc().minusDays(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarningEmailOver40DaysAgo_SendsAgain() {
|
||||
packagePromotion =
|
||||
packagePromotion
|
||||
void
|
||||
testSuccess_bulkPricingPackageOverActiveDomainsLimitAlreadySentWarningEmailOver40DaysAgo_SendsAgain() {
|
||||
bulkPricingPackage =
|
||||
bulkPricingPackage
|
||||
.asBuilder()
|
||||
.setMaxCreates(4)
|
||||
.setMaxDomains(1)
|
||||
.setLastNotificationSent(clock.nowUtc().minusDays(45))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 1 bulk pricing packages over their active domains limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
EmailMessage emailMessage = emailCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT);
|
||||
assertThat(emailMessage.body())
|
||||
.isEqualTo(
|
||||
String.format(DOMAIN_LIMIT_WARNING_EMAIL_BODY, 1, "abc123", "The Registrar", 1, 2));
|
||||
PackagePromotion packageAfterCheck =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
|
||||
BulkPricingPackage packageAfterCheck =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString(token.getToken()).get());
|
||||
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarning30DaysAgo_SendsUpgradeEmail() {
|
||||
packagePromotion =
|
||||
packagePromotion
|
||||
void
|
||||
testSuccess_bulkPricingPackageOverActiveDomainsLimitAlreadySentWarning30DaysAgo_SendsUpgradeEmail() {
|
||||
bulkPricingPackage =
|
||||
bulkPricingPackage
|
||||
.asBuilder()
|
||||
.setMaxCreates(4)
|
||||
.setMaxDomains(1)
|
||||
.setLastNotificationSent(clock.nowUtc().minusDays(31))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
// Domains limit is 1, creating 2 domains to go over the limit
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("foo.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
persistEppResource(
|
||||
DatabaseHelper.newDomain("buzz.tld", contact)
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
|
||||
action.run();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO, "Found 1 bulk pricing packages over their active domains limit.");
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(
|
||||
Level.INFO,
|
||||
"Package with package token abc123 has exceed their max active domains limit by 1"
|
||||
+ " name(s).");
|
||||
"Bulk pricing package with bulk token abc123 has exceed their max active domains limit"
|
||||
+ " by 1 name(s).");
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
EmailMessage emailMessage = emailCaptor.getValue();
|
||||
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_UPGRADE_EMAIL_SUBJECT);
|
||||
assertThat(emailMessage.body())
|
||||
.isEqualTo(
|
||||
String.format(DOMAIN_LIMIT_UPGRADE_EMAIL_BODY, 1, "abc123", "The Registrar", 1, 2));
|
||||
PackagePromotion packageAfterCheck =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
|
||||
BulkPricingPackage packageAfterCheck =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString(token.getToken()).get());
|
||||
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -48,7 +49,6 @@ import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import google.registry.util.StringGenerator.Alphabets;
|
||||
import java.util.Optional;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
@@ -85,7 +85,7 @@ public class RelockDomainActionTest {
|
||||
|
||||
private Domain domain;
|
||||
private RegistryLock oldLock;
|
||||
@Mock private SendEmailService sendEmailService;
|
||||
@Mock private GmailClient gmailClient;
|
||||
private RelockDomainAction action;
|
||||
|
||||
@BeforeEach
|
||||
@@ -107,7 +107,7 @@ public class RelockDomainActionTest {
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
verifyNoMoreInteractions(sendEmailService);
|
||||
verifyNoMoreInteractions(gmailClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -260,7 +260,7 @@ public class RelockDomainActionTest {
|
||||
ImmutableSet.of(new InternetAddress("Marla.Singer.RegistryLock@crr.com")))
|
||||
.setFrom(new InternetAddress("outgoing@example.com"))
|
||||
.build();
|
||||
verify(sendEmailService).sendEmail(expectedEmail);
|
||||
verify(gmailClient).sendEmail(expectedEmail);
|
||||
}
|
||||
|
||||
private void assertNonTransientFailureEmail(String exceptionMessage) throws Exception {
|
||||
@@ -294,7 +294,7 @@ public class RelockDomainActionTest {
|
||||
.setRecipients(recipients)
|
||||
.setFrom(new InternetAddress("outgoing@example.com"))
|
||||
.build();
|
||||
verify(sendEmailService).sendEmail(expectedEmail);
|
||||
verify(gmailClient).sendEmail(expectedEmail);
|
||||
}
|
||||
|
||||
private void assertTaskEnqueued(int numAttempts) {
|
||||
@@ -328,7 +328,7 @@ public class RelockDomainActionTest {
|
||||
alertRecipientAddress,
|
||||
gSuiteOutgoingAddress,
|
||||
"support@example.com",
|
||||
sendEmailService,
|
||||
gmailClient,
|
||||
domainLockUtils,
|
||||
response);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.flows;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.TestDataHelper.loadFile;
|
||||
import static google.registry.testing.TestLogHandlerUtils.findFirstLogMessageByPrefix;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -30,11 +31,13 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
@@ -46,7 +49,6 @@ import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/** Unit tests for {@link FlowRunner}. */
|
||||
class FlowRunnerTest {
|
||||
@@ -78,6 +80,21 @@ class FlowRunnerTest {
|
||||
}
|
||||
}
|
||||
|
||||
static class TestTransactionalFlow implements TransactionalFlow {
|
||||
private final Optional<TransactionIsolationLevel> isolationLevel;
|
||||
|
||||
TestTransactionalFlow(Optional<TransactionIsolationLevel> isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseOrGreeting run() {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
isolationLevel.orElse(tm().getDefaultTransactionIsolationLevel()));
|
||||
return mock(EppResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
JdkLoggerConfig.getConfig(FlowRunner.class).addHandler(handler);
|
||||
@@ -90,23 +107,39 @@ class FlowRunnerTest {
|
||||
flowRunner.isDryRun = false;
|
||||
flowRunner.isSuperuser = false;
|
||||
flowRunner.isTransactional = false;
|
||||
flowRunner.isolationLevelOverride = Optional.empty();
|
||||
flowRunner.sessionMetadata =
|
||||
new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.of());
|
||||
flowRunner.trid = Trid.create("client-123", "server-456");
|
||||
flowRunner.flowReporter = Mockito.mock(FlowReporter.class);
|
||||
flowRunner.flowReporter = mock(FlowReporter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_nonTransactionalCommand_setsCommandNameOnMetric() throws Exception {
|
||||
flowRunner.isTransactional = true;
|
||||
flowRunner.run(eppMetricBuilder);
|
||||
assertThat(eppMetricBuilder.build().getCommandName()).hasValue("TestCommand");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_transactionalCommand_setsCommandNameOnMetric() throws Exception {
|
||||
flowRunner.isTransactional = true;
|
||||
flowRunner.flowClass = TestTransactionalFlow.class;
|
||||
flowRunner.flowProvider = () -> new TestTransactionalFlow(Optional.empty());
|
||||
flowRunner.run(eppMetricBuilder);
|
||||
assertThat(eppMetricBuilder.build().getCommandName()).hasValue("TestCommand");
|
||||
assertThat(eppMetricBuilder.build().getCommandName()).hasValue("TestTransactional");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_transactionalCommand_isolationLevelOverride() throws Exception {
|
||||
flowRunner.isTransactional = true;
|
||||
flowRunner.isolationLevelOverride =
|
||||
Optional.of(TransactionIsolationLevel.TRANSACTION_READ_UNCOMMITTED);
|
||||
flowRunner.flowClass = TestTransactionalFlow.class;
|
||||
flowRunner.flowProvider = () -> new TestTransactionalFlow(flowRunner.isolationLevelOverride);
|
||||
if (RegistryConfig.getHibernatePerTransactionIsolationEnabled()) {
|
||||
flowRunner.run(eppMetricBuilder);
|
||||
assertThat(eppMetricBuilder.build().getCommandName()).hasValue("TestTransactional");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,8 +26,8 @@ import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEF
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
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.eppcommon.StatusValue.PENDING_DELETE;
|
||||
@@ -78,10 +78,10 @@ import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.domain.DomainCreateFlow.AnchorTenantCreatePeriodException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.BulkDomainRegisteredForTooManyYearsException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.PackageDomainRegisteredForTooManyYearsException;
|
||||
import google.registry.flows.domain.DomainCreateFlow.RenewalPriceInfo;
|
||||
import google.registry.flows.domain.DomainCreateFlow.SignedMarksOnlyDuringSunriseException;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkExpiredException;
|
||||
@@ -3673,12 +3673,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_packageToken_addsTokenToDomain() throws Exception {
|
||||
void testSuccess_bulkToken_addsTokenToDomain() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
@@ -3696,16 +3696,16 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.put("EXDATE", "2000-04-03T22:00:00.0Z")
|
||||
.build()));
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
assertThat(domain.getCurrentPackageToken()).isPresent();
|
||||
assertThat(domain.getCurrentPackageToken()).hasValue(token.createVKey());
|
||||
assertThat(domain.getCurrentBulkToken()).isPresent();
|
||||
assertThat(domain.getCurrentBulkToken()).hasValue(token.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_packageToken_registrationTooLong() throws Exception {
|
||||
void testFailure_bulkToken_registrationTooLong() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
@@ -3715,10 +3715,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"domain_create_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
|
||||
EppException thrown =
|
||||
assertThrows(PackageDomainRegisteredForTooManyYearsException.class, this::runFlow);
|
||||
assertThrows(BulkDomainRegisteredForTooManyYearsException.class, this::runFlow);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"The package token abc123 cannot be used to register names for longer than 1 year.");
|
||||
"The bulk token abc123 cannot be used to register names for longer than 1 year.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,14 +1091,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
doSuccessfulTest("domain_info_response_bulk.xml", false);
|
||||
@@ -1111,14 +1111,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
@@ -1143,14 +1143,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentPackageToken(token.createVKey()).build();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(token.createVKey()).build();
|
||||
persistResource(domain);
|
||||
sessionMetadata.setRegistrarId("TheRegistrar");
|
||||
setEppInput("domain_info_bulk.xml");
|
||||
|
||||
@@ -20,8 +20,8 @@ import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWit
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
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.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -77,8 +77,8 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemoveDomainTokenOnPackageDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemoveDomainTokenOnNonPackageDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.MissingRemoveDomainTokenOnBulkPricingDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.RemoveDomainTokenOnNonBulkPricingDomainException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
@@ -1249,59 +1249,53 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailsPackageDomainInvalidAllocationToken() throws Exception {
|
||||
void testFailsBulkPricingDomainInvalidAllocationToken() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
reloadResourceByForeignKey().asBuilder().setCurrentBulkToken(token.createVKey()).build());
|
||||
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "abc123"));
|
||||
|
||||
EppException thrown =
|
||||
assertThrows(MissingRemoveDomainTokenOnPackageDomainException.class, this::runFlow);
|
||||
assertThrows(MissingRemoveDomainTokenOnBulkPricingDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailsToRenewPackageDomainNoRemoveDomainToken() throws Exception {
|
||||
void testFailsToRenewBulkPricingDomainNoRemoveDomainToken() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistDomain();
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
reloadResourceByForeignKey().asBuilder().setCurrentBulkToken(token.createVKey()).build());
|
||||
|
||||
setEppInput("domain_renew.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "5"));
|
||||
|
||||
EppException thrown =
|
||||
assertThrows(MissingRemoveDomainTokenOnPackageDomainException.class, this::runFlow);
|
||||
assertThrows(MissingRemoveDomainTokenOnBulkPricingDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailsToRenewNonPackageDomainWithRemoveDomainToken() throws Exception {
|
||||
void testFailsToRenewNonBulkPricingDomainWithRemoveDomainToken() throws Exception {
|
||||
persistDomain();
|
||||
|
||||
setEppInput(
|
||||
@@ -1309,7 +1303,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEDOMAIN__"));
|
||||
|
||||
EppException thrown =
|
||||
assertThrows(RemoveDomainTokenOnNonPackageDomainException.class, this::runFlow);
|
||||
assertThrows(RemoveDomainTokenOnNonBulkPricingDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@@ -1319,17 +1313,14 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
reloadResourceByForeignKey().asBuilder().setCurrentBulkToken(token.createVKey()).build());
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEDOMAIN__"));
|
||||
@@ -1339,10 +1330,10 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
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
|
||||
// We still need to verify that the bulk token is removed as it's not being tested as a part of
|
||||
// doSuccessfulTest
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
Truth8.assertThat(domain.getCurrentPackageToken()).isEmpty();
|
||||
Truth8.assertThat(domain.getCurrentBulkToken()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1351,17 +1342,14 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.build());
|
||||
persistDomain(SPECIFIED, Money.of(USD, 2));
|
||||
persistResource(
|
||||
reloadResourceByForeignKey()
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.build());
|
||||
reloadResourceByForeignKey().asBuilder().setCurrentBulkToken(token.createVKey()).build());
|
||||
|
||||
setEppInput(
|
||||
"domain_renew_allocationtoken.xml",
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.flows.domain;
|
||||
import static com.google.common.collect.MoreCollectors.onlyElement;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
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.reporting.DomainTransactionRecord.TransactionReportField.NET_ADDS_4_YR;
|
||||
@@ -382,12 +382,12 @@ class DomainTransferApproveFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDryRun_PackageDomain() throws Exception {
|
||||
void testDryRun_bulkPricingDomain() throws Exception {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -398,8 +398,7 @@ class DomainTransferApproveFlowTest
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("10.00")))
|
||||
.build());
|
||||
persistResource(
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
persistResource(domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
clock.advanceOneMilli();
|
||||
setEppInput("domain_transfer_approve_wildcard.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
dryRunFlowAssertResponse(loadFile("domain_transfer_approve_response.xml"));
|
||||
@@ -411,12 +410,12 @@ class DomainTransferApproveFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_removesPackageToken() throws Exception {
|
||||
void testSuccess_removesBulkToken() throws Exception {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -427,8 +426,7 @@ class DomainTransferApproveFlowTest
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("10.00")))
|
||||
.build());
|
||||
persistResource(
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
persistResource(domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
clock.advanceOneMilli();
|
||||
setEppInput("domain_transfer_approve_wildcard.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
DateTime now = clock.nowUtc();
|
||||
@@ -460,7 +458,7 @@ class DomainTransferApproveFlowTest
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setRenewalPrice(Money.of(USD, new BigDecimal("10.00")))
|
||||
.build());
|
||||
assertThat(domain.getCurrentPackageToken()).isEmpty();
|
||||
assertThat(domain.getCurrentBulkToken()).isEmpty();
|
||||
assertThat(domain.getCurrentSponsorRegistrarId()).isEqualTo("NewRegistrar");
|
||||
assertThat(loadByKey(domain.getAutorenewBillingEvent()).getRenewalPriceBehavior())
|
||||
.isEqualTo(RenewalPriceBehavior.DEFAULT);
|
||||
|
||||
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
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.reporting.DomainTransactionRecord.TransactionReportField.TRANSFER_SUCCESSFUL;
|
||||
@@ -1329,7 +1329,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_specifiedRenewalPrice_notCarriedOverForPackageName() throws Exception {
|
||||
void testSuccess_specifiedRenewalPrice_notCarriedOverForBulkPricingName() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
persistResource(Tld.get("tld").asBuilder().build());
|
||||
domain = loadByEntity(domain);
|
||||
@@ -1343,13 +1343,13 @@ class DomainTransferRequestFlowTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
domain =
|
||||
persistResource(
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
@@ -1388,7 +1388,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_defaultRenewalPrice_carriedOverForPackageName() throws Exception {
|
||||
void testSuccess_defaultRenewalPrice_carriedOverForBulkPricingName() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
persistResource(Tld.get("tld").asBuilder().build());
|
||||
domain = loadByEntity(domain);
|
||||
@@ -1401,13 +1401,13 @@ class DomainTransferRequestFlowTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
domain =
|
||||
persistResource(
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
DateTime now = clock.nowUtc();
|
||||
|
||||
setEppInput("domain_transfer_request.xml");
|
||||
@@ -1445,7 +1445,7 @@ class DomainTransferRequestFlowTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_packageName_zeroPeriod() throws Exception {
|
||||
void testSuccess_bulkPricingName_zeroPeriod() throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
persistResource(Tld.get("tld").asBuilder().build());
|
||||
domain = loadByEntity(domain);
|
||||
@@ -1458,13 +1458,13 @@ class DomainTransferRequestFlowTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
domain =
|
||||
persistResource(
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
|
||||
doSuccessfulSuperuserExtensionTest(
|
||||
"domain_transfer_request_superuser_extension.xml",
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.model.domain;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
@@ -131,7 +131,7 @@ public class DomainSqlTest {
|
||||
allocationToken =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123Unlimited")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("dev", "app"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -152,9 +152,9 @@ public class DomainSqlTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDomainBasePersistenceWithCurrentPackageToken() {
|
||||
void testDomainBasePersistenceWithCurrentBulkToken() {
|
||||
persistResource(allocationToken);
|
||||
domain = domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build();
|
||||
persistDomain();
|
||||
assertEqualDomainExcept(loadByKey(domain.createVKey()));
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
@@ -812,7 +812,7 @@ public class DomainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClone_removesPackageFromTransferredDomain() {
|
||||
void testClone_removesBulkTokenFromTransferredDomain() {
|
||||
// If the transfer implicitly succeeded, the expiration time should be extended even if it
|
||||
// hadn't already expired
|
||||
DateTime now = DateTime.now(UTC);
|
||||
@@ -831,7 +831,7 @@ public class DomainTest {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -841,13 +841,13 @@ public class DomainTest {
|
||||
.asBuilder()
|
||||
.setRegistrationExpirationTime(previousExpiration)
|
||||
.setTransferData(transferData)
|
||||
.setCurrentPackageToken(allocationToken.createVKey())
|
||||
.setCurrentBulkToken(allocationToken.createVKey())
|
||||
.build());
|
||||
|
||||
assertThat(domain.getCurrentPackageToken()).isPresent();
|
||||
assertThat(domain.getCurrentBulkToken()).isPresent();
|
||||
Domain clonedDomain = domain.cloneProjectedAtTime(now);
|
||||
assertThat(clonedDomain.getRegistrationExpirationTime()).isEqualTo(newExpiration);
|
||||
assertThat(clonedDomain.getCurrentPackageToken()).isEmpty();
|
||||
assertThat(clonedDomain.getCurrentBulkToken()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -876,7 +876,7 @@ public class DomainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClone_doesNotRemovePackageForPendingTransfer() {
|
||||
void testClone_doesNotRemoveBulkTokenForPendingTransfer() {
|
||||
// Pending transfers shouldn't affect the expiration time
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime transferExpirationTime = now.plusDays(1);
|
||||
@@ -892,7 +892,7 @@ public class DomainTest {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
@@ -902,12 +902,12 @@ public class DomainTest {
|
||||
.asBuilder()
|
||||
.setRegistrationExpirationTime(previousExpiration)
|
||||
.setTransferData(transferData)
|
||||
.setCurrentPackageToken(allocationToken.createVKey())
|
||||
.setCurrentBulkToken(allocationToken.createVKey())
|
||||
.build());
|
||||
|
||||
Domain clonedDomain = domain.cloneProjectedAtTime(now);
|
||||
assertThat(clonedDomain.getRegistrationExpirationTime()).isEqualTo(previousExpiration);
|
||||
assertThat(clonedDomain.getCurrentPackageToken().get()).isEqualTo(allocationToken.createVKey());
|
||||
assertThat(clonedDomain.getCurrentBulkToken().get()).isEqualTo(allocationToken.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1043,48 +1043,48 @@ public class DomainTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_currentPackageTokenWrongPackageType() {
|
||||
void testFail_currentBulkTokenWrongTokenType() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder().setToken("abc123").setTokenType(SINGLE_USE).build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
() -> domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("The currentPackageToken must have a PACKAGE TokenType");
|
||||
.isEqualTo("The currentBulkToken must have a BULK_PRICING TokenType");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_packageTokenDoesNotExist() {
|
||||
void testFailure_bulkTokenDoesNotExist() {
|
||||
AllocationToken allocationToken =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("The package token abc123 does not exist");
|
||||
() -> domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build());
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("The bulk token abc123 does not exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_removeCurrentPackageToken() {
|
||||
void testSuccess_removeCurrentBulkToken() {
|
||||
AllocationToken allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.build());
|
||||
domain = domain.asBuilder().setCurrentPackageToken(allocationToken.createVKey()).build();
|
||||
assertThat(domain.getCurrentPackageToken().get()).isEqualTo(allocationToken.createVKey());
|
||||
domain = domain.asBuilder().setCurrentPackageToken(null).build();
|
||||
assertThat(domain.getCurrentPackageToken()).isEmpty();
|
||||
domain = domain.asBuilder().setCurrentBulkToken(allocationToken.createVKey()).build();
|
||||
assertThat(domain.getCurrentBulkToken().get()).isEqualTo(allocationToken.createVKey());
|
||||
domain = domain.asBuilder().setCurrentBulkToken(null).build();
|
||||
assertThat(domain.getCurrentBulkToken()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.CAN
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
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.testing.DatabaseHelper.createTld;
|
||||
@@ -218,28 +218,28 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_packageTokenNotSpecifiedRenewalBehavior() {
|
||||
void testFail_bulkTokenNotSpecifiedRenewalBehavior() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT);
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Package tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
.isEqualTo("Bulk tokens must have renewalPriceBehavior set to SPECIFIED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_packageTokenDiscountPremium() {
|
||||
void testFail_bulkTokenDiscountPremium() {
|
||||
AllocationToken.Builder builder =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountPremiums(true);
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Package tokens cannot discount premium names");
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Bulk tokens cannot discount premium names");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -292,17 +292,17 @@ public class AllocationTokenTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild_onlyOneClientInPackage() {
|
||||
void testBuild_onlyOneClientInBulkPricingPackage() {
|
||||
Buildable.Builder<AllocationToken> builder =
|
||||
new AllocationToken.Builder()
|
||||
.setToken("foobar")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("foo", "bar"));
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("PACKAGE tokens must have exactly one allowed client registrar");
|
||||
.isEqualTo("BULK_PRICING tokens must have exactly one allowed client registrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -31,10 +31,10 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Unit tests for {@link PackagePromotion}. */
|
||||
public class PackagePromotionTest extends EntityTestCase {
|
||||
/** Unit tests for {@link BulkPricingPackage}. */
|
||||
public class BulkPricingPackageTest extends EntityTestCase {
|
||||
|
||||
public PackagePromotionTest() {
|
||||
public BulkPricingPackageTest() {
|
||||
super(JpaEntityCoverageCheck.ENABLED);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PackagePromotionTest extends EntityTestCase {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -57,23 +57,23 @@ public class PackagePromotionTest extends EntityTestCase {
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
|
||||
PackagePromotion packagePromotion =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 10000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 10000))
|
||||
.setMaxCreates(40)
|
||||
.setMaxDomains(10)
|
||||
.setNextBillingDate(DateTime.parse("2011-11-12T05:00:00Z"))
|
||||
.build();
|
||||
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
assertAboutImmutableObjects()
|
||||
.that(tm().transact(() -> PackagePromotion.loadByTokenString("abc123")).get())
|
||||
.isEqualExceptFields(packagePromotion, "packagePromotionId");
|
||||
.that(tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123")).get())
|
||||
.isEqualExceptFields(bulkPricingPackage, "bulkPricingId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_tokenIsNotPackage() {
|
||||
void testFail_tokenIsNotBulkToken() {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
@@ -90,14 +90,14 @@ public class PackagePromotionTest extends EntityTestCase {
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
persistResource(
|
||||
new PackagePromotion.Builder()
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 10000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 10000))
|
||||
.setMaxCreates(40)
|
||||
.setMaxDomains(10)
|
||||
.setNextBillingDate(DateTime.parse("2011-11-12T05:00:00Z"))
|
||||
.build()));
|
||||
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Allocation token must be a PACKAGE type");
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Allocation token must be a BULK_PRICING type");
|
||||
}
|
||||
}
|
||||
@@ -507,11 +507,14 @@ class RegistrarTest extends EntityTestCase {
|
||||
@Test
|
||||
void testSuccess_setAllowedTldsUncached() {
|
||||
assertThat(
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setAllowedTldsUncached(ImmutableSet.of("xn--q9jyb4c"))
|
||||
.build()
|
||||
.getAllowedTlds())
|
||||
tm().transact(
|
||||
() -> {
|
||||
return registrar
|
||||
.asBuilder()
|
||||
.setAllowedTldsUncached(ImmutableSet.of("xn--q9jyb4c"))
|
||||
.build()
|
||||
.getAllowedTlds();
|
||||
}))
|
||||
.containsExactly("xn--q9jyb4c");
|
||||
}
|
||||
|
||||
@@ -524,9 +527,12 @@ class RegistrarTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testFailure_setAllowedTldsUncached_nonexistentTld() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> registrar.asBuilder().setAllowedTldsUncached(ImmutableSet.of("bad")));
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> registrar.asBuilder().setAllowedTldsUncached(ImmutableSet.of("bad")));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -614,11 +620,14 @@ class RegistrarTest extends EntityTestCase {
|
||||
|
||||
// Test that the uncached version works
|
||||
assertThat(
|
||||
registrar
|
||||
.asBuilder()
|
||||
.setAllowedTldsUncached(ImmutableSet.of("newtld"))
|
||||
.build()
|
||||
.getAllowedTlds())
|
||||
tm().transact(
|
||||
() -> {
|
||||
return registrar
|
||||
.asBuilder()
|
||||
.setAllowedTldsUncached(ImmutableSet.of("newtld"))
|
||||
.build()
|
||||
.getAllowedTlds();
|
||||
}))
|
||||
.containsExactly("newtld");
|
||||
|
||||
// Test that the "regular" cached version fails. If this doesn't throw - then we changed how
|
||||
|
||||
@@ -16,6 +16,9 @@ package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_READ_COMMITTED;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_READ_UNCOMMITTED;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.assertDetachedFromEntityManager;
|
||||
import static google.registry.testing.DatabaseHelper.existsInDb;
|
||||
@@ -31,6 +34,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
|
||||
@@ -81,7 +85,7 @@ class JpaTransactionManagerImplTest {
|
||||
.buildUnitTestExtension();
|
||||
|
||||
@Test
|
||||
void transact_succeeds() {
|
||||
void transact_success() {
|
||||
assertPersonEmpty();
|
||||
assertCompanyEmpty();
|
||||
tm().transact(
|
||||
@@ -89,6 +93,7 @@ class JpaTransactionManagerImplTest {
|
||||
insertPerson(10);
|
||||
insertCompany("Foo");
|
||||
insertCompany("Bar");
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
});
|
||||
assertPersonCount(1);
|
||||
assertPersonExist(10);
|
||||
@@ -97,6 +102,149 @@ class JpaTransactionManagerImplTest {
|
||||
assertCompanyExist("Bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void transact_setIsolationLevel() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
RegistryConfig.getHibernatePerTransactionIsolationEnabled()
|
||||
? TRANSACTION_READ_UNCOMMITTED
|
||||
: tm().getDefaultTransactionIsolationLevel());
|
||||
return null;
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
// Make sure that we can start a new transaction on the same thread with a different isolation
|
||||
// level.
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
RegistryConfig.getHibernatePerTransactionIsolationEnabled()
|
||||
? TRANSACTION_REPEATABLE_READ
|
||||
: tm().getDefaultTransactionIsolationLevel());
|
||||
return null;
|
||||
},
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
void transact_nestedTransactions_perTransactionIsolationLevelEnabled() {
|
||||
if (!RegistryConfig.getHibernatePerTransactionIsolationEnabled()) {
|
||||
return;
|
||||
}
|
||||
// Nested transactions allowed (both at the default isolation level).
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
});
|
||||
});
|
||||
// Nested transactions allowed (enclosed transaction does not have an override, using the
|
||||
// enclosing transaction's level).
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(TRANSACTION_READ_UNCOMMITTED);
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(TRANSACTION_READ_UNCOMMITTED);
|
||||
});
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
// Nested transactions allowed (Both have the same override).
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(TRANSACTION_REPEATABLE_READ);
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(TRANSACTION_REPEATABLE_READ);
|
||||
},
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
},
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
// Nested transactions disallowed (enclosed transaction has an override that conflicts from the
|
||||
// default).
|
||||
IllegalStateException e =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().transact(() -> {}, TRANSACTION_READ_COMMITTED);
|
||||
}));
|
||||
assertThat(e).hasMessageThat().contains("conflict detected");
|
||||
// Nested transactions disallowed (conflicting overrides).
|
||||
e =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().transact(() -> {}, TRANSACTION_READ_COMMITTED);
|
||||
},
|
||||
TRANSACTION_REPEATABLE_READ));
|
||||
assertThat(e).hasMessageThat().contains("conflict detected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void transact_nestedTransactions_perTransactionIsolationLevelDisabled() {
|
||||
if (RegistryConfig.getHibernatePerTransactionIsolationEnabled()) {
|
||||
return;
|
||||
}
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
});
|
||||
});
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
});
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
});
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(tm().getDefaultTransactionIsolationLevel());
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().assertTransactionIsolationLevel(
|
||||
tm().getDefaultTransactionIsolationLevel());
|
||||
},
|
||||
TRANSACTION_READ_COMMITTED);
|
||||
},
|
||||
TRANSACTION_READ_UNCOMMITTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void transact_hasNoEffectWithPartialSuccess() {
|
||||
assertPersonEmpty();
|
||||
@@ -606,19 +754,19 @@ class JpaTransactionManagerImplTest {
|
||||
verify(spyJpaTm, times(3)).delete(theEntityKey);
|
||||
}
|
||||
|
||||
private void insertPerson(int age) {
|
||||
private static void insertPerson(int age) {
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(String.format("INSERT INTO Person (age) VALUES (%d)", age))
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
private void insertCompany(String name) {
|
||||
private static void insertCompany(String name) {
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(String.format("INSERT INTO Company (name) VALUES ('%s')", name))
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
private void assertPersonExist(int age) {
|
||||
private static void assertPersonExist(int age) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
EntityManager em = tm().getEntityManager();
|
||||
@@ -631,7 +779,7 @@ class JpaTransactionManagerImplTest {
|
||||
});
|
||||
}
|
||||
|
||||
private void assertCompanyExist(String name) {
|
||||
private static void assertCompanyExist(String name) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
String maybeName =
|
||||
@@ -644,23 +792,23 @@ class JpaTransactionManagerImplTest {
|
||||
});
|
||||
}
|
||||
|
||||
private void assertPersonCount(int count) {
|
||||
private static void assertPersonCount(int count) {
|
||||
assertThat(countTable("Person")).isEqualTo(count);
|
||||
}
|
||||
|
||||
private void assertCompanyCount(int count) {
|
||||
private static void assertCompanyCount(int count) {
|
||||
assertThat(countTable("Company")).isEqualTo(count);
|
||||
}
|
||||
|
||||
private void assertPersonEmpty() {
|
||||
private static void assertPersonEmpty() {
|
||||
assertPersonCount(0);
|
||||
}
|
||||
|
||||
private void assertCompanyEmpty() {
|
||||
private static void assertCompanyEmpty() {
|
||||
assertCompanyCount(0);
|
||||
}
|
||||
|
||||
private int countTable(String tableName) {
|
||||
private static int countTable(String tableName) {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
BigInteger colCount =
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -49,6 +50,21 @@ public class ReplicaSimulatingJpaTransactionManager implements JpaTransactionMan
|
||||
delegate.teardown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getDefaultTransactionIsolationLevel() {
|
||||
return delegate.getDefaultTransactionIsolationLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getCurrentTransactionIsolationLevel() {
|
||||
return delegate.getCurrentTransactionIsolationLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertTransactionIsolationLevel(TransactionIsolationLevel expectedLevel) {
|
||||
delegate.assertTransactionIsolationLevel(expectedLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getStandaloneEntityManager() {
|
||||
return delegate.getStandaloneEntityManager();
|
||||
@@ -85,7 +101,7 @@ public class ReplicaSimulatingJpaTransactionManager implements JpaTransactionMan
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Supplier<T> work) {
|
||||
public <T> T transact(Supplier<T> work, TransactionIsolationLevel isolationLevel) {
|
||||
if (delegate.inTransaction()) {
|
||||
return work.get();
|
||||
}
|
||||
@@ -96,26 +112,48 @@ public class ReplicaSimulatingJpaTransactionManager implements JpaTransactionMan
|
||||
.createNativeQuery("SET TRANSACTION READ ONLY")
|
||||
.executeUpdate();
|
||||
return work.get();
|
||||
});
|
||||
},
|
||||
isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Supplier<T> work) {
|
||||
return transact(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(Supplier<T> work, TransactionIsolationLevel isolationLevel) {
|
||||
return transact(work, isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(Supplier<T> work) {
|
||||
return transact(work);
|
||||
return transactNoRetry(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(Runnable work) {
|
||||
public void transact(Runnable work, TransactionIsolationLevel isolationLevel) {
|
||||
transact(
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
},
|
||||
isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(Runnable work) {
|
||||
transact(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactNoRetry(Runnable work, TransactionIsolationLevel isolationLevel) {
|
||||
transact(work, isolationLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactNoRetry(Runnable work) {
|
||||
transact(work);
|
||||
transactNoRetry(work, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,9 +16,9 @@ package google.registry.reporting.billing;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -101,7 +101,7 @@ class PublishInvoicesActionTest {
|
||||
void testJobIndeterminate_returnsRetriableResponse() {
|
||||
expectedJob.setCurrentState("JOB_STATE_RUNNING");
|
||||
uploadAction.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NOT_MODIFIED);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.bigquery.BigqueryJobFailureException;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
@@ -34,7 +35,6 @@ import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Optional;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -65,7 +65,7 @@ class IcannReportingStagingActionTest {
|
||||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
action.sender = new InternetAddress("sender@example.com");
|
||||
action.recipient = new InternetAddress("recipient@example.com");
|
||||
action.emailService = mock(SendEmailService.class);
|
||||
action.gmailClient = mock(GmailClient.class);
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
|
||||
when(stager.stageReports(yearMonth, subdir, ReportType.ACTIVITY))
|
||||
@@ -89,7 +89,7 @@ class IcannReportingStagingActionTest {
|
||||
action.run();
|
||||
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b"));
|
||||
verify(action.emailService)
|
||||
verify(action.gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
@@ -105,7 +105,7 @@ class IcannReportingStagingActionTest {
|
||||
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||
verify(stager).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
||||
verify(action.emailService)
|
||||
verify(action.gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
@@ -124,7 +124,7 @@ class IcannReportingStagingActionTest {
|
||||
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||
verify(stager, times(2)).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
|
||||
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
|
||||
verify(action.emailService)
|
||||
verify(action.gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
@@ -147,7 +147,7 @@ class IcannReportingStagingActionTest {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("BigqueryJobFailureException: Expected failure");
|
||||
verify(stager, times(3)).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
|
||||
verify(action.emailService)
|
||||
verify(action.gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report staging summary [FAILURE]",
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.cloud.storage.BlobId;
|
||||
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Tld;
|
||||
@@ -43,7 +44,6 @@ import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -63,7 +63,7 @@ class IcannReportingUploadActionTest {
|
||||
private static final byte[] PAYLOAD_SUCCESS = "test,csv\n13,37".getBytes(UTF_8);
|
||||
private static final byte[] PAYLOAD_FAIL = "ahah,csv\n12,34".getBytes(UTF_8);
|
||||
private final IcannHttpReporter mockReporter = mock(IcannHttpReporter.class);
|
||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||
private final GmailClient gmailClient = mock(GmailClient.class);
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
private final TestLogHandler logHandler = new TestLogHandler();
|
||||
@@ -77,7 +77,7 @@ class IcannReportingUploadActionTest {
|
||||
action.gcsUtils = gcsUtils;
|
||||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
action.reportingBucket = "basin";
|
||||
action.emailService = emailService;
|
||||
action.gmailClient = gmailClient;
|
||||
action.sender = new InternetAddress("sender@example.com");
|
||||
action.recipient = new InternetAddress("recipient@example.com");
|
||||
action.response = response;
|
||||
@@ -127,7 +127,7 @@ class IcannReportingUploadActionTest {
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
|
||||
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
verify(emailService)
|
||||
verify(gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report upload summary: 3/4 succeeded",
|
||||
@@ -162,7 +162,7 @@ class IcannReportingUploadActionTest {
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200512.csv");
|
||||
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
verify(emailService)
|
||||
verify(gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report upload summary: 2/2 succeeded",
|
||||
@@ -191,7 +191,7 @@ class IcannReportingUploadActionTest {
|
||||
IcannReportingUploadAction action = createAction();
|
||||
action.run();
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
verifyNoMoreInteractions(emailService);
|
||||
verifyNoMoreInteractions(gmailClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,7 +206,7 @@ class IcannReportingUploadActionTest {
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
|
||||
verify(mockReporter, times(2)).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
verify(emailService)
|
||||
verify(gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report upload summary: 3/4 succeeded",
|
||||
@@ -266,7 +266,7 @@ class IcannReportingUploadActionTest {
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "foo-transactions-200606.csv");
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
verify(emailService)
|
||||
verify(gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report upload summary: 3/4 succeeded",
|
||||
@@ -336,7 +336,7 @@ class IcannReportingUploadActionTest {
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200606.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
|
||||
verify(emailService)
|
||||
verify(gmailClient)
|
||||
.sendEmail(
|
||||
EmailMessage.create(
|
||||
"ICANN Monthly report upload summary: 3/4 succeeded",
|
||||
|
||||
@@ -93,8 +93,7 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
|
||||
.scheduleTime(
|
||||
clock
|
||||
.nowUtc()
|
||||
// TODO(b/296582836): mitigating retry problem. Remove `+10` when bug is fixed.
|
||||
.plus(Duration.standardMinutes(ReportingModule.ENQUEUE_DELAY_MINUTES + 10))));
|
||||
.plus(Duration.standardMinutes(ReportingModule.ENQUEUE_DELAY_MINUTES))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,9 +19,9 @@ import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParse
|
||||
import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.getMatchB;
|
||||
import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
@@ -134,7 +134,7 @@ class PublishSpec11ReportActionTest {
|
||||
void testJobIndeterminate_returnsRetriableResponse() {
|
||||
expectedJob.setCurrentState("JOB_STATE_RUNNING");
|
||||
publishAction.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NOT_MODIFIED);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_SERVICE_UNAVAILABLE);
|
||||
verifyNoMoreInteractions(emailUtils);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import google.registry.model.console.UserTest;
|
||||
import google.registry.model.contact.ContactTest;
|
||||
import google.registry.model.domain.DomainSqlTest;
|
||||
import google.registry.model.domain.token.AllocationTokenTest;
|
||||
import google.registry.model.domain.token.PackagePromotionTest;
|
||||
import google.registry.model.domain.token.BulkPricingPackageTest;
|
||||
import google.registry.model.history.ContactHistoryTest;
|
||||
import google.registry.model.history.DomainHistoryTest;
|
||||
import google.registry.model.history.HostHistoryTest;
|
||||
@@ -82,6 +82,7 @@ import org.junit.runner.RunWith;
|
||||
BeforeSuiteTest.class,
|
||||
AllocationTokenTest.class,
|
||||
BillingBaseTest.class,
|
||||
BulkPricingPackageTest.class,
|
||||
ClaimsListDaoTest.class,
|
||||
ContactHistoryTest.class,
|
||||
ContactTest.class,
|
||||
@@ -91,7 +92,6 @@ import org.junit.runner.RunWith;
|
||||
DomainHistoryTest.class,
|
||||
HostHistoryTest.class,
|
||||
LockTest.class,
|
||||
PackagePromotionTest.class,
|
||||
PollMessageTest.class,
|
||||
PremiumListDaoTest.class,
|
||||
RdeRevisionTest.class,
|
||||
|
||||
@@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
@@ -32,16 +32,16 @@ import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Unit tests for {@link google.registry.tools.CreatePackagePromotionCommand}. */
|
||||
public class CreatePackagePromotionCommandTest
|
||||
extends CommandTestCase<CreatePackagePromotionCommand> {
|
||||
/** Unit tests for {@link CreateBulkPricingPackageCommand}. */
|
||||
public class CreateBulkPricingPackageCommandTest
|
||||
extends CommandTestCase<CreateBulkPricingPackageCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -55,20 +55,20 @@ public class CreatePackagePromotionCommandTest
|
||||
"--next_billing_date=2012-03-17",
|
||||
"abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
assertThat(packagePromotion.getMaxDomains()).isEqualTo(100);
|
||||
assertThat(packagePromotion.getMaxCreates()).isEqualTo(500);
|
||||
assertThat(packagePromotion.getPackagePrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(100);
|
||||
assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(500);
|
||||
assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2012-03-17T00:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_tokenIsNotPackageType() throws Exception {
|
||||
void testFailure_tokenIsNotBulkType() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
@@ -90,7 +90,7 @@ public class CreatePackagePromotionCommandTest
|
||||
"--next_billing_date=2012-03-17T05:00:00Z",
|
||||
"abc123"));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo("The allocation token must be of the PACKAGE token type");
|
||||
.isEqualTo("The allocation token must be of the BULK_PRICING token type");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,16 +107,16 @@ public class CreatePackagePromotionCommandTest
|
||||
"abc123"));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo(
|
||||
"An allocation token with the token String abc123 does not exist. The package token"
|
||||
+ " must be created first before it can be used to create a PackagePromotion");
|
||||
"An allocation token with the token String abc123 does not exist. The bulk token"
|
||||
+ " must be created first before it can be used to create a BulkPricingPackage");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_packagePromotionAlreadyExists() throws Exception {
|
||||
void testFailure_bulkPricingPackageAlreadyExists() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -129,9 +129,9 @@ public class CreatePackagePromotionCommandTest
|
||||
"--price=USD 1000.00",
|
||||
"--next_billing_date=2012-03-17T05:00:00Z",
|
||||
"abc123");
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -142,7 +142,8 @@ public class CreatePackagePromotionCommandTest
|
||||
"--price=USD 1000.00",
|
||||
"--next_billing_date=2012-03-17T05:00:00Z",
|
||||
"abc123"));
|
||||
assertThat(thrown.getMessage()).isEqualTo("PackagePromotion with token abc123 already exists");
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo("BulkPricingPackage with token abc123 already exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,7 +151,7 @@ public class CreatePackagePromotionCommandTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -158,16 +159,16 @@ public class CreatePackagePromotionCommandTest
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
runCommandForced("--price=USD 1000.00", "--next_billing_date=2012-03-17", "abc123");
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
assertThat(packagePromotion.getMaxDomains()).isEqualTo(0);
|
||||
assertThat(packagePromotion.getMaxCreates()).isEqualTo(0);
|
||||
assertThat(packagePromotion.getPackagePrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(0);
|
||||
assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(0);
|
||||
assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2012-03-17T00:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -175,7 +176,7 @@ public class CreatePackagePromotionCommandTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -184,15 +185,15 @@ public class CreatePackagePromotionCommandTest
|
||||
.build());
|
||||
runCommandForced("--max_domains=100", "--max_creates=500", "--price=USD 1000.00", "abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
assertThat(packagePromotion.getMaxDomains()).isEqualTo(100);
|
||||
assertThat(packagePromotion.getMaxCreates()).isEqualTo(500);
|
||||
assertThat(packagePromotion.getPackagePrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(packagePromotion.getNextBillingDate()).isEqualTo(END_OF_TIME);
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(100);
|
||||
assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(500);
|
||||
assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
assertThat(bulkPricingPackage.getNextBillingDate()).isEqualTo(END_OF_TIME);
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -200,7 +201,7 @@ public class CreatePackagePromotionCommandTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -217,6 +218,6 @@ public class CreatePackagePromotionCommandTest
|
||||
"--next_billing_date=2012-03-17T05:00:00Z",
|
||||
"abc123"));
|
||||
assertThat(thrown.getMessage())
|
||||
.isEqualTo("PackagePrice is required when creating a new package");
|
||||
.isEqualTo("BulkPrice is required when creating a new bulk pricing package");
|
||||
}
|
||||
}
|
||||
@@ -379,8 +379,8 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Invalid value for -t parameter. Allowed values:[DEFAULT_PROMO, PACKAGE, SINGLE_USE,"
|
||||
+ " UNLIMITED_USE]");
|
||||
"Invalid value for -t parameter. Allowed values:[BULK_PRICING, DEFAULT_PROMO, PACKAGE,"
|
||||
+ " SINGLE_USE, UNLIMITED_USE]");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -399,7 +399,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidPackageTokenStatusTransition() {
|
||||
void testFailure_invalidBulkTokenStatusTransition() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -408,14 +408,14 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
|
||||
"--number",
|
||||
"999",
|
||||
"--type",
|
||||
"PACKAGE",
|
||||
"BULK_PRICING",
|
||||
String.format(
|
||||
"--token_status_transitions=\"%s=NOT_STARTED,%s=VALID,%s=ENDED\"",
|
||||
START_OF_TIME, fakeClock.nowUtc(), fakeClock.nowUtc().plusDays(1)))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"PACKAGE tokens should not be generated with ENDED or CANCELLED in their transition"
|
||||
+ " map");
|
||||
"BULK_PRICING tokens should not be generated with ENDED or CANCELLED in their"
|
||||
+ " transition map");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -24,15 +24,16 @@ import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link GetPackagePromotionCommand}. */
|
||||
public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePromotionCommand> {
|
||||
/** Unit tests for {@link GetBulkPricingPackageCommand}. */
|
||||
public class GetBulkPricingPackageCommandTest
|
||||
extends CommandTestCase<GetBulkPricingPackageCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
@@ -45,33 +46,33 @@ public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePr
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setMaxDomains(100)
|
||||
.setMaxCreates(500)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
runCommand("abc123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccessMultiplePackages() throws Exception {
|
||||
void testSuccessMultipleBulkPricingPackages() throws Exception {
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -81,11 +82,11 @@ public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePr
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
new PackagePromotion.Builder()
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setMaxDomains(100)
|
||||
.setMaxCreates(500)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build()));
|
||||
@@ -93,7 +94,7 @@ public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePr
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("123abc")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -103,11 +104,11 @@ public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePr
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().put(
|
||||
new PackagePromotion.Builder()
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token2)
|
||||
.setMaxDomains(1000)
|
||||
.setMaxCreates(700)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 3000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 3000))
|
||||
.setNextBillingDate(DateTime.parse("2014-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2013-11-12T05:00:00Z"))
|
||||
.build()));
|
||||
@@ -116,12 +117,12 @@ public class GetPackagePromotionCommandTest extends CommandTestCase<GetPackagePr
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_packageDoesNotExist() {
|
||||
void testFailure_bulkPricingPackageDoesNotExist() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> runCommand("fakeToken"));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("PackagePromotion with package token fakeToken does not exist");
|
||||
.isEqualTo("BulkPricingPackage with token fakeToken does not exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -22,7 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.CAN
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.PACKAGE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
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.testing.DatabaseHelper.createTld;
|
||||
@@ -344,13 +344,13 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateStatusTransitions_endPackageTokenNoDomains() throws Exception {
|
||||
void testUpdateStatusTransitions_endBulkTokenNoDomains() throws Exception {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setTokenStatusTransitions(
|
||||
@@ -371,13 +371,13 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateStatusTransitions_endPackageTokenWithActiveDomainsFails() throws Exception {
|
||||
void testUpdateStatusTransitions_endBulkTokenWithActiveDomainsFails() throws Exception {
|
||||
DateTime now = fakeClock.nowUtc();
|
||||
AllocationToken token =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("token")
|
||||
.setTokenType(PACKAGE)
|
||||
.setTokenType(BULK_PRICING)
|
||||
.setRenewalPriceBehavior(SPECIFIED)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setTokenStatusTransitions(
|
||||
@@ -390,7 +390,7 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
persistResource(
|
||||
persistActiveDomain("example.tld")
|
||||
.asBuilder()
|
||||
.setCurrentPackageToken(token.createVKey())
|
||||
.setCurrentBulkToken(token.createVKey())
|
||||
.build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -406,8 +406,8 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Package token token can not end its promotion because it still has 1 domains in the"
|
||||
+ " package");
|
||||
"Bulk token token can not end its promotion because it still has 1 domains in the"
|
||||
+ " promotion");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.google.common.truth.Truth;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.PackagePromotion;
|
||||
import google.registry.model.domain.token.BulkPricingPackage;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
@@ -32,9 +32,9 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Unit tests for {@link google.registry.tools.UpdatePackagePromotionCommand}. */
|
||||
public class UpdatePackagePromotionCommandTest
|
||||
extends CommandTestCase<UpdatePackagePromotionCommand> {
|
||||
/** Unit tests for {@link UpdateBulkPricingPackageCommand}. */
|
||||
public class UpdateBulkPricingPackageCommandTest
|
||||
extends CommandTestCase<UpdateBulkPricingPackageCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
@@ -42,23 +42,23 @@ public class UpdatePackagePromotionCommandTest
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("abc123")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
|
||||
.setDiscountFraction(1)
|
||||
.build());
|
||||
PackagePromotion packagePromotion =
|
||||
new PackagePromotion.Builder()
|
||||
BulkPricingPackage bulkPricingPackage =
|
||||
new BulkPricingPackage.Builder()
|
||||
.setToken(token)
|
||||
.setMaxDomains(100)
|
||||
.setMaxCreates(500)
|
||||
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setBulkPrice(Money.of(CurrencyUnit.USD, 1000))
|
||||
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
|
||||
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.build();
|
||||
tm().transact(() -> tm().put(packagePromotion));
|
||||
tm().transact(() -> tm().put(bulkPricingPackage));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,25 +71,24 @@ public class UpdatePackagePromotionCommandTest
|
||||
"--clear_last_notification_sent",
|
||||
"abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
Truth.assertThat(packagePromotion.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(packagePromotion.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(packagePromotion.getPackagePrice())
|
||||
.isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
Truth.assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2013-03-17T00:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_packageDoesNotExist() throws Exception {
|
||||
void testFailure_bulkPackageDoesNotExist() throws Exception {
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("nullPackage")
|
||||
.setTokenType(TokenType.PACKAGE)
|
||||
.setTokenType(TokenType.BULK_PRICING)
|
||||
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
|
||||
.setAllowedTlds(ImmutableSet.of("foo"))
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
@@ -107,7 +106,7 @@ public class UpdatePackagePromotionCommandTest
|
||||
"--next_billing_date=2012-03-17",
|
||||
"nullPackage"));
|
||||
Truth.assertThat(thrown.getMessage())
|
||||
.isEqualTo("PackagePromotion with token nullPackage does not exist");
|
||||
.isEqualTo("BulkPricingPackage with token nullPackage does not exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,17 +118,16 @@ public class UpdatePackagePromotionCommandTest
|
||||
"--clear_last_notification_sent",
|
||||
"abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
Truth.assertThat(packagePromotion.getMaxDomains()).isEqualTo(100);
|
||||
Truth.assertThat(packagePromotion.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(packagePromotion.getPackagePrice())
|
||||
.isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
Truth.assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(100);
|
||||
Truth.assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2013-03-17T00:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,17 +139,16 @@ public class UpdatePackagePromotionCommandTest
|
||||
"--clear_last_notification_sent",
|
||||
"abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
Truth.assertThat(packagePromotion.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(packagePromotion.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(packagePromotion.getPackagePrice())
|
||||
.isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
Truth.assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2012-11-12T05:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -163,34 +160,32 @@ public class UpdatePackagePromotionCommandTest
|
||||
"--clear_last_notification_sent",
|
||||
"abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
Truth.assertThat(packagePromotion.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(packagePromotion.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(packagePromotion.getPackagePrice())
|
||||
.isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
Truth.assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
Truth.assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 1000));
|
||||
Truth.assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2013-03-17T00:00:00Z"));
|
||||
assertThat(packagePromotion.getLastNotificationSent()).isEmpty();
|
||||
assertThat(bulkPricingPackage.getLastNotificationSent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_dontClearLastNotificationSent() throws Exception {
|
||||
runCommandForced("--max_domains=200", "--max_creates=1000", "--price=USD 2000.00", "abc123");
|
||||
|
||||
Optional<PackagePromotion> packagePromotionOptional =
|
||||
tm().transact(() -> PackagePromotion.loadByTokenString("abc123"));
|
||||
assertThat(packagePromotionOptional).isPresent();
|
||||
PackagePromotion packagePromotion = packagePromotionOptional.get();
|
||||
Truth.assertThat(packagePromotion.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(packagePromotion.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(packagePromotion.getPackagePrice())
|
||||
.isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(packagePromotion.getNextBillingDate())
|
||||
Optional<BulkPricingPackage> bulkPricingPackageOptional =
|
||||
tm().transact(() -> BulkPricingPackage.loadByTokenString("abc123"));
|
||||
assertThat(bulkPricingPackageOptional).isPresent();
|
||||
BulkPricingPackage bulkPricingPackage = bulkPricingPackageOptional.get();
|
||||
Truth.assertThat(bulkPricingPackage.getMaxDomains()).isEqualTo(200);
|
||||
Truth.assertThat(bulkPricingPackage.getMaxCreates()).isEqualTo(1000);
|
||||
Truth.assertThat(bulkPricingPackage.getBulkPrice()).isEqualTo(Money.of(CurrencyUnit.USD, 2000));
|
||||
Truth.assertThat(bulkPricingPackage.getNextBillingDate())
|
||||
.isEqualTo(DateTime.parse("2012-11-12T05:00:00Z"));
|
||||
Truth.assertThat(packagePromotion.getLastNotificationSent().get())
|
||||
Truth.assertThat(bulkPricingPackage.getLastNotificationSent().get())
|
||||
.isEqualTo(DateTime.parse("2010-11-12T05:00:00.000Z"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.ui.server.console.settings;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.registrar.RegistrarConsoleModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link WhoisRegistrarFieldsAction}. */
|
||||
public class WhoisRegistrarFieldsActionTest {
|
||||
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2023-08-01T00:00:00.000Z"));
|
||||
private final FakeResponse fakeResponse = new FakeResponse();
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
private final AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of("TheRegistrar", Role.OWNER, "NewRegistrar", Role.OWNER));
|
||||
|
||||
private final HashMap<String, Object> uiRegistrarMap =
|
||||
Maps.newHashMap(
|
||||
ImmutableMap.of(
|
||||
"registrarId",
|
||||
"TheRegistrar",
|
||||
"whoisServer",
|
||||
"whois.nic.google",
|
||||
"type",
|
||||
"REAL",
|
||||
"emailAddress",
|
||||
"the.registrar@example.com",
|
||||
"state",
|
||||
"ACTIVE",
|
||||
"url",
|
||||
"\"http://my.fake.url\"",
|
||||
"localizedAddress",
|
||||
"{\"street\": [\"123 Example Boulevard\"], \"city\": \"Williamsburg\", \"state\":"
|
||||
+ " \"NY\", \"zip\": \"11201\", \"countryCode\": \"US\"}"));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@Test
|
||||
void testSuccess_setsAllFields() throws Exception {
|
||||
Registrar oldRegistrar = Registrar.loadRequiredRegistrarCached("TheRegistrar");
|
||||
assertThat(oldRegistrar.getWhoisServer()).isEqualTo("whois.nic.fakewhois.example");
|
||||
assertThat(oldRegistrar.getUrl()).isEqualTo("http://my.fake.url");
|
||||
ImmutableMap<String, Object> addressMap =
|
||||
ImmutableMap.of(
|
||||
"street",
|
||||
ImmutableList.of("123 Fake St"),
|
||||
"city",
|
||||
"Fakeville",
|
||||
"state",
|
||||
"NL",
|
||||
"zip",
|
||||
"10011",
|
||||
"countryCode",
|
||||
"CA");
|
||||
uiRegistrarMap.putAll(
|
||||
ImmutableMap.of(
|
||||
"whoisServer",
|
||||
"whois.nic.google",
|
||||
"url",
|
||||
"\"https://newurl.example\"",
|
||||
"localizedAddress",
|
||||
"{\"street\": [\"123 Fake St\"], \"city\": \"Fakeville\", \"state\":"
|
||||
+ " \"NL\", \"zip\": \"10011\", \"countryCode\": \"CA\"}"));
|
||||
WhoisRegistrarFieldsAction action = createAction();
|
||||
action.run();
|
||||
assertThat(fakeResponse.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get(); // skip cache
|
||||
assertThat(newRegistrar.getWhoisServer()).isEqualTo("whois.nic.google");
|
||||
assertThat(newRegistrar.getUrl()).isEqualTo("https://newurl.example");
|
||||
assertThat(newRegistrar.getLocalizedAddress().toJsonMap()).isEqualTo(addressMap);
|
||||
// the non-changed fields should be the same
|
||||
assertAboutImmutableObjects()
|
||||
.that(newRegistrar)
|
||||
.isEqualExceptFields(oldRegistrar, "whoisServer", "url", "localizedAddress");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_noAccessToRegistrar() throws Exception {
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarIdCached("NewRegistrar").get();
|
||||
AuthResult onlyTheRegistrar =
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.example")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.build()));
|
||||
uiRegistrarMap.put("registrarId", "NewRegistrar");
|
||||
WhoisRegistrarFieldsAction action = createAction(onlyTheRegistrar);
|
||||
action.run();
|
||||
assertThat(fakeResponse.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
// should be no change
|
||||
assertThat(DatabaseHelper.loadByEntity(newRegistrar)).isEqualTo(newRegistrar);
|
||||
}
|
||||
|
||||
private AuthResult defaultUserAuth() {
|
||||
return AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.example")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build()));
|
||||
}
|
||||
|
||||
private WhoisRegistrarFieldsAction createAction() throws IOException {
|
||||
return createAction(defaultUserAuth());
|
||||
}
|
||||
|
||||
private WhoisRegistrarFieldsAction createAction(AuthResult authResult) throws IOException {
|
||||
when(request.getReader())
|
||||
.thenReturn(new BufferedReader(new StringReader(uiRegistrarMap.toString())));
|
||||
return new WhoisRegistrarFieldsAction(
|
||||
authResult,
|
||||
fakeResponse,
|
||||
GSON,
|
||||
registrarAccessor,
|
||||
RegistrarConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(request, GSON)));
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import com.google.appengine.api.users.User;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -54,7 +55,6 @@ import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import google.registry.util.StringGenerator.Alphabets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -97,7 +97,7 @@ final class RegistryLockPostActionTest {
|
||||
private Domain domain;
|
||||
private RegistryLockPostAction action;
|
||||
|
||||
@Mock SendEmailService emailService;
|
||||
@Mock GmailClient gmailClient;
|
||||
@Mock HttpServletRequest mockRequest;
|
||||
@Mock HttpServletResponse mockResponse;
|
||||
|
||||
@@ -510,12 +510,12 @@ final class RegistryLockPostActionTest {
|
||||
private void assertFailureWithMessage(Map<String, ?> response, String message) {
|
||||
assertThat(response)
|
||||
.containsExactly("status", "ERROR", "results", ImmutableList.of(), "message", message);
|
||||
verifyNoMoreInteractions(emailService);
|
||||
verifyNoMoreInteractions(gmailClient);
|
||||
}
|
||||
|
||||
private void verifyEmail(String recipient) throws Exception {
|
||||
ArgumentCaptor<EmailMessage> emailCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
verify(gmailClient).sendEmail(emailCaptor.capture());
|
||||
EmailMessage sentMessage = emailCaptor.getValue();
|
||||
assertThat(sentMessage.subject()).matches("Registry (un)?lock verification");
|
||||
assertThat(sentMessage.body()).matches(EMAIL_MESSAGE_TEMPLATE);
|
||||
@@ -545,7 +545,7 @@ final class RegistryLockPostActionTest {
|
||||
jsonActionRunner,
|
||||
authResult,
|
||||
registrarAccessor,
|
||||
emailService,
|
||||
gmailClient,
|
||||
domainLockUtils,
|
||||
outgoingAddress);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n API APP PUBLIC
|
||||
/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n API,LEGACY USER PUBLIC
|
||||
/registrar ConsoleUiAction GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-create ConsoleRegistrarCreatorAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-ote-setup ConsoleOteSetupAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC
|
||||
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC
|
||||
/registry-lock-post RegistryLockPostAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n API APP PUBLIC
|
||||
/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n API,LEGACY USER PUBLIC
|
||||
/registrar ConsoleUiAction GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-create ConsoleRegistrarCreatorAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-ote-setup ConsoleOteSetupAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC
|
||||
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC
|
||||
/registry-lock-post RegistryLockPostAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC
|
||||
@@ -498,12 +498,12 @@
|
||||
|
||||
create table "PackagePromotion" (
|
||||
package_promotion_id bigserial not null,
|
||||
package_price_amount numeric(19, 2) not null,
|
||||
package_price_currency text not null,
|
||||
last_notification_sent timestamptz,
|
||||
max_creates int4 not null,
|
||||
max_domains int4 not null,
|
||||
next_billing_date timestamptz not null,
|
||||
package_price_amount numeric(19, 2) not null,
|
||||
package_price_currency text not null,
|
||||
token text not null,
|
||||
primary key (package_promotion_id)
|
||||
);
|
||||
|
||||
@@ -311,7 +311,7 @@ An EPP flow that creates a new domain resource.
|
||||
* Registrar is not logged in.
|
||||
* The current registry phase allows registrations only with signed marks.
|
||||
* The current registry phase does not allow for general registrations.
|
||||
* Package domain registered for too many years.
|
||||
* Bulk pricing domain registered for too many years.
|
||||
* Signed marks are only allowed during sunrise.
|
||||
* An allocation token was provided that is invalid for premium domains.
|
||||
* 2003
|
||||
@@ -499,8 +499,8 @@ comes in at the exact millisecond that the domain would have expired.
|
||||
* Resource status prohibits this operation.
|
||||
* The allocation token is not currently valid.
|
||||
* 2305
|
||||
* The __REMOVEDOMAIN__ token is missing on a package domain command
|
||||
* The __REMOVEDOMAIN__ token is not allowed on non package domains
|
||||
* The __REMOVEDOMAIN__ token is missing on a bulk pricing domain command
|
||||
* The __REMOVEDOMAIN__ token is not allowed on non bulk pricing domains
|
||||
* The allocation token is not valid for this domain.
|
||||
* The allocation token is not valid for this registrar.
|
||||
* The allocation token is not valid for this TLD.
|
||||
|
||||
Reference in New Issue
Block a user