1
0
mirror of https://github.com/google/nomulus synced 2026-07-18 22:12:24 +00:00

Compare commits

...

20 Commits

Author SHA1 Message Date
Pavlo Tkach 775f672f2a Do not enqueue DNS updates when flow doesn't affect nameservers (#1785) 2022-09-16 16:59:04 -04:00
gbrodman 372c854268 Create a scrap command to cancel OneTime billing events by ID (#1790)
This allows us to correct situations where we have erroneously charged
registrars for an action, without explicitly issuing a refund.
2022-09-16 16:17:31 -04:00
Lai Jiang edbca15bf4 Remove generics from TransferData (#1787)
`TransferData` is currently a generic class with a complicated type parameter that designate the `Builder` class of its concrete subclass, on order to facilitate returning the said `Builder` from an instance loosely typed to the superclass (`TransferData`) itself.

While this works, in most all places that a `TransferData` is used, the raw, un-generic type is declared, resulting a lot of warnings, not to mention the fact that type safety not actually checked when raw type is used.

In this PR, we make it so that the concrete `Builder` is returned through a protected abstract method that is implemented by the subclasses. The type information therefore no longer needs to be embedded in the superclass type signature, and reflection is not necessary to create the `Builder` either. Overall, it makes `TransferData` a much cleaner class without the messiness of generics.
2022-09-15 14:07:38 -04:00
sarahcaseybot 5f41adf843 Flyway file for autogenerated PackagePromotion id fix (#1789)
* Flyway file for autogenerated PackagePromotion id fix

* Actually include the flyway file
2022-09-15 13:28:46 -04:00
Lai Jiang e21f64b745 Delete EppResourceIndex and EppResourceIndexBucket (#1774) 2022-09-15 10:50:22 -04:00
sarahcaseybot 0dee97934a Prevent creation of package domains for more than 1 year (#1786)
* Prevent creation of package domains for more than 1 year

* Fix docs test
2022-09-14 14:49:56 -04:00
gbrodman 1070173264 Load, project, and save in one txn in ResaveAERP (#1780) 2022-09-13 15:59:49 -04:00
Pavlo Tkach b9a3c0cd96 Add dry run test for remove package token (#1782) 2022-09-13 11:20:53 -04:00
sarahcaseybot 120456d138 Increase dns update failure max retry count (#1781) 2022-09-12 16:17:31 -04:00
gbrodman 66736d52f0 Add a cookie-based OAuth2 authenticator (#1761)
This uses the GoogleIdTokenVerifier to verify ID tokens passed in
(presumably from a front end) via cookies. This isn't used anywhere yet
but it will be used for front-end API calls for the new console.
2022-09-12 15:03:05 -04:00
Lai Jiang b159541278 Remove ofy support from ServerSecret (#1773) 2022-09-09 10:38:12 -04:00
Lai Jiang 335b229ce8 Remove ofy support from TransferData (#1775)
Also makes some changes to eliminate the use of raw types.
2022-09-08 19:25:41 -04:00
Lai Jiang 8ee0a85531 Remove ofy embedded classes (#1778) 2022-09-08 16:12:57 -04:00
gbrodman 5cbc307cd1 Add a DAO for User objects and fix up the User DB object (#1765)
First, we create a sequence of User IDs in Postgres and assign it to the
User ID field, meaning that Hibernate can autogenerate IDs.

Next, add an update timestamp.

Next, add a constraint that we can't have multiple Users with the same
email address.

Finally, create a DAO since we'll usually want to query by that email
address (at least for now).
2022-09-08 15:21:56 -04:00
Lai Jiang bd37541b49 Remove ofy support from ForeignKeyIndex (#1777)
FKI used to be persisted in datastore to help speed up loading by foreign key.
Now it is just a helper class to do the same thing in SQL because
indexing is natively supported in SQL.
2022-09-08 13:12:02 -04:00
Lai Jiang 312bc143d5 Delete EntityGroupRoot (#1776) 2022-09-08 12:54:10 -04:00
Lai Jiang 49ade014ab Remove ofy from Lock (#1771)
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1771)
<!-- Reviewable:end -->
2022-09-07 17:32:03 -04:00
Lai Jiang b8d901effe Remove ofy support from registrar (#1762)
Also fixes some warnings about the use of raw types.
2022-09-07 14:24:42 -04:00
Lai Jiang 23520048dc Remove ofy support from AllocationToken (#1770) 2022-09-07 14:22:42 -04:00
Lai Jiang 37ed6c925c Remove ofy support from RdeRevision (#1772) 2022-09-07 13:30:38 -04:00
119 changed files with 2163 additions and 2950 deletions
-6
View File
@@ -61,12 +61,6 @@ by Joshua Bloch in his book Effective Java -->
<property name="message" value="Use assertThrows and expectThrows from JUnitBackports instead of the deprecated methods on ExpectedException."/>
</module>
<!-- Checks that the deprecated MockitoJUnitRunner is not used. -->
<module name="RegexpSingleline">
<property name="format" value="MockitoJUnitRunner"/>
<property name="message" value="MockitoJUnitRunner is deprecated. Use @RunWith(JUnit4.class) and MockitoRule instead."/>
</module>
<module name="LineLength">
<!-- Checks if a line is too long. -->
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="100"/>
@@ -44,7 +44,7 @@ import javax.inject.Inject;
/**
* Hard deletes load-test Contacts, Hosts, their subordinate history entries, and the associated
* ForeignKey and EppResourceIndex entities.
* ForeignKey entities.
*
* <p>This only deletes contacts and hosts, NOT domains. To delete domains, use {@link
* DeleteProberDataAction} and pass it the TLD(s) that the load test domains were created on. Note
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.batch.BatchModule.PARAM_DRY_RUN;
import static google.registry.config.RegistryEnvironment.PRODUCTION;
import static google.registry.model.ResourceTransferUtils.updateForeignKeyIndexDeletionTime;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
import static google.registry.model.tld.Registries.getTldsOfType;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
@@ -54,7 +53,7 @@ import org.joda.time.Duration;
/**
* Deletes all prober {@link Domain}s and their subordinate history entries, poll messages, and
* billing events, along with their ForeignKeyDomainIndex and EppResourceIndex entities.
* billing events, along with their ForeignKeyDomainIndex entities.
*/
@Action(
service = Action.Service.BACKEND,
@@ -267,8 +266,6 @@ public class DeleteProberDataAction implements Runnable {
// messages, or auto-renews because those will all be hard-deleted the next time the job runs
// anyway.
tm().putAll(ImmutableList.of(deletedDomain, historyEntry));
// updating foreign keys is a no-op in SQL
updateForeignKeyIndexDeletionTime(deletedDomain);
dnsQueue.addDomainRefreshTask(deletedDomain.getDomainName());
}
}
@@ -14,11 +14,14 @@
package google.registry.beam.resave;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import google.registry.beam.common.RegistryJpaIO;
import google.registry.beam.common.RegistryJpaIO.Read;
import google.registry.model.EppResource;
@@ -27,7 +30,7 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.Host;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.persistence.VKey;
import google.registry.util.DateTimeUtils;
import java.io.Serializable;
import java.util.concurrent.ThreadLocalRandom;
@@ -69,7 +72,7 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
* multiple times, and to avoid projecting and resaving the same domain multiple times.
*/
private static final String DOMAINS_TO_PROJECT_QUERY =
"FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
"SELECT repoId FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
+ " d.transferData.pendingTransferExpirationTime < current_timestamp()) OR"
+ " (d.registrationExpirationTime < current_timestamp() AND d.deletionTime ="
+ " (:END_OF_TIME)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
@@ -99,13 +102,13 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
/** Projects to the current time and saves any contacts with expired transfers. */
private void fastResaveContacts(Pipeline pipeline) {
Read<Contact, Contact> read =
Read<String, String> repoIdRead =
RegistryJpaIO.read(
"FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
"SELECT repoId FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
+ " transferData.pendingTransferExpirationTime < current_timestamp()",
Contact.class,
c -> c);
projectAndResaveResources(pipeline, Contact.class, read);
String.class,
r -> r);
projectAndResaveResources(pipeline, Contact.class, repoIdRead);
}
/**
@@ -116,61 +119,72 @@ public class ResaveAllEppResourcesPipeline implements Serializable {
* DomainBase#cloneProjectedAtTime(DateTime)}.
*/
private void fastResaveDomains(Pipeline pipeline) {
Read<Domain, Domain> read =
Read<String, String> repoIdRead =
RegistryJpaIO.read(
DOMAINS_TO_PROJECT_QUERY,
ImmutableMap.of("END_OF_TIME", DateTimeUtils.END_OF_TIME),
Domain.class,
d -> d);
projectAndResaveResources(pipeline, Domain.class, read);
String.class,
r -> r);
projectAndResaveResources(pipeline, Domain.class, repoIdRead);
}
/** Projects all resources to the current time and saves them. */
private <T extends EppResource> void forceResaveAllResources(Pipeline pipeline, Class<T> clazz) {
Read<T, T> read = RegistryJpaIO.read(() -> CriteriaQueryBuilder.create(clazz).build());
projectAndResaveResources(pipeline, clazz, read);
Read<String, String> repoIdRead =
RegistryJpaIO.read(
// Note: cannot use SQL parameters for the table name
String.format("SELECT repoId FROM %s", clazz.getSimpleName()), String.class, r -> r);
projectAndResaveResources(pipeline, clazz, repoIdRead);
}
/** Projects and re-saves the result of the provided {@link Read}. */
/** Projects and re-saves all resources with repo IDs provided by the {@link Read}. */
private <T extends EppResource> void projectAndResaveResources(
Pipeline pipeline, Class<T> clazz, Read<?, T> read) {
Pipeline pipeline, Class<T> clazz, Read<?, String> repoIdRead) {
int numShards = options.getSqlWriteShards();
int batchSize = options.getSqlWriteBatchSize();
String className = clazz.getSimpleName();
pipeline
.apply("Read " + className, read)
.apply("Read " + className, repoIdRead)
.apply(
"Shard data for class" + className,
WithKeys.<Integer, T>of(e -> ThreadLocalRandom.current().nextInt(numShards))
WithKeys.<Integer, String>of(e -> ThreadLocalRandom.current().nextInt(numShards))
.withKeyType(integers()))
.apply(
"Group into batches for class" + className,
GroupIntoBatches.<Integer, T>ofSize(batchSize).withShardedKey())
.apply("Map " + className + " to now", ParDo.of(new BatchedProjectionFunction<>()))
GroupIntoBatches.<Integer, String>ofSize(batchSize).withShardedKey())
.apply(
"Write transformed " + className,
RegistryJpaIO.<EppResource>write()
.withName("Write transformed " + className)
.withBatchSize(batchSize)
.withShards(numShards));
"Load, map, and save " + className,
ParDo.of(new BatchedLoadProjectAndSaveFunction(clazz)));
}
private static class BatchedProjectionFunction<T extends EppResource>
extends DoFn<KV<ShardedKey<Integer>, Iterable<T>>, EppResource> {
/** Function that loads, projects, and saves resources all in the same transaction. */
private static class BatchedLoadProjectAndSaveFunction
extends DoFn<KV<ShardedKey<Integer>, Iterable<String>>, Void> {
private final Class<? extends EppResource> clazz;
private BatchedLoadProjectAndSaveFunction(Class<? extends EppResource> clazz) {
this.clazz = clazz;
}
@ProcessElement
public void processElement(
@Element KV<ShardedKey<Integer>, Iterable<T>> element,
OutputReceiver<EppResource> outputReceiver) {
@Element KV<ShardedKey<Integer>, Iterable<String>> element,
OutputReceiver<Void> outputReceiver) {
jpaTm()
.transact(
() ->
element
.getValue()
.forEach(
resource ->
outputReceiver.output(
resource.cloneProjectedAtTime(jpaTm().getTransactionTime()))));
() -> {
DateTime now = jpaTm().getTransactionTime();
ImmutableList<VKey<? extends EppResource>> keys =
Streams.stream(element.getValue())
.map(repoId -> VKey.create(clazz, repoId))
.collect(toImmutableList());
ImmutableList<EppResource> mappedResources =
jpaTm().loadByKeys(keys).values().stream()
.map(r -> r.cloneProjectedAtTime(now))
.collect(toImmutableList());
jpaTm().putAll(mappedResources);
});
}
}
@@ -1482,11 +1482,6 @@ public final class RegistryConfig {
return CONFIG_SETTINGS.get().registryPolicy.defaultRegistrarWhoisServer;
}
/** Returns the number of {@code EppResourceIndex} buckets to be used. */
public static int getEppResourceIndexBucketCount() {
return CONFIG_SETTINGS.get().datastore.eppResourceIndexBucketsNum;
}
/** Returns the base retry duration that gets doubled after each failure within {@code Ofy}. */
public static Duration getBaseOfyRetryDuration() {
return Duration.millis(CONFIG_SETTINGS.get().datastore.baseOfyRetryMillis);
@@ -108,7 +108,6 @@ public class RegistryConfigSettings {
/** Configuration for Cloud Datastore. */
public static class Datastore {
public int eppResourceIndexBucketsNum;
public int baseOfyRetryMillis;
}
@@ -183,10 +183,6 @@ registryPolicy:
requireSslCertificates: true
datastore:
# Number of EPP resource index buckets in Datastore. Dont change after
# initial install.
eppResourceIndexBucketsNum: 997
# Milliseconds that Objectify waits to retry a Datastore transaction (this
# doubles after each failure).
baseOfyRetryMillis: 100
@@ -81,7 +81,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
// tasks.
public static final String APP_ENGINE_RETRY_HEADER = "X-AppEngine-TaskRetryCount";
public static final String CLOUD_TASKS_RETRY_HEADER = "X-CloudTasks-TaskRetryCount";
public static final int RETRIES_BEFORE_PERMANENT_FAILURE = 10;
public static final int RETRIES_BEFORE_PERMANENT_FAILURE = 20;
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -23,7 +23,6 @@ import static google.registry.model.IdService.allocateId;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException;
import google.registry.flows.ExtensionManager;
@@ -40,8 +39,6 @@ import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.ContactCreateData;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import javax.inject.Inject;
@@ -96,12 +93,7 @@ public final class ContactCreateFlow implements TransactionalFlow {
.setType(HistoryEntry.Type.CONTACT_CREATE)
.setXmlBytes(null) // We don't want to store contact details in the history entry.
.setContact(newContact);
tm().insertAll(
ImmutableSet.of(
newContact,
historyBuilder.build(),
ForeignKeyIndex.create(newContact, newContact.getDeletionTime()),
EppResourceIndex.create(Key.create(newContact))));
tm().insertAll(ImmutableSet.of(newContact, historyBuilder.build()));
return responseBuilder
.setResData(ContactCreateData.create(newContact.getContactId(), now))
.build();
@@ -59,7 +59,6 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName;
import com.googlecode.objectify.Key;
import google.registry.dns.DnsQueue;
import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException;
@@ -105,8 +104,6 @@ import google.registry.model.eppinput.EppInput;
import google.registry.model.eppinput.ResourceCommand;
import google.registry.model.eppoutput.CreateData.DomainCreateData;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew;
@@ -154,6 +151,7 @@ import org.joda.time.Duration;
* @error {@link DomainCreateFlow.MustHaveSignedMarksInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
* @error {@link DomainCreateFlow.PackageDomainRegisteredForTooManyYearsException}
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
@@ -392,6 +390,9 @@ public final class DomainCreateFlow implements TransactionalFlow {
.build();
if (allocationToken.isPresent()
&& allocationToken.get().getTokenType().equals(TokenType.PACKAGE)) {
if (years > 1) {
throw new PackageDomainRegisteredForTooManyYearsException(allocationToken.get().getToken());
}
domain =
domain.asBuilder().setCurrentPackageToken(allocationToken.get().createVKey()).build();
}
@@ -401,11 +402,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
entitiesToSave.add(
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
}
entitiesToSave.add(
domain,
domainHistory,
ForeignKeyIndex.create(domain, domain.getDeletionTime()),
EppResourceIndex.create(Key.create(domain)));
entitiesToSave.add(domain, domainHistory);
if (allocationToken.isPresent()
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
entitiesToSave.add(
@@ -757,4 +754,14 @@ public final class DomainCreateFlow implements TransactionalFlow {
ANCHOR_TENANT_CREATE_VALID_YEARS, invalidYears));
}
}
/** Package domain registered for too many years. */
static class PackageDomainRegisteredForTooManyYearsException extends CommandUseErrorException {
public PackageDomainRegisteredForTooManyYearsException(String token) {
super(
String.format(
"The package token %s cannot be used to register names for longer than 1 year.",
token));
}
}
}
@@ -29,7 +29,6 @@ import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurr
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.ResourceTransferUtils.denyPendingTransfer;
import static google.registry.model.ResourceTransferUtils.handlePendingTransferOnDelete;
import static google.registry.model.ResourceTransferUtils.updateForeignKeyIndexDeletionTime;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.ADD_FIELDS;
@@ -257,7 +256,6 @@ public final class DomainDeleteFlow implements TransactionalFlow {
Domain newDomain = builder.build();
DomainHistory domainHistory =
buildDomainHistory(newDomain, registry, now, durationUntilDelete, inAddGracePeriod);
updateForeignKeyIndexDeletionTime(newDomain);
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
// Close the autorenew billing event and poll message. This may delete the poll message. Store
// the updated recurring billing event, we'll need it later and can't reload it.
@@ -27,7 +27,6 @@ import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotReserved;
import static google.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
import static google.registry.flows.domain.DomainFlowUtils.verifyRegistrarIsActive;
import static google.registry.model.ResourceTransferUtils.updateForeignKeyIndexDeletionTime;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RESTORE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
@@ -188,7 +187,6 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
autorenewPollMessage,
now,
registrarId);
updateForeignKeyIndexDeletionTime(newDomain);
DomainHistory domainHistory = buildDomainHistory(newDomain, now);
entitiesToSave.add(newDomain, domainHistory, autorenewEvent, autorenewPollMessage);
tm().putAll(entitiesToSave.build());
@@ -274,7 +274,7 @@ public final class DomainTransferUtils {
* renewal, we must issue a cancellation for the autorenew, so that the losing registrar will not
* be charged (essentially, the gaining registrar takes on the cost of the year of registration
* that the autorenew just added). But, if the superuser extension is used to request a transfer
* without an additional year then the gaining registrar is not charged for the one year renewal
* without an additional year then the gaining registrar is not charged for the one-year renewal
* and the losing registrar still needs to be charged for the auto-renew.
*
* <p>For details on the policy justification, see b/19430703#comment17 and <a
@@ -181,7 +181,10 @@ public final class DomainUpdateFlow implements TransactionalFlow {
DomainHistory domainHistory =
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
validateNewState(newDomain);
dnsQueue.addDomainRefreshTask(targetId);
if (newDomain.getDsData() != existingDomain.getDsData()
|| newDomain.getNsHosts() != existingDomain.getNsHosts()) {
dnsQueue.addDomainRefreshTask(targetId);
}
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
entitiesToSave.add(newDomain, domainHistory);
Optional<BillingEvent.OneTime> statusUpdateBillingEvent =
@@ -268,12 +271,18 @@ public final class DomainUpdateFlow implements TransactionalFlow {
.setLastEppUpdateRegistrarId(registrarId)
.addStatusValues(add.getStatusValues())
.removeStatusValues(remove.getStatusValues())
.addNameservers(add.getNameservers().stream().collect(toImmutableSet()))
.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()))
.removeContacts(remove.getContacts())
.addContacts(add.getContacts())
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
if (!add.getNameservers().isEmpty()) {
domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet()));
}
if (!remove.getNameservers().isEmpty()) {
domainBuilder.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet()));
}
Optional<DomainUpdateSuperuserExtension> superuserExt =
eppInput.getSingleExtension(DomainUpdateSuperuserExtension.class);
if (superuserExt.isPresent()) {
@@ -152,7 +152,7 @@ public class AllocationTokenFlowUtils {
}
maybeTokenEntity =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKeyIfPresent(VKey.createSql(AllocationToken.class, token)));
if (!maybeTokenEntity.isPresent()) {
throw new InvalidAllocationTokenException();
@@ -27,7 +27,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig.Config;
import google.registry.dns.DnsQueue;
import google.registry.flows.EppException;
@@ -49,8 +48,6 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.host.Host;
import google.registry.model.host.HostCommand.Create;
import google.registry.model.host.HostHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import java.util.Optional;
import javax.inject.Inject;
@@ -107,7 +104,7 @@ public final class HostCreateFlow implements TransactionalFlow {
DateTime now = tm().getTransactionTime();
verifyResourceDoesNotExist(Host.class, targetId, now, registrarId);
// The superordinate domain of the host object if creating an in-bailiwick host, or null if
// creating an external host. This is looked up before we actually create the Host object so
// creating an external host. This is looked up before we actually create the Host object, so
// we can detect error conditions earlier.
Optional<Domain> superordinateDomain =
lookupSuperordinateDomain(validateHostName(targetId), now);
@@ -131,12 +128,7 @@ public final class HostCreateFlow implements TransactionalFlow {
.setSuperordinateDomain(superordinateDomain.map(Domain::createVKey).orElse(null))
.build();
historyBuilder.setType(HOST_CREATE).setHost(newHost);
ImmutableSet<ImmutableObject> entitiesToSave =
ImmutableSet.of(
newHost,
historyBuilder.build(),
ForeignKeyIndex.create(newHost, newHost.getDeletionTime()),
EppResourceIndex.create(Key.create(newHost)));
ImmutableSet<ImmutableObject> entitiesToSave = ImmutableSet.of(newHost, historyBuilder.build());
if (superordinateDomain.isPresent()) {
tm().update(
superordinateDomain
@@ -154,14 +146,14 @@ public final class HostCreateFlow implements TransactionalFlow {
/** Subordinate hosts must have an ip address. */
static class SubordinateHostMustHaveIpException extends RequiredParameterMissingException {
public SubordinateHostMustHaveIpException() {
SubordinateHostMustHaveIpException() {
super("Subordinate hosts must have an ip address");
}
}
/** External hosts must not have ip addresses. */
static class UnexpectedExternalHostIpException extends ParameterValueRangeErrorException {
public UnexpectedExternalHostIpException() {
UnexpectedExternalHostIpException() {
super("External hosts must not have ip addresses");
}
}
@@ -57,7 +57,6 @@ import google.registry.model.host.HostCommand.Update;
import google.registry.model.host.HostCommand.Update.AddRemove;
import google.registry.model.host.HostCommand.Update.Change;
import google.registry.model.host.HostHistory;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.persistence.VKey;
import java.util.Objects;
@@ -194,11 +193,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
ImmutableSet.Builder<ImmutableObject> entitiesToInsert = new ImmutableSet.Builder<>();
ImmutableSet.Builder<ImmutableObject> entitiesToUpdate = new ImmutableSet.Builder<>();
entitiesToUpdate.add(newHost);
// Keep the {@link ForeignKeyIndex} for this host up to date.
if (isHostRename) {
// Update the foreign key for the old host name and save one for the new host name.
entitiesToUpdate.add(ForeignKeyIndex.create(existingHost, now));
entitiesToUpdate.add(ForeignKeyIndex.create(newHost, newHost.getDeletionTime()));
updateSuperordinateDomains(existingHost, newHost);
}
enqueueTasks(existingHost, newHost);
@@ -16,23 +16,14 @@ package google.registry.model;
import com.google.common.collect.ImmutableSet;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.common.GaeUserIdConverter;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.server.Lock;
import google.registry.model.server.ServerSecret;
/** Sets of classes of the Objectify-registered entities in use throughout the model. */
@DeleteAfterMigration
@@ -41,25 +32,14 @@ public final class EntityClasses {
/** Set of entity classes. */
public static final ImmutableSet<Class<? extends ImmutableObject>> ALL_CLASSES =
ImmutableSet.of(
AllocationToken.class,
Contact.class,
ContactHistory.class,
Domain.class,
DomainHistory.class,
EntityGroupRoot.class,
EppResourceIndex.class,
EppResourceIndexBucket.class,
ForeignKeyIndex.ForeignKeyContactIndex.class,
ForeignKeyIndex.ForeignKeyDomainIndex.class,
ForeignKeyIndex.ForeignKeyHostIndex.class,
GaeUserIdConverter.class,
HistoryEntry.class,
Host.class,
HostHistory.class,
Lock.class,
RdeRevision.class,
Registrar.class,
ServerSecret.class);
HostHistory.class);
private EntityClasses() {}
}
@@ -140,6 +140,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
DateTime lastEppUpdateTime;
/** Status values associated with this resource. */
@Ignore
@Column(name = "statuses")
// TODO(b/177567432): rename to "statuses" once we're off datastore.
Set<StatusValue> status;
@@ -257,7 +257,7 @@ public abstract class ImmutableObject implements Cloneable {
return (Map<String, Object>) toMapRecursive(this);
}
public VKey createVKey() {
public VKey<? extends ImmutableObject> createVKey() {
throw new UnsupportedOperationException("VKey creation is not supported for this entity");
}
}
@@ -23,13 +23,11 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import google.registry.model.EppResource.BuilderWithTransferData;
import google.registry.model.EppResource.ForeignKeyedEppResource;
import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.ContactPendingActionNotificationResponse;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
@@ -104,13 +102,6 @@ public final class ResourceTransferUtils {
checkState(eppResource instanceof Contact || eppResource instanceof Domain);
}
/** Update the relevant {@link ForeignKeyIndex} to cache the new deletion time. */
public static <R extends EppResource> void updateForeignKeyIndexDeletionTime(R resource) {
if (resource instanceof ForeignKeyedEppResource) {
tm().insert(ForeignKeyIndex.create(resource, resource.getDeletionTime()));
}
}
/** If there is a transfer out, delete the server-approve entities and enqueue a poll message. */
public static <R extends EppResource & ResourceWithTransferData>
void handlePendingTransferOnDelete(
@@ -1,35 +0,0 @@
// Copyright 2021 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.model.annotations;
import com.googlecode.objectify.annotation.Entity;
import google.registry.model.common.EntityGroupRoot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for an Objectify {@link Entity} to indicate that it is in the cross-TLD entity group.
*
* <p>This means that the entity's <code>@Parent</code> field has to have the value of {@link
* EntityGroupRoot#getCrossTldKey}.
*/
@DeleteAfterMigration
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface InCrossTld {}
@@ -212,6 +212,9 @@ public abstract class BillingEvent extends ImmutableObject
return nullToEmptyImmutableCopy(flags);
}
@Override
public abstract VKey<? extends BillingEvent> createVKey();
/** Override Buildable.asBuilder() to give this method stronger typing. */
@Override
public abstract Builder<?, ?> asBuilder();
@@ -14,26 +14,22 @@
package google.registry.model.common;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.InCrossTld;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
/** A singleton entity in Datastore. */
/**
* A singleton entity in the database.
*
* <p>This class should not be deleted after the migration, because there is still a concept of
* singleton in SQL. We should remove the ofy @Id annotation after all of its subclass are Ofy-free.
*/
@DeleteAfterMigration
@MappedSuperclass
@InCrossTld
public abstract class CrossTldSingleton extends ImmutableObject {
public static final long SINGLETON_ID = 1; // There is always exactly one of these.
@Id @javax.persistence.Id long id = SINGLETON_ID;
@Transient @Parent Key<EntityGroupRoot> parent = getCrossTldKey();
}
@@ -1,57 +0,0 @@
// 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.model.common;
import com.google.apphosting.api.ApiProxy;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.BackupGroupRoot;
import google.registry.model.annotations.DeleteAfterMigration;
import javax.annotation.Nullable;
/**
* The root key for the entity group which is known as the cross-tld entity group for historical
* reasons.
*
* <p>This exists as a storage place for common configuration options and global settings that
* aren't updated too frequently. Entities in this entity group are usually cached upon load. The
* reason this common entity group exists is because it enables strongly consistent queries and
* updates across this seldomly updated data. This shared entity group also helps cut down on a
* potential ballooning in the number of entity groups enlisted in transactions.
*
* <p>Historically, each TLD used to have a separate namespace, and all entities for a TLD were in a
* single EntityGroupRoot for that TLD. Hence why there was a "cross-tld" entity group -- it was the
* entity group for the single namespace where global data applicable for all TLDs lived.
*/
@Entity
@DeleteAfterMigration
public class EntityGroupRoot extends BackupGroupRoot {
@SuppressWarnings("unused")
@Id
private String id;
/** The root key for cross-tld resources such as registrars. */
public static @Nullable Key<EntityGroupRoot> getCrossTldKey() {
// If we cannot get a current environment, calling Key.create() will fail. Instead we return a
// null in cases where this key is not actually needed (for example when loading an entity from
// SQL) to initialize an object, to avoid having to register a DatastoreEntityExtension in
// tests.
return ApiProxy.getCurrentEnvironment() == null
? null
: Key.create(EntityGroupRoot.class, "cross-tld");
}
}
@@ -25,7 +25,6 @@ import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Splitter;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.Range;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -44,7 +43,6 @@ import org.joda.time.DateTime;
* allows it to be embeddable with no translation needed and also delays parsing of the string on
* load until it's actually needed.
*/
@Embed
@Embeddable
public class TimeOfYear extends ImmutableObject implements UnsafeSerializable {
@@ -22,10 +22,12 @@ import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.BackupGroupRoot;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
@@ -37,10 +39,12 @@ import javax.persistence.Table;
@Index(columnList = "gaiaId", name = "user_gaia_id_idx"),
@Index(columnList = "emailAddress", name = "user_email_address_idx")
})
public class User extends ImmutableObject implements Buildable {
public class User extends BackupGroupRoot implements Buildable {
/** Autogenerated unique ID of this user. */
@Id private long id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/** GAIA ID associated with the user in question. */
@Column(nullable = false)
@@ -63,7 +67,7 @@ public class User extends ImmutableObject implements Buildable {
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
public long getId() {
public Long getId() {
return id;
}
@@ -0,0 +1,57 @@
// 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.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import java.util.Optional;
/** Data access object for {@link User} objects to simplify saving and retrieval. */
public class UserDao {
/** Retrieves the one user with this email address if it exists. */
public static Optional<User> loadUser(String emailAddress) {
return jpaTm()
.transact(
() ->
jpaTm()
.query("FROM User WHERE emailAddress = :emailAddress", User.class)
.setParameter("emailAddress", emailAddress)
.getResultStream()
.findFirst());
}
/** Saves the given user, checking that no existing user already exists with this email. */
public static void saveUser(User user) {
jpaTm()
.transact(
() -> {
// Check for an existing user (the unique constraint protects us, but this gives a
// nicer exception)
Optional<User> maybeSavedUser = loadUser(user.getEmailAddress());
if (maybeSavedUser.isPresent()) {
User savedUser = maybeSavedUser.get();
checkArgument(
savedUser.getId().equals(user.getId()),
String.format(
"Attempted save of User with email address %s and ID %s, user with that"
+ " email already exists with ID %s",
user.getEmailAddress(), user.getId(), savedUser.getId()));
}
jpaTm().put(user);
});
}
}
@@ -14,13 +14,12 @@
package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.AuthInfo;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlType;
/** A version of authInfo specifically for contacts. */
@Embed
@javax.persistence.Embeddable
@Embeddable
@XmlType(namespace = "urn:ietf:params:xml:ns:contact-1.0")
public class ContactAuthInfo extends AuthInfo {
public static ContactAuthInfo create(PasswordAuth pw) {
@@ -129,7 +129,7 @@ public class ContactBase extends EppResource implements ResourceWithTransferData
@Index String searchName;
/** Contacts voice number. Personal info; cleared by {@link Contact.Builder#wipeOut}. */
@IgnoreSave(IfNull.class)
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "voice_phone_number")),
@@ -138,7 +138,7 @@ public class ContactBase extends EppResource implements ResourceWithTransferData
ContactPhoneNumber voice;
/** Contacts fax number. Personal info; cleared by {@link Contact.Builder#wipeOut}. */
@IgnoreSave(IfNull.class)
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "phoneNumber", column = @Column(name = "fax_phone_number")),
@@ -151,6 +151,7 @@ public class ContactBase extends EppResource implements ResourceWithTransferData
String email;
/** Authorization info (aka transfer secret) of the contact. */
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
@@ -159,7 +160,7 @@ public class ContactBase extends EppResource implements ResourceWithTransferData
ContactAuthInfo authInfo;
/** Data about any pending or past transfers on this contact. */
ContactTransferData transferData;
@Ignore ContactTransferData transferData;
/**
* The time that this resource was last transferred.
@@ -172,6 +173,7 @@ public class ContactBase extends EppResource implements ResourceWithTransferData
// the wipeOut() function, so that data is not kept around for deleted contacts.
/** Disclosure policy. */
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "name", column = @Column(name = "disclose_types_name")),
@@ -14,7 +14,6 @@
package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.PhoneNumber;
import javax.persistence.Embeddable;
@@ -27,7 +26,6 @@ import javax.persistence.Embeddable;
*
* @see Contact
*/
@Embed
@Embeddable
public class ContactPhoneNumber extends PhoneNumber {
@@ -17,7 +17,6 @@ package google.registry.model.contact;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -30,7 +29,6 @@ import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
/** The "discloseType" from <a href="http://tools.ietf.org/html/rfc5733">RFC5733</a>. */
@Embed
@Embeddable
@XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"})
public class Disclose extends ImmutableObject implements UnsafeSerializable {
@@ -79,7 +77,6 @@ public class Disclose extends ImmutableObject implements UnsafeSerializable {
}
/** The "intLocType" from <a href="http://tools.ietf.org/html/rfc5733">RFC5733</a>. */
@Embed
public static class PostalInfoChoice extends ImmutableObject implements Serializable {
@XmlAttribute
PostalInfo.Type type;
@@ -17,7 +17,6 @@ package google.registry.model.domain;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
@@ -45,7 +44,6 @@ import javax.xml.bind.annotation.XmlEnumValue;
* @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping
* - Contact and Client Identifiers</a>
*/
@Embed
@Embeddable
public class DesignatedContact extends ImmutableObject implements UnsafeSerializable {
@@ -14,12 +14,11 @@
package google.registry.model.domain;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.AuthInfo;
import javax.persistence.Embeddable;
/** A version of authInfo specifically for domains. */
@Embed
@javax.persistence.Embeddable
@Embeddable
public class DomainAuthInfo extends AuthInfo {
public static DomainAuthInfo create(PasswordAuth pw) {
DomainAuthInfo instance = new DomainAuthInfo();
@@ -41,7 +41,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
@@ -51,7 +50,6 @@ import google.registry.flows.ResourceFlowUtils;
import google.registry.model.EppResource;
import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.Contact;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus;
@@ -60,7 +58,6 @@ import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus;
@@ -139,6 +136,7 @@ public class DomainBase extends EppResource
VKey<Contact> registrantContact;
/** Authorization info (aka transfer secret) of the domain. */
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
@@ -147,13 +145,14 @@ public class DomainBase extends EppResource
DomainAuthInfo authInfo;
/** Data used to construct DS records for this domain. */
@Transient Set<DelegationSignerData> dsData;
@Ignore @Transient Set<DelegationSignerData> dsData;
/**
* The claims notice supplied when this domain was created, if there was one.
*
* <p>It's {@literal @}XmlTransient because it's not returned in an info response.
*/
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "noticeId.tcnId", column = @Column(name = "launch_notice_tcn_id")),
@@ -217,7 +216,7 @@ public class DomainBase extends EppResource
VKey<PollMessage.Autorenew> autorenewPollMessage;
/** The unexpired grace periods for this domain (some of which may not be active yet). */
@Transient Set<GracePeriod> gracePeriods;
@Ignore @Transient Set<GracePeriod> gracePeriods;
/**
* The id of the signed mark that was used to create this domain in sunrise.
@@ -228,7 +227,7 @@ public class DomainBase extends EppResource
String smdId;
/** Data about any pending or past transfers on this domain. */
DomainTransferData transferData;
@Ignore DomainTransferData transferData;
/**
* The time that this resource was last transferred.
@@ -277,7 +276,7 @@ public class DomainBase extends EppResource
@Ignore DateTime dnsRefreshRequestTime;
/** The {@link AllocationToken} for the package this domain is currently a part of. */
@Nullable VKey<AllocationToken> currentPackageToken;
@Ignore @Nullable VKey<AllocationToken> currentPackageToken;
/**
* Returns the DNS refresh request time iff this domain's DNS needs refreshing, otherwise absent.
@@ -286,15 +285,6 @@ public class DomainBase extends EppResource
return Optional.ofNullable(dnsRefreshRequestTime);
}
public static <T> VKey<T> restoreOfyFrom(Key<Domain> domainKey, VKey<T> key, Long historyId) {
if (historyId == null) {
// This is a legacy key (or a null key, in which case this works too)
return VKey.restoreOfyFrom(key, EntityGroupRoot.class, "per-tld");
} else {
return VKey.restoreOfyFrom(key, domainKey, HistoryEntry.class, historyId);
}
}
public ImmutableSet<String> getSubordinateHosts() {
return nullToEmptyImmutableCopy(subordinateHosts);
}
@@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
@@ -133,6 +134,7 @@ public class DomainHistory extends HistoryEntry {
updatable = false)
})
// HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
@Ignore
Set<DomainDsDataHistory> dsDataHistories = new HashSet<>();
@DoNotCompare
@@ -153,6 +155,7 @@ public class DomainHistory extends HistoryEntry {
updatable = false)
})
// HashSet rather than ImmutableSet so that Hibernate can fill them out lazily on request
@Ignore
Set<GracePeriodHistory> gracePeriodHistories = new HashSet<>();
@Override
@@ -19,7 +19,6 @@ import static google.registry.model.IdService.allocateId;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
@@ -40,7 +39,6 @@ import org.joda.time.DateTime;
* <p>When a grace period expires, it is lazily removed from the {@link Domain} the next time the
* resource is loaded from Datastore.
*/
@Embed
@Entity
@Table(
indexes = {
@@ -14,8 +14,6 @@
package google.registry.model.domain;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.billing.BillingEvent;
@@ -31,7 +29,6 @@ import javax.persistence.Transient;
import org.joda.time.DateTime;
/** Base class containing common fields and methods for {@link GracePeriod}. */
@Embed
@MappedSuperclass
@Access(AccessType.FIELD)
public class GracePeriodBase extends ImmutableObject implements UnsafeSerializable {
@@ -40,7 +37,6 @@ public class GracePeriodBase extends ImmutableObject implements UnsafeSerializab
@Transient long gracePeriodId;
/** Repository id for the domain which this grace period belongs to. */
@Ignore
@Column(nullable = false)
String domainRepoId;
@@ -65,7 +61,6 @@ public class GracePeriodBase extends ImmutableObject implements UnsafeSerializab
// NB: Would @IgnoreSave(IfNull.class), but not allowed for @Embed collections.
@Access(AccessType.FIELD)
@Column(name = "billing_event_id")
@Ignore
VKey<BillingEvent.OneTime> billingEventOneTime = null;
/**
@@ -75,7 +70,6 @@ public class GracePeriodBase extends ImmutableObject implements UnsafeSerializab
// NB: Would @IgnoreSave(IfNull.class), but not allowed for @Embed collections.
@Access(AccessType.FIELD)
@Column(name = "billing_recurrence_id")
@Ignore
VKey<BillingEvent.Recurring> billingEventRecurring = null;
public long getGracePeriodId() {
@@ -14,9 +14,9 @@
package google.registry.model.domain;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import javax.persistence.Embeddable;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.xml.bind.annotation.XmlAttribute;
@@ -24,8 +24,7 @@ import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlValue;
/** The "periodType" from <a href="http://tools.ietf.org/html/rfc5731">RFC5731</a>. */
@Embed
@javax.persistence.Embeddable
@Embeddable
public class Period extends ImmutableObject implements UnsafeSerializable {
@Enumerated(EnumType.STRING)
@@ -23,12 +23,10 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import com.google.common.primitives.Ints;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import java.util.Optional;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
@@ -37,17 +35,15 @@ import javax.xml.bind.annotation.XmlValue;
import org.joda.time.DateTime;
/** The claims notice id from the claims phase. */
@Embed
@XmlType(propOrder = {"noticeId", "expirationTime", "acceptedTime"})
@javax.persistence.Embeddable
@Embeddable
public class LaunchNotice extends ImmutableObject implements UnsafeSerializable {
/** An empty instance to use in place of null. */
private static final NoticeIdType EMPTY_NOTICE_ID = new NoticeIdType();
/** An id with a validator-id attribute. */
@Embed
@javax.persistence.Embeddable
@Embeddable
public static class NoticeIdType extends ImmutableObject implements UnsafeSerializable {
/**
@@ -57,7 +53,6 @@ public class LaunchNotice extends ImmutableObject implements UnsafeSerializable
@XmlValue String tcnId;
/** The identifier of the TMDB provider to use, defaulting to the TMCH. */
@IgnoreSave(IfNull.class)
@XmlAttribute(name = "validatorID")
String validatorId;
@@ -16,7 +16,6 @@ package google.registry.model.domain.launch;
import static java.util.Objects.hash;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import java.util.Objects;
import javax.xml.bind.annotation.XmlAttribute;
@@ -47,7 +46,6 @@ import javax.xml.bind.annotation.XmlValue;
* sets it is the one that needs to make sure the domain isn't a trademark and that the fields are
* correct.
*/
@Embed
public class LaunchPhase extends ImmutableObject {
/**
@@ -16,7 +16,6 @@ package google.registry.model.domain.secdns;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.secdns.DelegationSignerData.DomainDsDataId;
import java.io.Serializable;
@@ -37,7 +36,6 @@ import javax.xml.bind.annotation.XmlType;
* @see <a href="http://tools.ietf.org/html/rfc4034">RFC 4034</a>
* <p>TODO(b/177567432): Rename this class to DomainDsData.
*/
@Embed
@XmlType(name = "dsData")
@Entity
@IdClass(DomainDsDataId.class)
@@ -14,8 +14,6 @@
package google.registry.model.domain.secdns;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import javax.persistence.Access;
@@ -29,12 +27,11 @@ import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/** Base class for {@link DelegationSignerData} and {@link DomainDsDataHistory}. */
@Embed
@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
@Ignore @XmlTransient @Transient String domainRepoId;
@XmlTransient @Transient String domainRepoId;
/** The identifier for this particular key in the domain. */
@Transient int keyTag;
@@ -15,14 +15,12 @@
package google.registry.model.domain.secdns;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import javax.xml.bind.annotation.XmlRootElement;
/** The EPP secDNS extension to be returned with domain info commands. */
@XmlRootElement(name = "infData")
@Embed
public class SecDnsInfoExtension extends ImmutableObject implements ResponseExtension {
/** Signatures for this domain. */
@@ -31,18 +31,11 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Range;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils;
import google.registry.model.BackupGroupRoot;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.reporting.HistoryEntry;
@@ -55,27 +48,23 @@ import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import org.joda.time.DateTime;
/** An entity representing an allocation token. */
@ReportedOn
@Entity
@WithStringVKey
@javax.persistence.Entity
@WithStringVKey(compositeKey = true)
@Table(
indexes = {
@javax.persistence.Index(
columnList = "token",
name = "allocation_token_token_idx",
unique = true),
@javax.persistence.Index(
columnList = "domainName",
name = "allocation_token_domain_name_idx"),
@javax.persistence.Index(columnList = "tokenType"),
@javax.persistence.Index(columnList = "redemption_domain_repo_id")
@Index(columnList = "token", name = "allocation_token_token_idx", unique = true),
@Index(columnList = "domainName", name = "allocation_token_domain_name_idx"),
@Index(columnList = "tokenType"),
@Index(columnList = "redemption_domain_repo_id")
})
public class AllocationToken extends BackupGroupRoot implements Buildable {
@@ -157,11 +146,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
}
/** The allocation token string. */
@javax.persistence.Id @Id String token;
@Id String token;
/** The key of the history entry for which the token was used. Null if not yet used. */
@Nullable
@Index
@AttributeOverrides({
@AttributeOverride(name = "repoId", column = @Column(name = "redemption_domain_repo_id")),
@AttributeOverride(
@@ -171,10 +159,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
DomainHistoryVKey redemptionHistoryEntry;
/** The fully-qualified domain name that this token is limited to, if any. */
@Nullable @Index String domainName;
@Nullable String domainName;
/** When this token was created. */
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds")
@@ -203,21 +191,12 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
@Enumerated(EnumType.STRING)
@Column(name = "renewalPriceBehavior", nullable = false)
@Ignore
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
@OnLoad
void onLoad() {
if (discountYears == 0) {
discountYears = 1;
}
}
/**
* Promotional token validity periods.
*
@@ -296,7 +275,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
throw new IllegalArgumentException(
String.format("%s tokens are not stored in the database", getTokenBehavior()));
}
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
return VKey.createSql(AllocationToken.class, getToken());
}
@Override
@@ -330,7 +309,8 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
"Redemption history entry can only be specified for SINGLE_USE tokens");
checkArgument(
getInstance().tokenType != TokenType.PACKAGE
|| getInstance().allowedClientIds.size() == 1,
|| (getInstance().allowedClientIds != null
&& getInstance().allowedClientIds.size() == 1),
"PACKAGE tokens must have exactly one allowed client registrar");
checkArgument(
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
@@ -14,7 +14,6 @@
package google.registry.model.eppcommon;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import javax.persistence.Embeddable;
@@ -45,7 +44,6 @@ public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializ
}
/** The "pwAuthInfoType" complex type. */
@Embed
@XmlType(namespace = "urn:ietf:params:xml:ns:eppcom-1.0")
@Embeddable
public static class PasswordAuth extends ImmutableObject implements UnsafeSerializable {
@@ -14,7 +14,6 @@
package google.registry.model.eppcommon;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import java.io.Serializable;
import javax.persistence.Embeddable;
@@ -26,7 +25,6 @@ import javax.xml.bind.annotation.XmlTransient;
* <p>When placed in a field "foo", this will correctly unmarshal from both {@code <foo/>} and
* {@code <foo></foo>}, and will unmarshal always to {@code <foo/>}.
*/
@Embed
@Embeddable
public class PresenceMarker extends ImmutableObject implements Serializable {
@XmlTransient
@@ -16,11 +16,11 @@ package google.registry.model.eppcommon;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@@ -30,9 +30,8 @@ import javax.xml.bind.annotation.XmlType;
* is formed using the {@code clTRID} associated with the command if supplied by the client and a
* {@code svTRID} (server transaction identifier) that is assigned by and unique to the server."
*/
@Embed
@XmlType(propOrder = {"clientTransactionId", "serverTransactionId"})
@javax.persistence.Embeddable
@Embeddable
public class Trid extends ImmutableObject implements UnsafeSerializable {
/** The server transaction id. */
@@ -1,79 +0,0 @@
// 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.model.index;
import static google.registry.util.TypeUtils.instantiate;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.BackupGroupRoot;
import google.registry.model.EppResource;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.ReportedOn;
import google.registry.persistence.VKey;
/** An index that allows for quick enumeration of all EppResource entities (e.g. via map reduce). */
@ReportedOn
@Entity
@DeleteAfterMigration
public class EppResourceIndex extends BackupGroupRoot {
@Id String id;
@Parent Key<EppResourceIndexBucket> bucket;
/** Although this field holds a {@link Key} it is named "reference" for historical reasons. */
Key<? extends EppResource> reference;
@Index String kind;
public String getId() {
return id;
}
public String getKind() {
return kind;
}
public Key<? extends EppResource> getKey() {
return reference;
}
@VisibleForTesting
public Key<EppResourceIndexBucket> getBucket() {
return bucket;
}
@VisibleForTesting
public static <T extends EppResource> EppResourceIndex create(
Key<EppResourceIndexBucket> bucket, Key<T> resourceKey) {
EppResourceIndex instance = instantiate(EppResourceIndex.class);
instance.reference = resourceKey;
instance.kind = resourceKey.getKind();
// creates a web-safe key string, this value is never used
// TODO(b/211785379): remove unused id
instance.id = VKey.from(resourceKey).stringify();
instance.bucket = bucket;
return instance;
}
public static <T extends EppResource> EppResourceIndex create(Key<T> resourceKey) {
return create(EppResourceIndexBucket.getBucketKey(resourceKey), resourceKey);
}
}
@@ -1,68 +0,0 @@
// 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.model.index;
import static google.registry.config.RegistryConfig.getEppResourceIndexBucketCount;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Hashing;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.VirtualEntity;
/** A virtual entity to represent buckets to which EppResourceIndex objects are randomly added. */
@Entity
@VirtualEntity
@DeleteAfterMigration
public class EppResourceIndexBucket extends ImmutableObject {
@SuppressWarnings("unused")
@Id
private long bucketId;
/**
* Deterministic function that returns a bucket id based on the resource's roid.
* NB: At the moment, nothing depends on this being deterministic, so we have the ability to
* change the number of buckets and utilize a random distribution once we do.
*/
private static long getBucketIdFromEppResource(Key<? extends EppResource> resourceKey) {
int numBuckets = getEppResourceIndexBucketCount();
// IDs can't be 0, so add 1 to the hash.
return Hashing.consistentHash(resourceKey.getName().hashCode(), numBuckets) + 1L;
}
/** Gets a bucket key as a function of an EppResource to be indexed. */
public static Key<EppResourceIndexBucket> getBucketKey(Key<? extends EppResource> resourceKey) {
return Key.create(EppResourceIndexBucket.class, getBucketIdFromEppResource(resourceKey));
}
/** Gets the specified numbered bucket key. */
public static Key<EppResourceIndexBucket> getBucketKey(int bucketId) {
return Key.create(EppResourceIndexBucket.class, bucketId);
}
/** Returns the keys to all buckets. */
public static Iterable<Key<EppResourceIndexBucket>> getAllBuckets() {
ImmutableList.Builder<Key<EppResourceIndexBucket>> builder = new ImmutableList.Builder<>();
for (int bucketId = 1; bucketId <= getEppResourceIndexBucketCount(); bucketId++) {
builder.add(getBucketKey(bucketId));
}
return builder.build();
}
}
@@ -19,10 +19,8 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.entriesToImmutableMap;
import static google.registry.util.TypeUtils.instantiate;
@@ -36,17 +34,10 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig;
import google.registry.model.BackupGroupRoot;
import google.registry.model.CacheUtils;
import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
import google.registry.model.EppResource;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
@@ -67,22 +58,15 @@ import org.joda.time.DateTime;
* the foreign key string. The instance is never deleted, but it is updated if a newer entity
* becomes the active entity.
*/
@DeleteAfterMigration
public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroupRoot {
/** The {@link ForeignKeyIndex} type for {@link Contact} entities. */
@ReportedOn
@Entity
public static class ForeignKeyContactIndex extends ForeignKeyIndex<Contact> {}
/** The {@link ForeignKeyIndex} type for {@link Domain} entities. */
@ReportedOn
@Entity
public static class ForeignKeyDomainIndex extends ForeignKeyIndex<Domain> {}
/** The {@link ForeignKeyIndex} type for {@link Host} entities. */
@ReportedOn
@Entity
public static class ForeignKeyHostIndex extends ForeignKeyIndex<Host> {}
private static final ImmutableBiMap<
@@ -100,23 +84,18 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
Domain.class, "fullyQualifiedDomainName",
Host.class, "fullyQualifiedHostName");
@Id String foreignKey;
String foreignKey;
/**
* The deletion time of this {@link ForeignKeyIndex}.
*
* <p>This will generally be equal to the deletion time of {@link #topReference}. However, in the
* <p>This will generally be equal to the deletion time of {@link #reference}. However, in the
* case of a {@link Host} that was renamed, this field will hold the time of the rename.
*/
@Index DateTime deletionTime;
DateTime deletionTime;
/**
* The referenced resource.
*
* <p>This field holds a key to the only referenced resource. It is named "topReference" for
* historical reasons.
*/
VKey<E> topReference;
/** The referenced resource. */
VKey<E> reference;
public String getForeignKey() {
return foreignKey;
@@ -127,7 +106,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
}
public VKey<E> getResourceKey() {
return topReference;
return reference;
}
@SuppressWarnings("unchecked")
@@ -137,26 +116,19 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
}
/** Create a {@link ForeignKeyIndex} instance for a resource, expiring at a specified time. */
public static <E extends EppResource> ForeignKeyIndex<E> create(
@SuppressWarnings("unchecked")
private static <E extends EppResource> ForeignKeyIndex<E> create(
E resource, DateTime deletionTime) {
@SuppressWarnings("unchecked")
Class<E> resourceClass = (Class<E>) resource.getClass();
ForeignKeyIndex<E> instance = instantiate(mapToFkiClass(resourceClass));
instance.topReference = (VKey<E>) resource.createVKey();
instance.reference = (VKey<E>) resource.createVKey();
instance.foreignKey = resource.getForeignKey();
instance.deletionTime = deletionTime;
return instance;
}
/** Create a {@link ForeignKeyIndex} key for a resource. */
public static <E extends EppResource> Key<ForeignKeyIndex<E>> createKey(E resource) {
@SuppressWarnings("unchecked")
Class<E> resourceClass = (Class<E>) resource.getClass();
return Key.create(mapToFkiClass(resourceClass), resource.getForeignKey());
}
/**
* Loads a {@link Key} to an {@link EppResource} from Datastore by foreign key.
* Loads a {@link VKey} to an {@link EppResource} from the database by foreign key.
*
* <p>Returns null if no foreign key index with this foreign key was ever created, or if the most
* recently created foreign key index was deleted before time "now". This method does not actually
@@ -172,7 +144,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
public static <E extends EppResource> VKey<E> loadAndGetKey(
Class<E> clazz, String foreignKey, DateTime now) {
ForeignKeyIndex<E> index = load(clazz, foreignKey, now);
return (index == null) ? null : index.getResourceKey();
return index == null ? null : index.getResourceKey();
}
/**
@@ -196,7 +168,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
*/
public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> load(
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
return loadIndexesFromStore(clazz, foreignKeys, true, false).entrySet().stream()
return loadIndexesFromStore(clazz, foreignKeys, false).entrySet().stream()
.filter(e -> now.isBefore(e.getValue().getDeletionTime()))
.collect(entriesToImmutableMap());
}
@@ -206,55 +178,37 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
* keys, regardless of whether or not they have been soft-deleted.
*
* <p>Used by both the cached (w/o deletion check) and the non-cached (with deletion check) calls.
*
* <p>Note that in the cached case, we wish to run this outside of any transaction because we may
* be loading many entities, going over the Datastore limit on the number of enrolled entity
* groups per transaction (25). If we require consistency, however, we must use a transaction.
*
* @param inTransaction whether or not to use an Objectify transaction
*/
private static <E extends EppResource>
ImmutableMap<String, ForeignKeyIndex<E>> loadIndexesFromStore(
Class<E> clazz,
Collection<String> foreignKeys,
boolean inTransaction,
boolean useReplicaJpaTm) {
if (tm().isOfy()) {
Class<ForeignKeyIndex<E>> fkiClass = mapToFkiClass(clazz);
return ImmutableMap.copyOf(
inTransaction
? auditedOfy().load().type(fkiClass).ids(foreignKeys)
: tm().doTransactionless(() -> auditedOfy().load().type(fkiClass).ids(foreignKeys)));
} else {
String property = RESOURCE_CLASS_TO_FKI_PROPERTY.get(clazz);
JpaTransactionManager jpaTmToUse = useReplicaJpaTm ? replicaJpaTm() : jpaTm();
ImmutableList<ForeignKeyIndex<E>> indexes =
jpaTmToUse.transact(
() ->
jpaTmToUse
.criteriaQuery(
CriteriaQueryBuilder.create(clazz)
.whereFieldIsIn(property, foreignKeys)
.build())
.getResultStream()
.map(e -> ForeignKeyIndex.create(e, e.getDeletionTime()))
.collect(toImmutableList()));
// We need to find and return the entities with the maximum deletionTime for each foreign key.
return Multimaps.index(indexes, ForeignKeyIndex::getForeignKey).asMap().entrySet().stream()
.map(
entry ->
Maps.immutableEntry(
entry.getKey(),
entry.getValue().stream()
.max(Comparator.comparing(ForeignKeyIndex::getDeletionTime))
.get()))
.collect(entriesToImmutableMap());
}
Class<E> clazz, Collection<String> foreignKeys, boolean useReplicaJpaTm) {
String property = RESOURCE_CLASS_TO_FKI_PROPERTY.get(clazz);
JpaTransactionManager jpaTmToUse = useReplicaJpaTm ? replicaJpaTm() : jpaTm();
ImmutableList<ForeignKeyIndex<E>> indexes =
jpaTmToUse.transact(
() ->
jpaTmToUse
.criteriaQuery(
CriteriaQueryBuilder.create(clazz)
.whereFieldIsIn(property, foreignKeys)
.build())
.getResultStream()
.map(e -> create(e, e.getDeletionTime()))
.collect(toImmutableList()));
// We need to find and return the entities with the maximum deletionTime for each foreign key.
return Multimaps.index(indexes, ForeignKeyIndex::getForeignKey).asMap().entrySet().stream()
.map(
entry ->
Maps.immutableEntry(
entry.getKey(),
entry.getValue().stream()
.max(Comparator.comparing(ForeignKeyIndex::getDeletionTime))
.get()))
.collect(entriesToImmutableMap());
}
static final CacheLoader<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>> CACHE_LOADER =
new AppEngineEnvironmentCacheLoader<
VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>() {
new CacheLoader<VKey<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>() {
@Override
public Optional<ForeignKeyIndex<?>> load(VKey<ForeignKeyIndex<?>> key) {
@@ -263,7 +217,6 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
loadIndexesFromStore(
RESOURCE_CLASS_TO_FKI_CLASS.inverse().get(key.getKind()),
ImmutableSet.of(foreignKey),
false,
true)
.get(foreignKey));
}
@@ -280,7 +233,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
Streams.stream(keys).map(v -> v.getSqlKey().toString()).collect(toImmutableSet());
ImmutableSet<VKey<ForeignKeyIndex<?>>> typedKeys = ImmutableSet.copyOf(keys);
ImmutableMap<String, ? extends ForeignKeyIndex<? extends EppResource>> existingFkis =
loadIndexesFromStore(resourceClass, foreignKeys, false, true);
loadIndexesFromStore(resourceClass, foreignKeys, true);
// ofy omits keys that don't have values in Datastore, so re-add them in
// here with Optional.empty() values.
return Maps.asMap(
@@ -333,14 +286,14 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
public static <E extends EppResource> ImmutableMap<String, ForeignKeyIndex<E>> loadCached(
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
if (!RegistryConfig.isEppResourceCachingEnabled()) {
return tm().doTransactionless(() -> load(clazz, foreignKeys, now));
return load(clazz, foreignKeys, now);
}
Class<? extends ForeignKeyIndex<?>> fkiClass = mapToFkiClass(clazz);
// Safe to cast VKey<FKI<E>> to VKey<FKI<?>>
@SuppressWarnings("unchecked")
ImmutableList<VKey<ForeignKeyIndex<?>>> fkiVKeys =
foreignKeys.stream()
.map(fk -> (VKey<ForeignKeyIndex<?>>) VKey.create(fkiClass, fk))
.map(fk -> (VKey<ForeignKeyIndex<?>>) VKey.createSql(fkiClass, fk))
.collect(toImmutableList());
// This cast is safe because when we loaded ForeignKeyIndexes above we used type clazz, which
// is scoped to E.
@@ -15,7 +15,6 @@
package google.registry.model.poll;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import google.registry.model.eppcommon.Trid;
@@ -36,7 +35,6 @@ public class PendingActionNotificationResponse extends ImmutableObject
implements ResponseData, UnsafeSerializable {
/** The inner name type that contains a name and the result boolean. */
@Embed
@Embeddable
static class NameOrId extends ImmutableObject implements UnsafeSerializable {
@XmlValue
@@ -80,7 +78,6 @@ public class PendingActionNotificationResponse extends ImmutableObject
}
/** An adapter to output the XML in response to resolving a pending command on a domain. */
@Embed
@XmlRootElement(name = "panData", namespace = "urn:ietf:params:xml:ns:domain-1.0")
@XmlType(
propOrder = {"name", "trid", "processedDate"},
@@ -105,7 +102,6 @@ public class PendingActionNotificationResponse extends ImmutableObject
}
/** An adapter to output the XML in response to resolving a pending command on a contact. */
@Embed
@XmlRootElement(name = "panData", namespace = "urn:ietf:params:xml:ns:contact-1.0")
@XmlType(
propOrder = {"id", "trid", "processedDate"},
@@ -130,7 +126,6 @@ public class PendingActionNotificationResponse extends ImmutableObject
}
/** An adapter to output the XML in response to resolving a pending command on a host. */
@Embed
@XmlRootElement(name = "panData", namespace = "urn:ietf:params:xml:ns:domain-1.0")
@XmlType(
propOrder = {"name", "trid", "processedDate"},
@@ -215,6 +215,9 @@ public abstract class PollMessage extends ImmutableObject
return domainRepoId != null ? Type.DOMAIN : contactRepoId != null ? Type.CONTACT : Type.HOST;
}
@Override
public abstract VKey<? extends PollMessage> createVKey();
public abstract ImmutableList<ResponseData> getResponseData();
/** Override Buildable.asBuilder() to give this method stronger typing. */
@@ -19,10 +19,6 @@ import static google.registry.model.rde.RdeNamingUtils.makePartialName;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.base.VerifyException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.BackupGroupRoot;
import google.registry.model.ImmutableObject;
import google.registry.model.rde.RdeRevision.RdeRevisionId;
@@ -32,10 +28,11 @@ import java.io.Serializable;
import java.util.Optional;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Transient;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
@@ -47,18 +44,14 @@ import org.joda.time.LocalDate;
* flag is included in the generated XML.
*/
@Entity
@javax.persistence.Entity
@IdClass(RdeRevisionId.class)
public final class RdeRevision extends BackupGroupRoot {
/** String triplet of tld, date, and mode, e.g. {@code soy_2015-09-01_full}. */
@Id @Transient String id;
@Id String tld;
@javax.persistence.Id @Ignore String tld;
@Id LocalDate date;
@javax.persistence.Id @Ignore LocalDate date;
@javax.persistence.Id @Ignore RdeMode mode;
@Id RdeMode mode;
/**
* Number of last revision successfully staged to GCS.
@@ -71,10 +64,8 @@ public final class RdeRevision extends BackupGroupRoot {
/** Hibernate requires an empty constructor. */
private RdeRevision() {}
public static RdeRevision create(
String id, String tld, LocalDate date, RdeMode mode, int revision) {
public static RdeRevision create(String tld, LocalDate date, RdeMode mode, int revision) {
RdeRevision instance = new RdeRevision();
instance.id = id;
instance.tld = tld;
instance.date = date;
instance.mode = mode;
@@ -92,12 +83,9 @@ public final class RdeRevision extends BackupGroupRoot {
* @return {@code 0} for first deposit generation and {@code >0} for resends
*/
public static int getNextRevision(String tld, DateTime date, RdeMode mode) {
String id = makePartialName(tld, date, mode);
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, id);
Optional<RdeRevision> revisionOptional =
tm().transact(
() -> tm().loadByKeyIfPresent(VKey.create(RdeRevision.class, sqlKey, ofyKey)));
tm().transact(() -> tm().loadByKeyIfPresent(VKey.createSql(RdeRevision.class, sqlKey)));
return revisionOptional.map(rdeRevision -> rdeRevision.revision + 1).orElse(0);
}
@@ -121,12 +109,10 @@ public final class RdeRevision extends BackupGroupRoot {
*/
public static void saveRevision(String tld, DateTime date, RdeMode mode, int revision) {
checkArgument(revision >= 0, "Negative revision: %s", revision);
String triplet = makePartialName(tld, date, mode);
tm().assertInTransaction();
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, triplet);
Optional<RdeRevision> revisionOptional =
tm().loadByKeyIfPresent(VKey.create(RdeRevision.class, sqlKey, ofyKey));
tm().loadByKeyIfPresent(VKey.createSql(RdeRevision.class, sqlKey));
if (revision == 0) {
revisionOptional.ifPresent(
rdeRevision -> {
@@ -139,7 +125,7 @@ public final class RdeRevision extends BackupGroupRoot {
checkArgument(
revisionOptional.isPresent(),
"Couldn't find existing RDE revision %s when trying to save new revision %s",
triplet,
makePartialName(tld, date, mode),
revision);
checkArgument(
revisionOptional.get().revision == revision - 1,
@@ -147,7 +133,7 @@ public final class RdeRevision extends BackupGroupRoot {
revision - 1,
revisionOptional.get());
}
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
RdeRevision object = create(tld, date.toLocalDate(), mode, revision);
tm().put(object);
}
@@ -26,8 +26,6 @@ import static com.google.common.collect.Sets.immutableEnumSet;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer;
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.model.tld.Registries.assertTldsExist;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@@ -44,7 +42,6 @@ import static java.util.function.Predicate.isEqual;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -55,12 +52,6 @@ import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
@@ -68,9 +59,6 @@ import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import google.registry.model.UnsafeSerializable;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.model.annotations.InCrossTld;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldType;
import google.registry.persistence.VKey;
@@ -83,6 +71,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
@@ -90,25 +79,22 @@ import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.joda.money.CurrencyUnit;
import org.joda.time.DateTime;
/** Information about a registrar. */
@ReportedOn
@Entity
@javax.persistence.Entity
@Table(
indexes = {
@javax.persistence.Index(columnList = "registrarName", name = "registrar_name_idx"),
@javax.persistence.Index(
columnList = "ianaIdentifier",
name = "registrar_iana_identifier_idx"),
@Index(columnList = "registrarName", name = "registrar_name_idx"),
@Index(columnList = "ianaIdentifier", name = "registrar_iana_identifier_idx"),
})
@InCrossTld
public class Registrar extends ImmutableObject
implements Buildable, Jsonifiable, UnsafeSerializable {
@@ -197,7 +183,7 @@ public class Registrar extends ImmutableObject
/** The states in which a {@link Registrar} is considered {@link #isLive live}. */
private static final ImmutableSet<State> LIVE_STATES =
Sets.immutableEnumSet(State.ACTIVE, State.SUSPENDED);
immutableEnumSet(State.ACTIVE, State.SUSPENDED);
/**
* The types for which a {@link Registrar} should be included in WHOIS and RDAP output. We exclude
@@ -213,18 +199,9 @@ public class Registrar extends ImmutableObject
private static final Comparator<RegistrarPoc> CONTACT_EMAIL_COMPARATOR =
comparing(RegistrarPoc::getEmailAddress, String::compareTo);
/**
* A caching {@link Supplier} of a registrarId to {@link Registrar} map.
*
* <p>The supplier's get() method enters a transactionless context briefly to avoid enrolling the
* query inside an unrelated client-affecting transaction.
*/
/** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */
private static final Supplier<ImmutableMap<String, Registrar>> CACHE_BY_REGISTRAR_ID =
memoizeWithShortExpiration(
() ->
tm().doTransactionless(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId)));
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
memoizeWithShortExpiration(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId));
/**
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
@@ -233,7 +210,6 @@ public class Registrar extends ImmutableObject
* <p>TODO(b/177567432): Rename this field to registrarId.
*/
@Id
@javax.persistence.Id
@Column(name = "registrarId", nullable = false)
String clientIdentifier;
@@ -248,7 +224,6 @@ public class Registrar extends ImmutableObject
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
* Registrars</a>
*/
@Index
@Column(nullable = false)
String registrarName;
@@ -313,7 +288,6 @@ public class Registrar extends ImmutableObject
* Localized {@link RegistrarAddress} for this registrar. Contents can be represented in
* unrestricted UTF-8.
*/
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(
@@ -338,7 +312,6 @@ public class Registrar extends ImmutableObject
* Internationalized {@link RegistrarAddress} for this registrar. All contained values must be
* representable in the 7-bit US-ASCII character set.
*/
@Ignore
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")),
@@ -367,14 +340,14 @@ public class Registrar extends ImmutableObject
*
* <ul>
* <li>8 is used for Testing Registrar.
* <li>9997 is used by ICAAN for SLA monitoring.
* <li>9997 is used by ICANN for SLA monitoring.
* <li>9999 is used for cases when the registry operator acts as registrar.
* </ul>
*
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
* IDs</a>
*/
@Index @Nullable Long ianaIdentifier;
@Nullable Long ianaIdentifier;
/** Purchase Order number used for invoices in external billing system, if applicable. */
@Nullable String poNumber;
@@ -395,7 +368,7 @@ public class Registrar extends ImmutableObject
/**
* ICANN referral email address.
*
* <p>This value is specified in the initial registrar contact. It can't be edited in the web GUI
* <p>This value is specified in the initial registrar contact. It can't be edited in the web GUI,
* and it must be specified when the registrar account is created.
*/
String icannReferralEmail;
@@ -406,10 +379,10 @@ public class Registrar extends ImmutableObject
// Metadata.
/** The time when this registrar was created. */
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** An automatically managed last-saved timestamp. */
@Ignore UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;
@@ -610,18 +583,13 @@ public class Registrar extends ImmutableObject
}
private Iterable<RegistrarPoc> getContactsIterable() {
if (tm().isOfy()) {
return auditedOfy().load().type(RegistrarPoc.class).ancestor(Registrar.this);
} else {
return tm().transact(
() ->
jpaTm()
.query(
"FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
.setParameter("registrarId", clientIdentifier)
.getResultStream()
.collect(toImmutableList()));
}
return tm().transact(
() ->
jpaTm()
.query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
.setParameter("registrarId", clientIdentifier)
.getResultStream()
.collect(toImmutableList()));
}
@Override
@@ -687,18 +655,13 @@ public class Registrar extends ImmutableObject
/** Creates a {@link VKey} for this instance. */
@Override
public VKey<Registrar> createVKey() {
return createVKey(Key.create(this));
return createVKey(clientIdentifier);
}
/** Creates a {@link VKey} for the given {@code registrarId}. */
public static VKey<Registrar> createVKey(String registrarId) {
checkArgumentNotNull(registrarId, "registrarId must be specified");
return createVKey(Key.create(getCrossTldKey(), Registrar.class, registrarId));
}
/** Creates a {@link VKey} instance from a {@link Key} instance. */
public static VKey<Registrar> createVKey(Key<Registrar> key) {
return VKey.create(Registrar.class, key.getName(), key);
return VKey.createSql(Registrar.class, registrarId);
}
/** A builder for constructing {@link Registrar}, since it is immutable. */
@@ -774,7 +737,7 @@ public class Registrar extends ImmutableObject
Set<VKey<Registry>> missingTldKeys =
Sets.difference(
newTldKeys, tm().transact(() -> tm().loadByKeysIfPresent(newTldKeys)).keySet());
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexisting TLDs: %s", missingTldKeys);
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds);
return this;
}
@@ -18,8 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
@@ -43,15 +41,13 @@ import org.joda.time.DateTime;
* only exception is for reportField = TRANSFER_LOSING_SUCCESSFUL or TRANSFER_LOSING_NACKED, which
* uses HistoryEntry.otherClientId because the losing party in a transfer is always the otherClient.
*/
@Embed
@Entity
public class DomainTransactionRecord extends ImmutableObject
implements Buildable, UnsafeSerializable {
@Id
@Ignore
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ImmutableObject.Insignificant
@Insignificant
Long id;
/** The TLD this record operates on. */
@@ -62,9 +58,9 @@ public class DomainTransactionRecord extends ImmutableObject
// Datastore-SQL validation. They are excluded from equality check since they are not set in
// Datastore.
// TODO(b/203609782): post migration, decide whether to keep these two fields.
@Ignore @ImmutableObject.Insignificant String domainRepoId;
@Insignificant String domainRepoId;
@Ignore @ImmutableObject.Insignificant Long historyRevisionId;
@Insignificant Long historyRevisionId;
/**
* The time this Transaction takes effect (counting grace periods and other nuances).
@@ -25,10 +25,9 @@ import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
@@ -155,8 +154,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
* The length of time that a create, allocate, renewal, or transfer request was issued for. Will
* be null for all other types.
*/
@IgnoreSave(IfNull.class)
@Transient // domain-specific
@Ignore @Transient // domain-specific
Period period;
/**
@@ -189,6 +187,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
/** Transaction id that made this change, or null if the entry was not created by a flow. */
@Nullable
@Ignore
@AttributeOverrides({
@AttributeOverride(
name = "clientTransactionId",
@@ -218,6 +217,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, UnsafeSe
* also be empty if the HistoryEntry refers to an EPP mutation that does not affect domain
* transaction counts (such as contact or host mutations).
*/
@Ignore
@Transient // domain-specific
@ImmutableObject.EmptySetToNull
protected Set<DomainTransactionRecord> domainTransactionRecords;
@@ -16,7 +16,6 @@ package google.registry.model.server;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.isAtOrAfter;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@@ -24,15 +23,8 @@ import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManager;
import google.registry.util.RequestStatusChecker;
import google.registry.util.RequestStatusCheckerImpl;
import java.io.Serializable;
@@ -40,6 +32,8 @@ import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
import javax.persistence.Table;
@@ -57,8 +51,6 @@ import org.joda.time.Duration;
* safe calls that automatically lock and unlock, see LockHandler.
*/
@Entity
@NotBackedUp(reason = Reason.TRANSIENT)
@javax.persistence.Entity
@Table
@IdClass(Lock.LockId.class)
public class Lock extends ImmutableObject implements Serializable {
@@ -109,14 +101,13 @@ public class Lock extends ImmutableObject implements Serializable {
/** The resource name used to create the lock. */
@Column(nullable = false)
@javax.persistence.Id
@Id
String resourceName;
/** The tld used to create the lock, or GLOBAL if it's cross-TLD. */
// TODO(b/177567432): rename to "scope" post-Datastore
@Column(nullable = false, name = "scope")
@javax.persistence.Id
String tld;
@Column(nullable = false)
@Id
String scope;
public DateTime getExpirationTime() {
return expirationTime;
@@ -124,7 +115,7 @@ public class Lock extends ImmutableObject implements Serializable {
@PostLoad
void postLoad() {
lockId = makeLockId(resourceName, tld);
lockId = makeLockId(resourceName, scope);
}
/**
@@ -146,7 +137,7 @@ public class Lock extends ImmutableObject implements Serializable {
instance.expirationTime = acquiredTime.plus(leaseLength);
instance.acquiredTime = acquiredTime;
instance.resourceName = resourceName;
instance.tld = scope;
instance.scope = scope;
return instance;
}
@@ -157,8 +148,13 @@ public class Lock extends ImmutableObject implements Serializable {
@AutoValue
abstract static class AcquireResult {
public abstract DateTime transactionTime();
public abstract @Nullable Lock existingLock();
public abstract @Nullable Lock newLock();
@Nullable
public abstract Lock existingLock();
@Nullable
public abstract Lock newLock();
public abstract LockState lockState();
public static AcquireResult create(
@@ -213,64 +209,18 @@ public class Lock extends ImmutableObject implements Serializable {
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning) {
return acquireWithTransactionManager(
resourceName, tld, leaseLength, requestStatusChecker, checkThreadRunning, tm());
}
/**
* Try to acquire a lock in SQL. Returns absent if it can't be acquired.
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*/
public static Optional<Lock> acquireSql(
String resourceName,
@Nullable String tld,
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning) {
return acquireWithTransactionManager(
resourceName, tld, leaseLength, requestStatusChecker, checkThreadRunning, jpaTm());
}
/** Release the lock. */
public void release() {
releaseWithTransactionManager(tm());
}
/**
* Release the lock from SQL.
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*/
public void releaseSql() {
releaseWithTransactionManager(jpaTm());
}
/** Try to acquire a lock. Returns absent if it can't be acquired. */
private static Optional<Lock> acquireWithTransactionManager(
String resourceName,
@Nullable String tld,
Duration leaseLength,
RequestStatusChecker requestStatusChecker,
boolean checkThreadRunning,
TransactionManager transactionManager) {
String scope = (tld != null) ? tld : GLOBAL;
String lockId = makeLockId(resourceName, scope);
String scope = tld != null ? tld : GLOBAL;
// It's important to use transactNew rather than transact, because a Lock can be used to control
// access to resources like GCS that can't be transactionally rolled back. Therefore, the lock
// must be definitively acquired before it is used, even when called inside another transaction.
Supplier<AcquireResult> lockAcquirer =
() -> {
DateTime now = transactionManager.getTransactionTime();
DateTime now = jpaTm().getTransactionTime();
// Checking if an unexpired lock still exists - if so, the lock can't be acquired.
Lock lock =
transactionManager
.loadByKeyIfPresent(
VKey.create(
Lock.class,
new LockId(resourceName, scope),
Key.create(Lock.class, lockId)))
jpaTm()
.loadByKeyIfPresent(VKey.createSql(Lock.class, new LockId(resourceName, scope)))
.orElse(null);
if (lock != null) {
logger.atInfo().log(
@@ -290,17 +240,11 @@ public class Lock extends ImmutableObject implements Serializable {
Lock newLock =
create(resourceName, scope, requestStatusChecker.getLogId(), now, leaseLength);
// Locks are not parented under an EntityGroupRoot (so as to avoid write
// contention) and don't need to be backed up.
transactionManager.put(newLock);
jpaTm().put(newLock);
return AcquireResult.create(now, lock, newLock, lockState);
};
// In ofy, backup is determined per-action, but in SQL it's determined per-transaction
AcquireResult acquireResult =
transactionManager.isOfy()
? transactionManager.transactNew(lockAcquirer)
: ((JpaTransactionManager) transactionManager).transactWithoutBackup(lockAcquirer);
AcquireResult acquireResult = jpaTm().transactWithoutBackup(lockAcquirer);
logAcquireResult(acquireResult);
lockMetrics.recordAcquire(resourceName, scope, acquireResult.lockState());
@@ -308,62 +252,51 @@ public class Lock extends ImmutableObject implements Serializable {
}
/** Release the lock. */
private void releaseWithTransactionManager(TransactionManager transactionManager) {
public void release() {
// Just use the default clock because we aren't actually doing anything that will use the clock.
Supplier<Void> lockReleaser =
() -> {
// To release a lock, check that no one else has already obtained it and if not
// delete it. If the lock in Datastore was different then this lock is gone already;
// delete it. If the lock in the database was different, then this lock is gone already;
// this can happen if release() is called around the expiration time and the lock
// expires underneath us.
VKey<Lock> key =
VKey.create(
Lock.class, new LockId(resourceName, tld), Key.create(Lock.class, lockId));
Lock loadedLock = transactionManager.loadByKeyIfPresent(key).orElse(null);
if (Lock.this.equals(loadedLock)) {
VKey<Lock> key = VKey.createSql(Lock.class, new LockId(resourceName, scope));
Lock loadedLock = jpaTm().loadByKeyIfPresent(key).orElse(null);
if (equals(loadedLock)) {
// Use deleteIgnoringReadOnly() so that we don't create a commit log entry for deleting
// the lock.
logger.atInfo().log("Deleting lock: %s", lockId);
transactionManager.delete(key);
jpaTm().delete(key);
lockMetrics.recordRelease(
resourceName,
tld,
new Duration(acquiredTime, transactionManager.getTransactionTime()));
resourceName, scope, new Duration(acquiredTime, jpaTm().getTransactionTime()));
} else {
logger.atSevere().log(
"The lock we acquired was transferred to someone else before we"
+ " released it! Did action take longer than lease length?"
+ " Our lock: %s, current lock: %s",
Lock.this, loadedLock);
this, loadedLock);
logger.atInfo().log(
"Not deleting lock: %s - someone else has it: %s", lockId, loadedLock);
}
return null;
};
// In ofy, backup is determined per-action, but in SQL it's determined per-transaction
if (transactionManager.isOfy()) {
transactionManager.transact(lockReleaser);
} else {
((JpaTransactionManager) transactionManager).transactWithoutBackup(lockReleaser);
}
jpaTm().transactWithoutBackup(lockReleaser);
}
static class LockId extends ImmutableObject implements Serializable {
String resourceName;
// TODO(b/177567432): rename to "scope" post-Datastore
@Column(name = "scope")
String tld;
String scope;
// Required for Hibernate
@SuppressWarnings("unused")
private LockId() {}
LockId(String resourceName, String tld) {
LockId(String resourceName, String scope) {
this.resourceName = checkArgumentNotNull(resourceName, "The resource name cannot be null");
this.tld = checkArgumentNotNull(tld, "Scope of a lock cannot be null");
this.scope = checkArgumentNotNull(scope, "Scope of a lock cannot be null");
}
}
}
@@ -19,27 +19,16 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Longs;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Unindex;
import google.registry.model.CacheUtils;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.model.common.CrossTldSingleton;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
import javax.persistence.Entity;
/** A secret number used for generating tokens (such as XSRF tokens). */
@Entity
@javax.persistence.Entity
@Unindex
@NotBackedUp(reason = Reason.AUTO_GENERATED)
// TODO(b/27427316): Replace this with an entry in KMSKeyring
public class ServerSecret extends CrossTldSingleton {
/**
@@ -52,13 +41,6 @@ public class ServerSecret extends CrossTldSingleton {
CacheUtils.newCacheBuilder().build(singletonClazz -> retrieveAndSaveSecret());
private static ServerSecret retrieveAndSaveSecret() {
if (tm().isOfy()) {
// Attempt a quick load if we're in ofy first to short-circuit sans transaction
Optional<ServerSecret> secretWithoutTransaction = tm().loadSingleton(ServerSecret.class);
if (secretWithoutTransaction.isPresent()) {
return secretWithoutTransaction.get();
}
}
return tm().transact(
() -> {
// Make sure we're in a transaction and attempt to load any existing secret, then
@@ -77,35 +59,13 @@ public class ServerSecret extends CrossTldSingleton {
return CACHE.get(ServerSecret.class);
}
/** Most significant 8 bytes of the UUID value (stored separately for legacy purposes). */
@Transient long mostSignificant;
/** Least significant 8 bytes of the UUID value (stored separately for legacy purposes). */
@Transient long leastSignificant;
/** The UUID value itself. */
@Column(columnDefinition = "uuid")
@Ignore
UUID secret;
/** Convert the Datastore representation to SQL. */
@OnLoad
void onLoad() {
secret = new UUID(mostSignificant, leastSignificant);
}
/** Convert the SQL representation to Datastore. */
@PostLoad
void postLoad() {
mostSignificant = secret.getMostSignificantBits();
leastSignificant = secret.getLeastSignificantBits();
}
@VisibleForTesting
static ServerSecret create(UUID uuid) {
ServerSecret secret = new ServerSecret();
secret.mostSignificant = uuid.getMostSignificantBits();
secret.leastSignificant = uuid.getLeastSignificantBits();
secret.secret = uuid;
return secret;
}
@@ -113,8 +73,8 @@ public class ServerSecret extends CrossTldSingleton {
/** Returns the value of this ServerSecret as a byte array. */
public byte[] asBytes() {
return ByteBuffer.allocate(Longs.BYTES * 2)
.putLong(mostSignificant)
.putLong(leastSignificant)
.putLong(secret.getMostSignificantBits())
.putLong(secret.getLeastSignificantBits())
.array();
}
@@ -18,7 +18,6 @@ import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.io.BaseEncoding.base64;
import com.google.appengine.api.datastore.Text;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@@ -31,7 +30,6 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-smd-02#section-2.4">
* draft-lozano-tmch-smd-02 § 2.4</a>
*/
@Embed
@XmlRootElement(name = "encodedSignedMark")
public class EncodedSignedMark extends ImmutableObject implements AbstractSignedMark {
@@ -42,8 +40,9 @@ public class EncodedSignedMark extends ImmutableObject implements AbstractSigned
String encoding;
/**
* Encoded data. This is stored in a Text field rather than a String because Objectify cannot
* autoconvert Strings greater than 500 characters to Text within {@link Embed} collections.
* Encoded data. This is stored in a Text field rather than a String due to historical reasons,
* namely that Objectify cannot autoconvert Strings greater than 500 characters to Text within
* {@code Embed} collections.
*/
@XmlValue
@XmlJavaTypeAdapter(RemoveWhitespaceTextAdapter.class)
@@ -22,7 +22,6 @@ import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.filterValues;
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
@@ -66,7 +65,6 @@ public final class Registries {
auditedOfy()
.load()
.type(Registry.class)
.ancestor(getCrossTldKey())
.keys()
.list()
.stream()
@@ -14,15 +14,11 @@
package google.registry.model.transfer;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Unindex;
import javax.persistence.Embeddable;
/** Transfer data for contact. */
@Embed
@Unindex
@Embeddable
public class ContactTransferData extends TransferData<ContactTransferData.Builder> {
public class ContactTransferData extends TransferData {
public static final ContactTransferData EMPTY = new ContactTransferData();
@Override
@@ -30,17 +26,21 @@ public class ContactTransferData extends TransferData<ContactTransferData.Builde
return EMPTY.equals(this);
}
@Override
protected Builder createEmptyBuilder() {
return new Builder();
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
public static class Builder
extends TransferData.Builder<ContactTransferData, ContactTransferData.Builder> {
/** Create a {@link ContactTransferData.Builder} wrapping a new instance. */
public static class Builder extends TransferData.Builder<ContactTransferData, Builder> {
/** Create a {@link Builder} wrapping a new instance. */
public Builder() {}
/** Create a {@link ContactTransferData.Builder} wrapping the given instance. */
/** Create a {@link Builder} wrapping the given instance. */
private Builder(ContactTransferData instance) {
super(instance);
}
@@ -17,11 +17,6 @@ package google.registry.model.transfer;
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Unindex;
import com.googlecode.objectify.condition.IfNull;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.Period;
import google.registry.model.domain.Period.Unit;
@@ -38,10 +33,8 @@ import javax.persistence.Embedded;
import org.joda.time.DateTime;
/** Transfer data for domain. */
@Embed
@Unindex
@Embeddable
public class DomainTransferData extends TransferData<DomainTransferData.Builder> {
public class DomainTransferData extends TransferData {
public static final DomainTransferData EMPTY = new DomainTransferData();
/**
@@ -75,7 +68,6 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
@Column(name = "transfer_registration_expiration_time")
DateTime transferredRegistrationExpirationTime;
@Ignore
@Column(name = "transfer_billing_cancellation_id")
public VKey<BillingEvent.Cancellation> billingCancellationId;
@@ -86,7 +78,6 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
* being transferred is not a domain.
*/
@Column(name = "transfer_billing_event_id")
@Ignore
VKey<BillingEvent.OneTime> serverApproveBillingEvent;
/**
@@ -96,7 +87,6 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
* being transferred is not a domain.
*/
@Column(name = "transfer_billing_recurrence_id")
@Ignore
VKey<BillingEvent.Recurring> serverApproveAutorenewEvent;
/**
@@ -105,7 +95,6 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
* <p>This field should be null if there is not currently a pending transfer or if the object
* being transferred is not a domain.
*/
@IgnoreSave(IfNull.class)
@Column(name = "transfer_autorenew_poll_message_id")
VKey<PollMessage.Autorenew> serverApproveAutorenewPollMessage;
@@ -113,13 +102,12 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
* Autorenew history, which we need to preserve because it's often used in contexts where we
* haven't loaded the autorenew object.
*/
@Ignore
@Column(name = "transfer_autorenew_poll_message_history_id")
Long serverApproveAutorenewPollMessageHistoryId;
@Override
public Builder copyConstantFieldsToBuilder() {
return super.copyConstantFieldsToBuilder().setTransferPeriod(this.transferPeriod);
return ((Builder) super.copyConstantFieldsToBuilder()).setTransferPeriod(transferPeriod);
}
public Period getTransferPeriod() {
@@ -169,6 +157,11 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
return EMPTY.equals(this);
}
@Override
protected Builder createEmptyBuilder() {
return new Builder();
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
@@ -192,7 +185,7 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
}
public static class Builder extends TransferData.Builder<DomainTransferData, Builder> {
/** Create a {@link DomainTransferData.Builder} wrapping a new instance. */
/** Create a {@link Builder} wrapping a new instance. */
public Builder() {}
/** Create a {@link Builder} wrapping the given instance. */
@@ -21,14 +21,12 @@ import static google.registry.util.CollectionUtils.nullToEmpty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.Buildable;
import google.registry.model.EppResource;
import google.registry.model.eppcommon.Trid;
import google.registry.model.poll.PollMessage;
import google.registry.persistence.VKey;
import google.registry.util.NullIgnoringCollectionBuilder;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.util.Set;
import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
@@ -42,9 +40,7 @@ import javax.persistence.MappedSuperclass;
* are implicitly transferred with their superordinate domain.
*/
@MappedSuperclass
public abstract class TransferData<
B extends TransferData.Builder<? extends TransferData, ? extends TransferData.Builder>>
extends BaseTransferObject implements Buildable {
public abstract class TransferData extends BaseTransferObject implements Buildable {
/** The transaction id of the most recent transfer request (or null if there never was one). */
@Embedded
@@ -58,11 +54,9 @@ public abstract class TransferData<
})
Trid transferRequestTrid;
@Ignore
@Column(name = "transfer_repo_id")
String repoId;
@Ignore
@Column(name = "transfer_history_entry_id")
Long historyEntryId;
@@ -71,15 +65,12 @@ public abstract class TransferData<
//
// In addition, there may be a third poll message for the autorenew poll message on domain
// transfer if applicable.
@Ignore
@Column(name = "transfer_poll_message_id_1")
Long pollMessageId1;
@Ignore
@Column(name = "transfer_poll_message_id_2")
Long pollMessageId2;
@Ignore
@Column(name = "transfer_poll_message_id_3")
Long pollMessageId3;
@@ -105,7 +96,9 @@ public abstract class TransferData<
}
@Override
public abstract Builder asBuilder();
public abstract Builder<?, ?> asBuilder();
protected abstract Builder<?, ?> createEmptyBuilder();
/**
* Returns a fresh Builder populated only with the constant fields of this TransferData, i.e.
@@ -121,13 +114,13 @@ public abstract class TransferData<
* <li>transferPeriod
* </ul>
*/
public B copyConstantFieldsToBuilder() {
B newBuilder = new TypeInstantiator<B>(getClass()) {}.instantiate();
public Builder<?, ?> copyConstantFieldsToBuilder() {
Builder<?, ?> newBuilder = createEmptyBuilder();
newBuilder
.setTransferRequestTrid(this.transferRequestTrid)
.setTransferRequestTime(this.transferRequestTime)
.setGainingRegistrarId(this.gainingClientId)
.setLosingRegistrarId(this.losingClientId);
.setTransferRequestTrid(transferRequestTrid)
.setTransferRequestTime(transferRequestTime)
.setGainingRegistrarId(gainingClientId)
.setLosingRegistrarId(losingClientId);
return newBuilder;
}
@@ -170,7 +163,7 @@ public abstract class TransferData<
extends BaseTransferObject.Builder<T, B> {
/** Create a {@link Builder} wrapping a new instance. */
public Builder() {}
protected Builder() {}
/** Create a {@link Builder} wrapping the given instance. */
protected Builder(T instance) {
@@ -14,7 +14,6 @@
package google.registry.model.transfer;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.EppResource;
import google.registry.model.eppoutput.EppResponse.ResponseData;
import javax.persistence.Embeddable;
@@ -33,7 +32,6 @@ import org.joda.time.DateTime;
public class TransferResponse extends BaseTransferObject implements ResponseData {
/** An adapter to output the XML in response to a transfer command on a domain. */
@Embed
@XmlRootElement(name = "trnData", namespace = "urn:ietf:params:xml:ns:domain-1.0")
@XmlType(propOrder = {
"fullyQualifiedDomainName",
@@ -82,7 +80,6 @@ public class TransferResponse extends BaseTransferObject implements ResponseData
}
/** An adapter to output the XML in response to a transfer command on a contact. */
@Embed
@XmlRootElement(name = "trnData", namespace = "urn:ietf:params:xml:ns:contact-1.0")
@XmlType(propOrder = {
"contactId",
@@ -39,7 +39,7 @@ import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.persistence.PersistenceModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.rde.JSchModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.Modules.UrlFetchServiceModule;
@@ -67,7 +67,7 @@ import javax.inject.Singleton;
GroupsModule.class,
GroupssettingsModule.class,
JSchModule.class,
Jackson2Module.class,
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
@@ -32,7 +32,7 @@ import google.registry.keyring.kms.KmsModule;
import google.registry.module.frontend.FrontendRequestComponent.FrontendRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
@@ -56,7 +56,7 @@ import javax.inject.Singleton;
FrontendRequestComponentModule.class,
GroupsModule.class,
GroupssettingsModule.class,
Jackson2Module.class,
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
@@ -32,7 +32,7 @@ import google.registry.module.pubapi.PubApiRequestComponent.PubApiRequestCompone
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.persistence.PersistenceModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
@@ -51,7 +51,7 @@ import javax.inject.Singleton;
DummyKeyringModule.class,
GroupsModule.class,
GroupssettingsModule.class,
Jackson2Module.class,
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
@@ -33,7 +33,7 @@ import google.registry.keyring.kms.KmsModule;
import google.registry.module.tools.ToolsRequestComponent.ToolsRequestComponentModule;
import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
@@ -54,7 +54,7 @@ import javax.inject.Singleton;
DriveModule.class,
GroupsModule.class,
GroupssettingsModule.class,
Jackson2Module.class,
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
@@ -38,8 +38,8 @@ import javax.persistence.MappedSuperclass;
*/
@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class EppHistoryVKey<K, E extends EppResource> extends ImmutableObject
implements Serializable {
public abstract class EppHistoryVKey<K extends ImmutableObject, E extends EppResource>
extends ImmutableObject implements Serializable {
private static final long serialVersionUID = -3906580677709539818L;
@@ -30,10 +30,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import google.registry.model.ImmutableObject;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.persistence.JpaRetries;
import google.registry.persistence.VKey;
import google.registry.util.Clock;
@@ -77,18 +73,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Retrier retrier = new Retrier(new SystemSleeper(), 3);
// The entity of classes in this set will be simply ignored when passed to modification
// operations, i.e. insert, put, update and delete. This is to help maintain a single code path
// when we switch from ofy to tm() for the database migration as we don't need have a condition
// to exclude the Datastore specific entities when the underlying tm() is jpaTm().
// TODO(b/176108270): Remove this property after database migration.
private static final ImmutableSet<Class<? extends ImmutableObject>> IGNORED_ENTITY_CLASSES =
ImmutableSet.of(
EppResourceIndex.class,
ForeignKeyContactIndex.class,
ForeignKeyDomainIndex.class,
ForeignKeyHostIndex.class);
// EntityManagerFactory is thread safe.
private final EntityManagerFactory emf;
private final Clock clock;
@@ -274,9 +258,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override
public void insert(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction();
transactionInfo.get().insertObject(entity);
}
@@ -296,9 +277,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override
public void put(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction();
transactionInfo.get().updateObject(entity);
}
@@ -322,9 +300,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override
public void update(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return;
}
assertInTransaction();
checkArgument(exists(entity), "Given entity does not exist");
transactionInfo.get().updateObject(entity);
@@ -479,9 +454,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
private int internalDelete(VKey<?> key) {
checkArgumentNotNull(key, "key must be specified");
assertInTransaction();
if (IGNORED_ENTITY_CLASSES.contains(key.getKind())) {
return 0;
}
EntityType<?> entityType = getEntityType(key.getKind());
ImmutableSet<EntityId> entityIds = getEntityIdsFromSqlKey(entityType, key.getSqlKey());
String sql =
@@ -505,9 +477,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override
public <T> T delete(T entity) {
checkArgumentNotNull(entity, "entity must be specified");
if (isEntityOfIgnoredClass(entity)) {
return entity;
}
assertInTransaction();
T managedEntity = entity;
if (!getEntityManager().contains(entity)) {
@@ -556,10 +525,6 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
}
}
private static boolean isEntityOfIgnoredClass(Object entity) {
return IGNORED_ENTITY_CLASSES.contains(entity.getClass());
}
private static ImmutableSet<EntityId> getEntityIdsFromEntity(
EntityType<?> entityType, Object entity) {
if (entityType.hasSingleIdAttribute()) {
@@ -622,12 +587,12 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
}
/** Gets the field definition from clazz or any superclass. */
private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
private static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
// Note that we have to use getDeclaredField() for this, getField() just finds public fields.
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class base = clazz.getSuperclass();
Class<?> base = clazz.getSuperclass();
if (base != null) {
return getField(base, fieldName);
} else {
@@ -18,7 +18,6 @@ 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.model.annotations.InCrossTld;
import google.registry.persistence.VKey;
import java.util.NoSuchElementException;
import java.util.Optional;
@@ -176,18 +175,14 @@ public interface TransactionManager {
/**
* Returns a list of all entities of the given type that exist in the database.
*
* <p>The resulting list is empty if there are no entities of this type. In Datastore mode, if the
* class is a member of the cross-TLD entity group (i.e. if it has the {@link InCrossTld}
* annotation, then the correct ancestor query will automatically be applied.
* <p>The resulting list is empty if there are no entities of this type.
*/
<T> ImmutableList<T> loadAllOf(Class<T> clazz);
/**
* Returns a stream of all entities of the given type that exist in the database.
*
* <p>The resulting stream is empty if there are no entities of this type. In Datastore mode, if
* the class is a member of the cross-TLD entity group (i.e. if it has the {@link InCrossTld}
* annotation, then the correct ancestor query will automatically be applied.
* <p>The resulting stream is empty if there are no entities of this type.
*/
<T> Stream<T> loadAllOfStream(Class<T> clazz);
@@ -48,7 +48,6 @@ import google.registry.model.common.Cursor.CursorType;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.JpaTransactionManagerType;
@@ -169,13 +168,6 @@ import org.joda.time.Duration;
* the initial query for the history entry running at {@code READ_COMMITTED} transaction isolation
* level.
*
* <p>This is also true in Datastore because:
*
* <ol>
* <li>{@code EppResource} queries are strongly consistent thanks to {@link EppResourceIndex}
* <li>{@code EppResource} entities are rewinded to the point-in-time of the watermark
* </ol>
*
* <p>Here's what's not deterministic:
*
* <ul>
@@ -19,7 +19,7 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import com.google.appengine.api.users.UserService;
@@ -63,17 +63,12 @@ public final class Modules {
}
}
/**
* Dagger module that causes the Jackson2 JSON parser to be used for Google APIs requests.
*
* <p>Jackson1 and GSON can also satisfy the {@link JsonFactory} interface, but we've decided to
* go with Jackson2, since it's what's used in the public examples for using Google APIs.
*/
/** Dagger module that causes the Google GSON parser to be used for Google APIs requests. */
@Module
public static final class Jackson2Module {
public static final class GsonModule {
@Provides
static JsonFactory provideJsonFactory() {
return JacksonFactory.getDefaultInstance();
return GsonFactory.getDefaultInstance();
}
}
@@ -14,11 +14,17 @@
package google.registry.request.auth;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.appengine.api.oauth.OAuthService;
import com.google.appengine.api.oauth.OAuthServiceFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
import javax.inject.Singleton;
/**
* Dagger module for authentication routines.
@@ -29,8 +35,9 @@ public class AuthModule {
/** Provides the custom authentication mechanisms (including OAuth). */
@Provides
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
OAuthAuthenticationMechanism oauthAuthenticationMechanism) {
return ImmutableList.of(oauthAuthenticationMechanism);
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
CookieOAuth2AuthenticationMechanism cookieOAuth2AuthenticationMechanism) {
return ImmutableList.of(oauthAuthenticationMechanism, cookieOAuth2AuthenticationMechanism);
}
/** Provides the OAuthService instance. */
@@ -38,4 +45,15 @@ public class AuthModule {
OAuthService provideOauthService() {
return OAuthServiceFactory.getOAuthService();
}
@Provides
@Singleton
GoogleIdTokenVerifier provideGoogleIdTokenVerifier(
@Config("allowedOauthClientIds") ImmutableSet<String> allowedOauthClientIds,
NetHttpTransport httpTransport,
JsonFactory jsonFactory) {
return new GoogleIdTokenVerifier.Builder(httpTransport, jsonFactory)
.setAudience(allowedOauthClientIds)
.build();
}
}
@@ -0,0 +1,89 @@
// 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.request.auth;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.common.flogger.FluentLogger;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
* A way to authenticate HTTP requests using OAuth2 ID tokens stored in cookies.
*
* <p>This is generic to Google Single-Sign-On and doesn't have any ties with Google App Engine.
*/
public class CookieOAuth2AuthenticationMechanism implements AuthenticationMechanism {
private static final String ID_TOKEN_COOKIE_NAME = "idToken";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final GoogleIdTokenVerifier googleIdTokenVerifier;
@Inject
public CookieOAuth2AuthenticationMechanism(GoogleIdTokenVerifier googleIdTokenVerifier) {
this.googleIdTokenVerifier = googleIdTokenVerifier;
}
@Override
public AuthResult authenticate(HttpServletRequest request) {
String rawIdToken = getRawIdTokenFromCookie(request);
if (rawIdToken == null) {
return AuthResult.NOT_AUTHENTICATED;
}
GoogleIdToken googleIdToken;
try {
googleIdToken = googleIdTokenVerifier.verify(rawIdToken);
} catch (IOException | GeneralSecurityException e) {
logger.atInfo().withCause(e).log("Error when verifying access token");
return AuthResult.NOT_AUTHENTICATED;
}
// A null token means the provided ID token was invalid or expired
if (googleIdToken == null) {
logger.atInfo().log("Token %s failed validation", rawIdToken);
return AuthResult.NOT_AUTHENTICATED;
}
String emailAddress = googleIdToken.getPayload().getEmail();
Optional<User> maybeUser = UserDao.loadUser(emailAddress);
if (!maybeUser.isPresent()) {
logger.atInfo().log("No user found for email address %s", emailAddress);
return AuthResult.NOT_AUTHENTICATED;
}
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
}
@Nullable
private String getRawIdTokenFromCookie(HttpServletRequest request) {
if (request.getCookies() == null) {
logger.atInfo().log("No cookies passed in request");
return null;
}
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equals(ID_TOKEN_COOKIE_NAME)) {
return cookie.getValue();
}
}
logger.atInfo().log("No ID token cookie");
return null;
}
}
@@ -38,26 +38,5 @@ public interface LockHandler extends Serializable {
* @return true if all locks were acquired and the callable was run; false otherwise.
*/
boolean executeWithLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames);
/**
* Acquire one or more locks using only Cloud SQL and execute a Void {@link Callable}.
*
* <p>Runs on a thread that will be killed if it doesn't complete before the lease expires.
*
* <p>Note that locks are specific either to a given tld or to the entire system (in which case
* tld should be passed as null).
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*
* @return true if all locks were acquired and the callable was run; false otherwise.
*/
boolean executeWithSqlLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames);
Callable<Void> callable, @Nullable String tld, Duration leaseLength, String... lockNames);
}
@@ -76,27 +76,6 @@ public class LockHandlerImpl implements LockHandler {
return executeWithLockAcquirer(callable, tld, leaseLength, this::acquire, lockNames);
}
/**
* Acquire one or more locks using only Cloud SQL and execute a Void {@link Callable}.
*
* <p>Thread will be killed if it doesn't complete before the lease expires.
*
* <p>Note that locks are specific either to a given tld or to the entire system (in which case
* tld should be passed as null).
*
* <p>This method exists so that Beam pipelines can acquire / load / release locks.
*
* @return whether all locks were acquired and the callable was run.
*/
@Override
public boolean executeWithSqlLocks(
final Callable<Void> callable,
@Nullable String tld,
Duration leaseLength,
String... lockNames) {
return executeWithLockAcquirer(callable, tld, leaseLength, this::acquireSql, lockNames);
}
private boolean executeWithLockAcquirer(
final Callable<Void> callable,
@Nullable String tld,
@@ -138,11 +117,6 @@ public class LockHandlerImpl implements LockHandler {
return Lock.acquire(lockName, tld, leaseLength, requestStatusChecker, true);
}
@VisibleForTesting
Optional<Lock> acquireSql(String lockName, @Nullable String tld, Duration leaseLength) {
return Lock.acquireSql(lockName, tld, leaseLength, requestStatusChecker, true);
}
private interface LockAcquirer {
Optional<Lock> acquireLock(String lockName, @Nullable String tld, Duration leaseLength);
}
@@ -314,7 +314,7 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
private ImmutableSet<String> getExistingTokenStrings(ImmutableSet<String> candidates) {
ImmutableSet<VKey<AllocationToken>> existingTokenKeys =
candidates.stream()
.map(input -> VKey.create(AllocationToken.class, input))
.map(input -> VKey.createSql(AllocationToken.class, input))
.collect(toImmutableSet());
return tm().transact(
() ->
@@ -47,7 +47,7 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
for (List<String> tokens : Lists.partition(mainParameters, BATCH_SIZE)) {
ImmutableList<VKey<AllocationToken>> tokenKeys =
tokens.stream()
.map(t -> VKey.create(AllocationToken.class, t))
.map(t -> VKey.createSql(AllocationToken.class, t))
.collect(toImmutableList());
tm().transact(
() ->
@@ -16,6 +16,7 @@ package google.registry.tools;
import com.google.common.collect.ImmutableMap;
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
import google.registry.tools.javascrap.CreateCancellationsForOneTimesCommand;
/** Container class to create and run remote commands against a Datastore instance. */
public final class RegistryTool {
@@ -36,6 +37,7 @@ public final class RegistryTool {
.put("convert_idn", ConvertIdnCommand.class)
.put("count_domains", CountDomainsCommand.class)
.put("create_anchor_tenant", CreateAnchorTenantCommand.class)
.put("create_cancellations_for_one_times", CreateCancellationsForOneTimesCommand.class)
.put("create_cdns_tld", CreateCdnsTld.class)
.put("create_contact", CreateContactCommand.class)
.put("create_domain", CreateDomainCommand.class)
@@ -92,7 +94,6 @@ public final class RegistryTool {
.put("pending_escrow", PendingEscrowCommand.class)
.put("registrar_poc", RegistrarPocCommand.class)
.put("renew_domain", RenewDomainCommand.class)
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
.put("save_sql_credential", SaveSqlCredentialCommand.class)
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)
.put("set_database_migration_state", SetDatabaseMigrationStateCommand.class)
@@ -36,12 +36,13 @@ import google.registry.persistence.PersistenceModule.ReadOnlyReplicaJpaTm;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.rde.RdeModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.GsonModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.Modules.UrlFetchServiceModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.tools.AuthModule.LocalCredentialModule;
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
import google.registry.tools.javascrap.CreateCancellationsForOneTimesCommand;
import google.registry.util.UtilsModule;
import google.registry.whois.NonCachingWhoisModule;
import javax.annotation.Nullable;
@@ -65,7 +66,7 @@ import javax.inject.Singleton;
CloudTasksUtilsModule.class,
DummyKeyringModule.class,
DnsUpdateWriterModule.class,
Jackson2Module.class,
GsonModule.class,
KeyModule.class,
KeyringModule.class,
KmsModule.class,
@@ -95,6 +96,8 @@ interface RegistryToolComponent {
void inject(CreateAnchorTenantCommand command);
void inject(CreateCancellationsForOneTimesCommand command);
void inject(CreateCdnsTld command);
void inject(CreateContactCommand command);
@@ -1,57 +0,0 @@
// 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 static com.google.common.collect.Lists.partition;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameters;
import com.googlecode.objectify.Key;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.tld.Registry;
/**
* Command to re-save all environment entities to ensure that they have valid commit logs.
*
* <p>The entities that are re-saved are those of type {@link Registry}, {@link Registrar}, and
* {@link RegistrarPoc}.
*/
@Parameters(commandDescription = "Re-save all environment entities.")
@DeleteAfterMigration
final class ResaveEnvironmentEntitiesCommand implements CommandWithRemoteApi {
private static final int BATCH_SIZE = 10;
@Override
public void run() {
batchSave(Registry.class);
batchSave(Registrar.class);
batchSave(RegistrarPoc.class);
}
private static <T> void batchSave(Class<T> clazz) {
System.out.printf("Re-saving %s entities.\n", clazz.getSimpleName());
for (final Iterable<Key<T>> batch :
partition(
auditedOfy().load().type(clazz).ancestor(getCrossTldKey()).keys().list(), BATCH_SIZE)) {
tm().transact(() -> auditedOfy().save().entities(auditedOfy().load().keys(batch).values()));
System.out.printf("Re-saved entities batch: %s.\n", batch);
}
}
}
@@ -55,7 +55,7 @@ abstract class UpdateOrDeleteAllocationTokensCommand extends ConfirmingCommand
if (tokens != null) {
ImmutableSet<VKey<AllocationToken>> keys =
tokens.stream()
.map(token -> VKey.create(AllocationToken.class, token))
.map(token -> VKey.createSql(AllocationToken.class, token))
.collect(toImmutableSet());
ImmutableSet<VKey<AllocationToken>> nonexistentKeys =
tm().transact(
@@ -0,0 +1,126 @@
// 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.tools.javascrap;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableSet;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Cancellation;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.QueryComposer.Comparator;
import google.registry.tools.CommandWithRemoteApi;
import google.registry.tools.ConfirmingCommand;
import google.registry.tools.params.LongParameter;
import java.util.List;
/**
* Command to create {@link Cancellation}s for specified {@link OneTime} billing events.
*
* <p>This can be used to fix situations where we've inadvertently billed registrars. It's generally
* easier and better to issue cancellations within the Nomulus system than to attempt to issue
* refunds after the fact.
*/
@Parameters(separators = " =", commandDescription = "Manually create Cancellations for OneTimes.")
public class CreateCancellationsForOneTimesCommand extends ConfirmingCommand
implements CommandWithRemoteApi {
@Parameter(
description = "Space-delimited billing event ID(s) to cancel",
required = true,
validateWith = LongParameter.class)
private List<Long> mainParameters;
private ImmutableSet<OneTime> oneTimesToCancel;
@Override
protected void init() {
ImmutableSet.Builder<Long> missingIdsBuilder = new ImmutableSet.Builder<>();
ImmutableSet.Builder<Long> alreadyCancelledIdsBuilder = new ImmutableSet.Builder<>();
ImmutableSet.Builder<OneTime> oneTimesBuilder = new ImmutableSet.Builder<>();
tm().transact(
() -> {
for (Long billingEventId : ImmutableSet.copyOf(mainParameters)) {
VKey<OneTime> key = VKey.createSql(OneTime.class, billingEventId);
if (tm().exists(key)) {
OneTime oneTime = tm().loadByKey(key);
if (alreadyCancelled(oneTime)) {
alreadyCancelledIdsBuilder.add(billingEventId);
} else {
oneTimesBuilder.add(oneTime);
}
} else {
missingIdsBuilder.add(billingEventId);
}
}
});
oneTimesToCancel = oneTimesBuilder.build();
System.out.printf("Found %d OneTime(s) to cancel\n", oneTimesToCancel.size());
ImmutableSet<Long> missingIds = missingIdsBuilder.build();
if (!missingIds.isEmpty()) {
System.out.printf("Missing OneTime(s) for IDs %s\n", missingIds);
}
ImmutableSet<Long> alreadyCancelledIds = alreadyCancelledIdsBuilder.build();
if (!alreadyCancelledIds.isEmpty()) {
System.out.printf(
"The following OneTime IDs were already cancelled: %s\n", alreadyCancelledIds);
}
}
@Override
protected String prompt() {
return String.format("Create cancellations for %d OneTime(s)?", oneTimesToCancel.size());
}
@Override
protected String execute() throws Exception {
int cancelledOneTimes = 0;
for (OneTime oneTime : oneTimesToCancel) {
cancelledOneTimes +=
tm().transact(
() -> {
if (alreadyCancelled(oneTime)) {
System.out.printf(
"OneTime %d already cancelled, this is unexpected.\n", oneTime.getId());
return 0;
}
tm().put(
new Cancellation.Builder()
.setOneTimeEventKey(oneTime.createVKey())
.setBillingTime(oneTime.getBillingTime())
.setDomainHistoryId(oneTime.getDomainHistoryId())
.setRegistrarId(oneTime.getRegistrarId())
.setEventTime(oneTime.getEventTime())
.setReason(BillingEvent.Reason.ERROR)
.setTargetId(oneTime.getTargetId())
.build());
System.out.printf(
"Added Cancellation for OneTime with ID %d\n", oneTime.getId());
return 1;
});
}
return String.format("Created %d Cancellation event(s)", cancelledOneTimes);
}
private boolean alreadyCancelled(OneTime oneTime) {
return tm().createQueryComposer(Cancellation.class)
.where("refOneTime", Comparator.EQ, oneTime.getId())
.first()
.isPresent();
}
}
@@ -18,7 +18,6 @@ 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.model.EppResourceUtils.loadByForeignKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntitiesIfPresent;
import static google.registry.testing.DatabaseHelper.loadByEntity;
@@ -30,12 +29,10 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryEnvironment;
import google.registry.dns.DnsQueue;
import google.registry.model.ImmutableObject;
@@ -43,8 +40,6 @@ import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Registry;
@@ -279,8 +274,7 @@ class DeleteProberDataActionTest {
}
/**
* Persists and returns a domain and a descendant history entry, billing event, and poll message,
* along with the ForeignKeyIndex and EppResourceIndex.
* Persists and returns a domain and a descendant history entry, billing event, and poll message.
*/
private static Set<ImmutableObject> persistDomainAndDescendants(String fqdn) {
Domain domain = persistDeletedDomain(fqdn, DELETION_TIME);
@@ -318,11 +312,6 @@ class DeleteProberDataActionTest {
.add(historyEntry)
.add(billingEvent)
.add(pollMessage);
if (tm().isOfy()) {
builder
.add(ForeignKeyIndex.load(Domain.class, fqdn, START_OF_TIME))
.add(loadByEntity(EppResourceIndex.create(Key.create(domain))));
}
return builder.build();
}
@@ -23,6 +23,7 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_CREATED;
import static google.registry.dns.PublishDnsUpdatesAction.RETRIES_BEFORE_PERMANENT_FAILURE;
import static google.registry.request.RequestParameters.PARAM_TLD;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
@@ -392,10 +393,13 @@ public class PublishDnsUpdatesActionTest {
}
@Test
void testTaskFailsAfterTenRetries_doesNotRetry() {
void testTaskFailsAfterMaxRetries_doesNotRetry() {
action =
createAction(
"xn--q9jyb4c", ImmutableSet.of(), ImmutableSet.of("ns1.example.xn--q9jyb4c"), 10);
"xn--q9jyb4c",
ImmutableSet.of(),
ImmutableSet.of("ns1.example.xn--q9jyb4c"),
RETRIES_BEFORE_PERMANENT_FAILURE);
doThrow(new RuntimeException()).when(dnsWriter).commit();
action.run();
cloudTasksHelper.assertNoTasksEnqueued(DNS_PUBLISH_PUSH_QUEUE_NAME);
@@ -405,7 +409,11 @@ public class PublishDnsUpdatesActionTest {
@Test
void testDomainDnsUpdateRetriesExhausted_emailIsSent() {
action =
createAction("xn--q9jyb4c", ImmutableSet.of("example.xn--q9jyb4c"), ImmutableSet.of(), 10);
createAction(
"xn--q9jyb4c",
ImmutableSet.of("example.xn--q9jyb4c"),
ImmutableSet.of(),
RETRIES_BEFORE_PERMANENT_FAILURE);
doThrow(new RuntimeException()).when(dnsWriter).commit();
action.run();
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
@@ -423,7 +431,10 @@ public class PublishDnsUpdatesActionTest {
void testHostDnsUpdateRetriesExhausted_emailIsSent() {
action =
createAction(
"xn--q9jyb4c", ImmutableSet.of(), ImmutableSet.of("ns1.example.xn--q9jyb4c"), 10);
"xn--q9jyb4c",
ImmutableSet.of(),
ImmutableSet.of("ns1.example.xn--q9jyb4c"),
RETRIES_BEFORE_PERMANENT_FAILURE);
doThrow(new RuntimeException()).when(dnsWriter).commit();
action.run();
ArgumentCaptor<EmailMessage> contentCaptor = ArgumentCaptor.forClass(EmailMessage.class);
@@ -85,6 +85,7 @@ import google.registry.flows.domain.DomainCreateFlow.AnchorTenantCreatePeriodExc
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;
@@ -1352,7 +1353,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
private void assertAllocationTokenWasRedeemed(String token) throws Exception {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry())
.hasValue(
@@ -1362,7 +1363,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
private void assertAllocationTokenWasNotRedeemed(String token) {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isFalse();
}
@@ -3142,11 +3143,40 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "1"));
runFlowAssertResponse(
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
loadFile(
"domain_create_response_wildcard.xml",
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "example.tld")
.put("CRDATE", "1999-04-03T22:00:00.0Z")
.put("EXDATE", "2000-04-03T22:00:00.0Z")
.build()));
Domain domain = reloadResourceByForeignKey();
assertThat(domain.getCurrentPackageToken()).isPresent();
Truth8.assertThat(domain.getCurrentPackageToken()).hasValue(token.createVKey());
}
@Test
void testFailure_packageToken_registrationTooLong() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistContactsAndHosts();
setEppInput(
"domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
EppException thrown =
assertThrows(PackageDomainRegisteredForTooManyYearsException.class, this::runFlow);
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"The package token abc123 cannot be used to register names for longer than 1 year.");
}
}
@@ -332,7 +332,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
@Test
private void assertAllocationTokenWasNotRedeemed(String token) {
AllocationToken reloadedToken =
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
tm().transact(() -> tm().loadByKey(VKey.createSql(AllocationToken.class, token)));
assertThat(reloadedToken.isRedeemed()).isFalse();
}
@@ -1294,4 +1294,32 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
Domain domain = reloadResourceByForeignKey();
Truth8.assertThat(domain.getCurrentPackageToken()).isEmpty();
}
@Test
void testDryRunRemovePackageToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken.Builder()
.setToken("abc123")
.setTokenType(PACKAGE)
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld"))
.setRenewalPriceBehavior(SPECIFIED)
.build());
persistDomain(SPECIFIED, Money.of(USD, 2));
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
setEppInput(
"domain_renew_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2", "TOKEN", "__REMOVEPACKAGE__"));
dryRunFlowAssertResponse(
loadFile(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2002-04-03T22:00:00.0Z")));
}
}
@@ -47,6 +47,7 @@ import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -1727,4 +1728,10 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
runFlowAsSuperuser();
assertAboutDomains().that(reloadResourceByForeignKey()).hasNoAutorenewEndTime();
}
@Test
void testDnsTaskIsNotTriggeredWhenNoDSChangeSubmitted() throws Exception {
setEppInput("domain_update_no_ds_change.xml");
assertNoDnsTasksEnqueued();
}
}
@@ -106,7 +106,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
}
/**
* Setup a domain with a transfer that should have been server approved a day ago.
* Set up a domain with a transfer that should have been server approved a day ago.
*
* <p>The transfer is from "TheRegistrar" to "NewRegistrar".
*/
@@ -508,7 +508,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
.asBuilder()
.setLastTransferTime(lastTransferTime)
.build());
// Set the new domain to have a last transfer time that is different than the last transfer
// Set the new domain to have a last transfer time that is different from the last transfer
// time on the host in question.
persistResource(
DatabaseHelper.newDomain("example.tld")
@@ -546,7 +546,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
.asBuilder()
.setLastTransferTime(clock.nowUtc().minusDays(5))
.build());
// Set the new domain to have a last transfer time that is different than the last transfer
// Set the new domain to have a last transfer time that is different from the last transfer
// time on the host in question.
persistResource(
DatabaseHelper.newDomain("example.tld")
@@ -814,7 +814,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
clock.nowUtc().minusDays(2), clock.nowUtc().minusDays(3));
}
/** Test when the new superdordinate domain has never been transferred before. */
/** Test when the new superordinate domain has never been transferred before. */
@Test
void testSuccess_externalToSubord_lastTransferTimeNotOverridden_whenNull() throws Exception {
doExternalToInternalLastTransferTimeTest(clock.nowUtc().minusDays(2), null);
@@ -20,18 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.Host;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.server.Lock;
import google.registry.model.server.ServerSecret;
import google.registry.testing.TestObject;
import org.junit.jupiter.api.Test;
@@ -48,26 +38,11 @@ public class ClassPathManagerTest {
* below are all classes supported in CLASS_REGISTRY. This test breaks if someone changes a
* classname without preserving the original name.
*/
assertThat(ClassPathManager.getClass("ForeignKeyContactIndex"))
.isEqualTo(ForeignKeyContactIndex.class);
assertThat(ClassPathManager.getClass("AllocationToken")).isEqualTo(AllocationToken.class);
assertThat(ClassPathManager.getClass("RdeRevision")).isEqualTo(RdeRevision.class);
assertThat(ClassPathManager.getClass("Host")).isEqualTo(Host.class);
assertThat(ClassPathManager.getClass("Registrar")).isEqualTo(Registrar.class);
assertThat(ClassPathManager.getClass("Contact")).isEqualTo(Contact.class);
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
assertThat(ClassPathManager.getClass("EppResourceIndexBucket"))
.isEqualTo(EppResourceIndexBucket.class);
assertThat(ClassPathManager.getClass("EntityGroupRoot")).isEqualTo(EntityGroupRoot.class);
assertThat(ClassPathManager.getClass("Lock")).isEqualTo(Lock.class);
assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class);
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
assertThat(ClassPathManager.getClass("ForeignKeyHostIndex"))
.isEqualTo(ForeignKeyHostIndex.class);
assertThat(ClassPathManager.getClass("ServerSecret")).isEqualTo(ServerSecret.class);
assertThat(ClassPathManager.getClass("EppResourceIndex")).isEqualTo(EppResourceIndex.class);
assertThat(ClassPathManager.getClass("ForeignKeyDomainIndex"))
.isEqualTo(ForeignKeyDomainIndex.class);
}
@Test
@@ -100,27 +75,12 @@ public class ClassPathManagerTest {
* The classes below are all classes supported in CLASS_NAME_REGISTRY. This test breaks if
* someone changes a classname without preserving the original name.
*/
assertThat(ClassPathManager.getClassName(ForeignKeyContactIndex.class))
.isEqualTo("ForeignKeyContactIndex");
assertThat(ClassPathManager.getClassName(AllocationToken.class)).isEqualTo("AllocationToken");
assertThat(ClassPathManager.getClassName(RdeRevision.class)).isEqualTo("RdeRevision");
assertThat(ClassPathManager.getClassName(Host.class)).isEqualTo("Host");
assertThat(ClassPathManager.getClassName(Registrar.class)).isEqualTo("Registrar");
assertThat(ClassPathManager.getClassName(Contact.class)).isEqualTo("Contact");
assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class))
.isEqualTo("GaeUserIdConverter");
assertThat(ClassPathManager.getClassName(EppResourceIndexBucket.class))
.isEqualTo("EppResourceIndexBucket");
assertThat(ClassPathManager.getClassName(EntityGroupRoot.class)).isEqualTo("EntityGroupRoot");
assertThat(ClassPathManager.getClassName(Lock.class)).isEqualTo("Lock");
assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain");
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
assertThat(ClassPathManager.getClassName(ForeignKeyHostIndex.class))
.isEqualTo("ForeignKeyHostIndex");
assertThat(ClassPathManager.getClassName(ServerSecret.class)).isEqualTo("ServerSecret");
assertThat(ClassPathManager.getClassName(EppResourceIndex.class)).isEqualTo("EppResourceIndex");
assertThat(ClassPathManager.getClassName(ForeignKeyDomainIndex.class))
.isEqualTo("ForeignKeyDomainIndex");
}
@Test
@@ -0,0 +1,89 @@
// 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.model.console;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.truth.Truth8;
import google.registry.model.EntityTestCase;
import org.junit.jupiter.api.Test;
/** Tests for {@link UserDao}. */
public class UserDaoTest extends EntityTestCase {
@Test
void testSuccess_saveAndRetrieve() {
User user1 =
new User.Builder()
.setEmailAddress("email@email.com")
.setGaiaId("gaiaId")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
User user2 =
new User.Builder()
.setEmailAddress("foo@bar.com")
.setGaiaId("otherId")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
UserDao.saveUser(user1);
UserDao.saveUser(user2);
assertAboutImmutableObjects()
.that(user1)
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "id", "updateTimestamp");
assertAboutImmutableObjects()
.that(user2)
.isEqualExceptFields(UserDao.loadUser("foo@bar.com").get(), "id", "updateTimestamp");
}
@Test
void testSuccess_absentUser() {
User user =
new User.Builder()
.setEmailAddress("email@email.com")
.setGaiaId("gaiaId")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
UserDao.saveUser(user);
User fromDb = UserDao.loadUser("email@email.com").get();
// nonexistent one should never exist
Truth8.assertThat(UserDao.loadUser("nonexistent@email.com")).isEmpty();
// now try deleting the one that does exist
jpaTm().transact(() -> jpaTm().delete(fromDb));
Truth8.assertThat(UserDao.loadUser("email@email.com")).isEmpty();
}
@Test
void testFailure_sameEmail() {
User user1 =
new User.Builder()
.setEmailAddress("email@email.com")
.setGaiaId("gaiaId")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
User user2 =
new User.Builder()
.setEmailAddress("email@email.com")
.setGaiaId("otherId")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
UserDao.saveUser(user1);
assertThrows(IllegalArgumentException.class, () -> UserDao.saveUser(user2));
assertAboutImmutableObjects()
.that(user1)
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "id", "updateTimestamp");
}
}
@@ -15,8 +15,8 @@
package google.registry.model.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.EntityTestCase;
@@ -29,32 +29,6 @@ public class UserTest extends EntityTestCase {
super(JpaEntityCoverageCheck.ENABLED);
}
@Test
void testPersistence_lookupByEmail() {
User user =
new User.Builder()
.setGaiaId("gaiaId")
.setEmailAddress("email@email.com")
.setUserRoles(
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
.build();
persistResource(user);
jpaTm()
.transact(
() -> {
assertThat(
jpaTm()
.query("FROM User WHERE emailAddress = 'email@email.com'", User.class)
.getSingleResult())
.isEqualTo(user);
assertThat(
jpaTm()
.query("FROM User WHERE emailAddress = 'bad@fake.com'", User.class)
.getResultList())
.isEmpty();
});
}
@Test
void testPersistence_lookupByGaiaId() {
User user =
@@ -64,15 +38,16 @@ public class UserTest extends EntityTestCase {
.setUserRoles(
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
.build();
persistResource(user);
jpaTm().transact(() -> jpaTm().put(user));
jpaTm()
.transact(
() -> {
assertThat(
assertAboutImmutableObjects()
.that(
jpaTm()
.query("FROM User WHERE gaiaId = 'gaiaId'", User.class)
.getSingleResult())
.isEqualTo(user);
.isEqualExceptFields(user, "id", "updateTimestamp");
assertThat(
jpaTm()
.query("FROM User WHERE gaiaId = 'badGaiaId'", User.class)
@@ -222,8 +222,7 @@ public class AllocationTokenTest extends EntityTestCase {
.setToken("abc123")
.setTokenType(TokenType.PACKAGE)
.setRenewalPriceBehavior(RenewalPriceBehavior.DEFAULT);
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> builder.build());
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Package tokens must have renewalPriceBehavior set to SPECIFIED");
@@ -280,7 +279,7 @@ public class AllocationTokenTest extends EntityTestCase {
@Test
void testBuild_onlyOneClientInPackage() {
Buildable.Builder builder =
Buildable.Builder<AllocationToken> builder =
new AllocationToken.Builder()
.setToken("foobar")
.setTokenType(PACKAGE)
@@ -531,7 +530,7 @@ public class AllocationTokenTest extends EntityTestCase {
}
private void assertTerminal(TokenStatus status) {
// The "terminal" message is slightly different so it must be tested separately
// The "terminal" message is slightly different, so it must be tested separately
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
@@ -15,17 +15,13 @@
package google.registry.model.index;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import com.google.common.collect.ImmutableList;
import google.registry.model.EntityTestCase;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.TestCacheExtension;
import java.time.Duration;
import org.junit.jupiter.api.BeforeEach;
@@ -44,16 +40,6 @@ class ForeignKeyIndexTest extends EntityTestCase {
createTld("com");
}
@Test
void testModifyForeignKeyIndex_notThrowExceptionInSql() {
Domain domain = DatabaseHelper.newDomain("test.com");
ForeignKeyIndex<Domain> fki = ForeignKeyIndex.create(domain, fakeClock.nowUtc());
tm().transact(() -> tm().insert(fki));
tm().transact(() -> tm().put(fki));
tm().transact(() -> tm().delete(fki));
tm().transact(() -> tm().update(fki));
}
@Test
void testLoadForNonexistentForeignKey_returnsNull() {
assertThat(ForeignKeyIndex.load(Host.class, "ns1.example.com", fakeClock.nowUtc())).isNull();
@@ -62,11 +48,7 @@ class ForeignKeyIndexTest extends EntityTestCase {
@Test
void testLoadForDeletedForeignKey_returnsNull() {
Host host = persistActiveHost("ns1.example.com");
if (tm().isOfy()) {
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
} else {
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
}
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
assertThat(ForeignKeyIndex.load(Host.class, "ns1.example.com", fakeClock.nowUtc())).isNull();
}
@@ -74,15 +56,7 @@ class ForeignKeyIndexTest extends EntityTestCase {
void testLoad_newerKeyHasBeenSoftDeleted() {
Host host1 = persistActiveHost("ns1.example.com");
fakeClock.advanceOneMilli();
if (tm().isOfy()) {
ForeignKeyHostIndex fki = new ForeignKeyHostIndex();
fki.foreignKey = "ns1.example.com";
fki.topReference = host1.createVKey();
fki.deletionTime = fakeClock.nowUtc();
persistResource(fki);
} else {
persistResource(host1.asBuilder().setDeletionTime(fakeClock.nowUtc()).build());
}
persistResource(host1.asBuilder().setDeletionTime(fakeClock.nowUtc()).build());
assertThat(ForeignKeyIndex.load(Host.class, "ns1.example.com", fakeClock.nowUtc())).isNull();
}
@@ -90,11 +64,7 @@ class ForeignKeyIndexTest extends EntityTestCase {
void testBatchLoad_skipsDeletedAndNonexistent() {
persistActiveHost("ns1.example.com");
Host host = persistActiveHost("ns2.example.com");
if (tm().isOfy()) {
persistResource(ForeignKeyIndex.create(host, fakeClock.nowUtc().minusDays(1)));
} else {
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
}
persistResource(host.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
assertThat(
ForeignKeyIndex.load(
Host.class,
@@ -1,348 +0,0 @@
// 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.model.ofy;
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.ofy.Ofy.getBaseEntityClassFromEntityOrKey;
import static google.registry.testing.DatabaseHelper.newContact;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreTimeoutException;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.taskqueue.TransientFailureException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.OnSave;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactHistory;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.FakeClock;
import google.registry.util.SystemClock;
import java.util.ConcurrentModificationException;
import java.util.function.Supplier;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Tests for our wrapper around Objectify. */
@Disabled
public class OfyTest {
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2000-01-01TZ"));
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder().withCloudSql().withClock(fakeClock).build();
/** An entity to use in save and delete tests. */
private HistoryEntry someObject;
@BeforeEach
void beforeEach() {
someObject =
new ContactHistory.Builder()
.setRegistrarId("clientid")
.setModificationTime(START_OF_TIME)
.setType(HistoryEntry.Type.CONTACT_CREATE)
.setContact(newContact("parentContact"))
.setTrid(Trid.create("client", "server"))
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
.build();
// This can't be initialized earlier because namespaces need the AppEngineExtension to work.
}
@Test
void testSavingKeyTwiceInOneCall() {
assertThrows(
IllegalArgumentException.class,
() -> ofy().transact(() -> auditedOfy().save().entities(someObject, someObject)));
}
/** Simple entity class with lifecycle callbacks. */
@com.googlecode.objectify.annotation.Entity
public static class LifecycleObject extends ImmutableObject {
@Parent Key<?> parent = getCrossTldKey();
@Id long id = 1;
boolean onLoadCalled;
boolean onSaveCalled;
@OnLoad
public void load() {
onLoadCalled = true;
}
@OnSave
public void save() {
onSaveCalled = true;
}
}
@Test
void testLifecycleCallbacks_loadFromEntity() {
auditedOfy().factory().register(LifecycleObject.class);
LifecycleObject object = new LifecycleObject();
Entity entity = auditedOfy().save().toEntity(object);
assertThat(object.onSaveCalled).isTrue();
assertThat(auditedOfy().load().<LifecycleObject>fromEntity(entity).onLoadCalled).isTrue();
}
@Test
void testLifecycleCallbacks_loadFromDatastore() {
auditedOfy().factory().register(LifecycleObject.class);
final LifecycleObject object = new LifecycleObject();
ofy().transact(() -> auditedOfy().save().entity(object).now());
assertThat(object.onSaveCalled).isTrue();
auditedOfy().clearSessionCache();
assertThat(auditedOfy().load().entity(object).now().onLoadCalled).isTrue();
}
/** Avoid regressions of b/21309102 where transaction time did not change on each retry. */
@Test
void testTransact_getsNewTimestampOnEachTry() {
ofy()
.transact(
new Runnable() {
DateTime firstAttemptTime;
@Override
public void run() {
if (firstAttemptTime == null) {
// Sleep a bit to ensure that the next attempt is at a new millisecond.
firstAttemptTime = ofy().getTransactionTime();
sleepUninterruptibly(java.time.Duration.ofMillis(10));
throw new ConcurrentModificationException();
}
assertThat(ofy().getTransactionTime()).isGreaterThan(firstAttemptTime);
}
});
}
@Test
void testTransact_transientFailureException_retries() {
assertThat(
ofy()
.transact(
new Supplier<Integer>() {
int count = 0;
@Override
public Integer get() {
count++;
if (count == 3) {
return count;
}
throw new TransientFailureException("");
}
}))
.isEqualTo(3);
}
@Test
void testTransact_datastoreTimeoutException_noManifest_retries() {
assertThat(
ofy()
.transact(
new Supplier<Integer>() {
int count = 0;
@Override
public Integer get() {
// We don't write anything in this transaction, so there is no commit log
// manifest.
// Therefore it's always safe to retry since nothing got written.
count++;
if (count == 3) {
return count;
}
throw new DatastoreTimeoutException("");
}
}))
.isEqualTo(3);
}
@Test
void testTransact_datastoreTimeoutException_manifestNotWrittenToDatastore_retries() {
assertThat(
ofy()
.transact(
new Supplier<Integer>() {
int count = 0;
@Override
public Integer get() {
// There will be something in the manifest now, but it won't be committed if
// we throw.
auditedOfy().save().entity(someObject.asHistoryEntry());
count++;
if (count == 3) {
return count;
}
throw new DatastoreTimeoutException("");
}
}))
.isEqualTo(3);
}
@Test
void testTransact_datastoreTimeoutException_manifestWrittenToDatastore_returnsSuccess() {
// A work unit that throws if it is ever retried.
Supplier work =
new Supplier<Void>() {
boolean firstCallToVrun = true;
@Override
public Void get() {
if (firstCallToVrun) {
firstCallToVrun = false;
auditedOfy().save().entity(someObject.asHistoryEntry());
return null;
}
fail("Shouldn't have retried.");
return null;
}
};
// A commit logged work that throws on the first attempt to get its result.
CommitLoggedWork<Void> commitLoggedWork =
new CommitLoggedWork<Void>(work, new SystemClock()) {
boolean firstCallToGetResult = true;
@Override
public Void getResult() {
if (firstCallToGetResult) {
firstCallToGetResult = false;
throw new DatastoreTimeoutException("");
}
return null;
}
};
// Despite the DatastoreTimeoutException in the first call to getResult(), this should succeed
// without retrying. If a retry is triggered, the test should fail due to the call to fail().
auditedOfy().transactCommitLoggedWork(commitLoggedWork);
}
void doReadOnlyRetryTest(final RuntimeException e) {
assertThat(
ofy()
.transactNewReadOnly(
new Supplier<Integer>() {
int count = 0;
@Override
public Integer get() {
count++;
if (count == 3) {
return count;
}
throw new TransientFailureException("");
}
}))
.isEqualTo(3);
}
@Test
void testTransactNewReadOnly_transientFailureException_retries() {
doReadOnlyRetryTest(new TransientFailureException(""));
}
@Test
void testTransactNewReadOnly_datastoreTimeoutException_retries() {
doReadOnlyRetryTest(new DatastoreTimeoutException(""));
}
@Test
void testTransactNewReadOnly_datastoreFailureException_retries() {
doReadOnlyRetryTest(new DatastoreFailureException(""));
}
@Test
void test_getBaseEntityClassFromEntityOrKey_regularEntity() {
Contact contact = newContact("testcontact");
assertThat(getBaseEntityClassFromEntityOrKey(contact)).isEqualTo(Contact.class);
assertThat(getBaseEntityClassFromEntityOrKey(Key.create(contact))).isEqualTo(Contact.class);
}
@Test
void test_getBaseEntityClassFromEntityOrKey_unregisteredEntity() {
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() -> getBaseEntityClassFromEntityOrKey(new SystemClock()));
assertThat(thrown).hasMessageThat().contains("SystemClock");
}
@Test
void test_getBaseEntityClassFromEntityOrKey_unregisteredEntityKey() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
getBaseEntityClassFromEntityOrKey(
Key.create(
com.google.appengine.api.datastore.KeyFactory.createKey(
"UnknownKind", 1))));
assertThat(thrown).hasMessageThat().contains("UnknownKind");
}
@Test
void test_doWithFreshSessionCache() {
auditedOfy().saveWithoutBackup().entity(someObject.asHistoryEntry()).now();
final HistoryEntry modifiedObject =
someObject.asBuilder().setModificationTime(END_OF_TIME).build();
// Mutate the saved objected, bypassing the Objectify session cache.
getDatastoreService()
.put(auditedOfy().saveWithoutBackup().toEntity(modifiedObject.asHistoryEntry()));
// Normal loading should come from the session cache and shouldn't reflect the mutation.
assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry());
// Loading inside doWithFreshSessionCache() should reflect the mutation.
boolean ran =
auditedOfy()
.doWithFreshSessionCache(
() -> {
assertAboutImmutableObjects()
.that(auditedOfy().load().entity(someObject).now())
.isEqualExceptFields(modifiedObject, "contactBase");
return true;
});
assertThat(ran).isTrue();
// Test the normal loading again to verify that we've restored the original session unchanged.
assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry());
}
}
@@ -121,8 +121,7 @@ public class RdeRevisionTest extends EntityTestCase {
}
public static void save(String tld, DateTime date, RdeMode mode, int revision) {
String triplet = RdeNamingUtils.makePartialName(tld, date, mode);
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
RdeRevision object = RdeRevision.create(tld, date.toLocalDate(), mode, revision);
tm().transact(() -> tm().put(object));
}
}
@@ -49,7 +49,8 @@ public class LockTest extends EntityTestCase {
super(JpaEntityCoverageCheck.ENABLED);
}
private Optional<Lock> acquire(String tld, Duration leaseLength, LockState expectedLockState) {
private static Optional<Lock> acquire(
String tld, Duration leaseLength, LockState expectedLockState) {
Lock.lockMetrics = mock(LockMetrics.class);
Optional<Lock> lock = Lock.acquire(RESOURCE_NAME, tld, leaseLength, requestStatusChecker, true);
verify(Lock.lockMetrics).recordAcquire(RESOURCE_NAME, tld, expectedLockState);
@@ -58,7 +59,7 @@ public class LockTest extends EntityTestCase {
return lock;
}
private void release(Lock lock, String expectedTld, long expectedMillis) {
private static void release(Lock lock, String expectedTld, long expectedMillis) {
Lock.lockMetrics = mock(LockMetrics.class);
lock.release();
verify(Lock.lockMetrics)
@@ -15,20 +15,16 @@
package google.registry.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.AppEngineExtension;
import javax.persistence.Transient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -74,7 +70,6 @@ class DomainHistoryVKeyTest {
@Entity
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends ImmutableObject {
@Transient @Parent Key<EntityGroupRoot> parent = getCrossTldKey();
@Id @javax.persistence.Id String id = "id";

Some files were not shown because too many files have changed in this diff Show More