mirror of
https://github.com/google/nomulus
synced 2026-05-24 08:41:48 +00:00
Compare commits
20 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7353ef338 | ||
|
|
832e1ce047 | ||
|
|
8087f5bbca | ||
|
|
7f3dbfb62f | ||
|
|
04f429c4d6 | ||
|
|
40a4c3101c | ||
|
|
e2dfb6488d | ||
|
|
c5aa0125ab | ||
|
|
01e2d24658 | ||
|
|
19bc1c9c9c | ||
|
|
c361c9e601 | ||
|
|
aa0dcea537 | ||
|
|
e920e4d201 | ||
|
|
cd13f6c5d3 | ||
|
|
210de9340e | ||
|
|
5d58be6f0a | ||
|
|
d8066ca752 | ||
|
|
ca3ae9b0e4 | ||
|
|
295251ee78 | ||
|
|
7ca0e9387c |
@@ -31,10 +31,6 @@ def coreResourcesDir = "${rootDir}/core/build/resources/main"
|
||||
war {
|
||||
webInf {
|
||||
from "../../core/src/main/java/google/registry/env/common/${project.name}/WEB-INF"
|
||||
|
||||
from("${coreResourcesDir}/META-INF/persistence.xml") {
|
||||
into "classes/META-INF"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,15 @@ PRESUBMITS = {
|
||||
"System.(out|err).println is only allowed in tools/ packages. Please "
|
||||
"use a logger instead.",
|
||||
|
||||
# ObjectifyService.register is restricted to main/ or AppEngineRule.
|
||||
PresubmitCheck(
|
||||
r".*\bObjectifyService\.register", "java", {
|
||||
"/build/", "/generated/", "node_modules/", "src/main/",
|
||||
"AppEngineRule.java"
|
||||
}):
|
||||
"ObjectifyService.register is not allowed in tests. Please use "
|
||||
"AppengineRule.register instead.",
|
||||
|
||||
# PostgreSQLContainer instantiation must specify docker tag
|
||||
PresubmitCheck(
|
||||
r"[\s\S]*new\s+PostgreSQLContainer(<[\s\S]*>)?\(\s*\)[\s\S]*",
|
||||
|
||||
@@ -295,6 +295,8 @@ dependencies {
|
||||
testAnnotationProcessor deps['com.google.auto.value:auto-value']
|
||||
annotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
|
||||
annotationProcessor project(':processor')
|
||||
testAnnotationProcessor project(':processor')
|
||||
|
||||
testCompile deps['com.google.appengine:appengine-testing']
|
||||
testCompile deps['com.google.guava:guava-testlib']
|
||||
@@ -707,7 +709,10 @@ task outcastTest(type: FilteringTest) {
|
||||
tests = outcastTestPatterns
|
||||
|
||||
// Sets the maximum number of test executors that may exist at the same time.
|
||||
maxParallelForks 5
|
||||
// Note that this number appears to contribute to NoClassDefFoundError
|
||||
// exceptions on certain machines and distros. The root cause is unclear.
|
||||
// Try reducing this number if you experience similar problems.
|
||||
maxParallelForks 3
|
||||
}
|
||||
|
||||
// Whitebox test verifying that RegistryTool can be instantiated. Note the
|
||||
|
||||
@@ -12,24 +12,24 @@ com.google.dagger:dagger-producers:2.21
|
||||
com.google.dagger:dagger-spi:2.21
|
||||
com.google.dagger:dagger:2.21
|
||||
com.google.errorprone:error_prone_annotation:2.3.3
|
||||
com.google.errorprone:error_prone_annotations:2.3.3
|
||||
com.google.errorprone:error_prone_annotations:2.3.4
|
||||
com.google.errorprone:error_prone_check_api:2.3.3
|
||||
com.google.errorprone:error_prone_core:2.3.3
|
||||
com.google.errorprone:error_prone_type_annotations:2.3.3
|
||||
com.google.errorprone:javac-shaded:9-dev-r4023-3
|
||||
com.google.googlejavaformat:google-java-format:1.5
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:27.0.1-jre
|
||||
com.google.guava:guava:28.2-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.1
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.protobuf:protobuf-java:3.4.0
|
||||
com.googlecode.java-diff-utils:diffutils:1.3.0
|
||||
com.squareup:javapoet:1.11.1
|
||||
com.squareup:javapoet:1.12.1
|
||||
javax.annotation:jsr250-api:1.0
|
||||
javax.inject:javax.inject:1
|
||||
javax.persistence:javax.persistence-api:2.2
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:2.5.3
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.checkerframework:dataflow:2.5.3
|
||||
org.checkerframework:javacutil:2.5.3
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.17
|
||||
org.pcollections:pcollections:2.1.2
|
||||
|
||||
@@ -12,24 +12,24 @@ com.google.dagger:dagger-producers:2.21
|
||||
com.google.dagger:dagger-spi:2.21
|
||||
com.google.dagger:dagger:2.21
|
||||
com.google.errorprone:error_prone_annotation:2.3.3
|
||||
com.google.errorprone:error_prone_annotations:2.3.3
|
||||
com.google.errorprone:error_prone_annotations:2.3.4
|
||||
com.google.errorprone:error_prone_check_api:2.3.3
|
||||
com.google.errorprone:error_prone_core:2.3.3
|
||||
com.google.errorprone:error_prone_type_annotations:2.3.3
|
||||
com.google.errorprone:javac-shaded:9-dev-r4023-3
|
||||
com.google.googlejavaformat:google-java-format:1.5
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:27.0.1-jre
|
||||
com.google.guava:guava:28.2-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.1
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
com.google.protobuf:protobuf-java:3.4.0
|
||||
com.googlecode.java-diff-utils:diffutils:1.3.0
|
||||
com.squareup:javapoet:1.11.1
|
||||
com.squareup:javapoet:1.12.1
|
||||
javax.annotation:jsr250-api:1.0
|
||||
javax.inject:javax.inject:1
|
||||
javax.persistence:javax.persistence-api:2.2
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:2.5.3
|
||||
org.checkerframework:checker-qual:2.10.0
|
||||
org.checkerframework:dataflow:2.5.3
|
||||
org.checkerframework:javacutil:2.5.3
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.17
|
||||
org.pcollections:pcollections:2.1.2
|
||||
|
||||
@@ -283,7 +283,9 @@ public class DeleteContactsAndHostsAction implements Runnable {
|
||||
/** Determine whether the target resource is a linked resource on the domain. */
|
||||
private boolean isLinked(DomainBase domain, Key<? extends EppResource> resourceKey) {
|
||||
if (resourceKey.getKind().equals(KIND_CONTACT)) {
|
||||
return domain.getReferencedContacts().contains(resourceKey);
|
||||
return domain
|
||||
.getReferencedContacts()
|
||||
.contains(VKey.createOfy(ContactResource.class, (Key<ContactResource>) resourceKey));
|
||||
} else if (resourceKey.getKind().equals(KIND_HOST)) {
|
||||
return domain
|
||||
.getNameservers()
|
||||
|
||||
@@ -207,8 +207,20 @@ hibernate:
|
||||
|
||||
# Connection pool configurations.
|
||||
hikariConnectionTimeout: 20000
|
||||
hikariMinimumIdle: 0
|
||||
hikariMaximumPoolSize: 20
|
||||
# We occasionally received "Connection is not available, request timed out"
|
||||
# exception when setting minimumIdle to 0 and it turned out it is a bug (See
|
||||
# https://github.com/brettwooldridge/HikariCP/issues/1212) in HikariCP.
|
||||
#
|
||||
# We tried to use a fixed size pool but ran into an issue(See b/155383029),
|
||||
# so we need further investigation to figure out the proper size of the pool.
|
||||
#
|
||||
# HikariCP also recommends not setting minimumIdle for maximum performance
|
||||
# and responsiveness to spike demands (See
|
||||
# https://github.com/brettwooldridge/HikariCP).
|
||||
#
|
||||
# TODO(b/154720215): Investigate the long term fix.
|
||||
hikariMinimumIdle: 1
|
||||
hikariMaximumPoolSize: 10
|
||||
hikariIdleTimeout: 300000
|
||||
|
||||
cloudSql:
|
||||
|
||||
@@ -48,6 +48,7 @@ import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -96,8 +97,9 @@ public final class ResourceFlowUtils {
|
||||
queryForLinkedDomains(fki.getResourceKey(), now)
|
||||
.limit(FAILFAST_CHECK_COUNT)
|
||||
.keys();
|
||||
VKey<R> resourceVKey = VKey.createOfy(resourceClass, fki.getResourceKey());
|
||||
Predicate<DomainBase> predicate =
|
||||
domain -> getPotentialReferences.apply(domain).contains(fki.getResourceKey());
|
||||
domain -> getPotentialReferences.apply(domain).contains(resourceVKey);
|
||||
return ofy().load().keys(keys).values().stream().anyMatch(predicate)
|
||||
? new ResourceToDeleteIsReferencedException()
|
||||
: null;
|
||||
@@ -184,9 +186,8 @@ public final class ResourceFlowUtils {
|
||||
}
|
||||
// The roid should match one of the contacts.
|
||||
Optional<Key<ContactResource>> foundContact =
|
||||
domain
|
||||
.getReferencedContacts()
|
||||
.stream()
|
||||
domain.getReferencedContacts().stream()
|
||||
.map(VKey::getOfyKey)
|
||||
.filter(key -> key.getName().equals(authRepoId))
|
||||
.findFirst();
|
||||
if (!foundContact.isPresent()) {
|
||||
|
||||
@@ -77,6 +77,7 @@ import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
@@ -352,7 +353,7 @@ public class DomainCreateFlow implements TransactionalFlow {
|
||||
.setLaunchNotice(hasClaimsNotice ? launchCreate.get().getNotice() : null)
|
||||
.setSmdId(signedMarkId)
|
||||
.setDsData(secDnsCreate.isPresent() ? secDnsCreate.get().getDsData() : null)
|
||||
.setRegistrant(command.getRegistrant())
|
||||
.setRegistrant(VKey.createOfy(ContactResource.class, command.getRegistrant()))
|
||||
.setAuthInfo(command.getAuthInfo())
|
||||
.setFullyQualifiedDomainName(targetId)
|
||||
.setNameservers(
|
||||
|
||||
@@ -212,6 +212,28 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
builder.setDeletePollMessage(Key.create(deletePollMessage));
|
||||
}
|
||||
|
||||
// Cancel any grace periods that were still active, and set the expiration time accordingly.
|
||||
DateTime newExpirationTime = existingDomain.getRegistrationExpirationTime();
|
||||
for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) {
|
||||
// No cancellation is written if the grace period was not for a billable event.
|
||||
if (gracePeriod.hasBillingEvent()) {
|
||||
entitiesToSave.add(
|
||||
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId));
|
||||
if (gracePeriod.getOneTimeBillingEvent() != null) {
|
||||
// Take the amount of amount of registration time being refunded off the expiration time.
|
||||
// This can be either add grace periods or renew grace periods.
|
||||
BillingEvent.OneTime oneTime =
|
||||
ofy().load().key(gracePeriod.getOneTimeBillingEvent()).now();
|
||||
newExpirationTime = newExpirationTime.minusYears(oneTime.getPeriodYears());
|
||||
} else if (gracePeriod.getRecurringBillingEvent() != null) {
|
||||
// Take 1 year off the registration if in the autorenew grace period (no need to load the
|
||||
// recurring billing event; all autorenews are for 1 year).
|
||||
newExpirationTime = newExpirationTime.minusYears(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.setRegistrationExpirationTime(newExpirationTime);
|
||||
|
||||
DomainBase newDomain = builder.build();
|
||||
updateForeignKeyIndexDeletionTime(newDomain);
|
||||
handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry);
|
||||
@@ -221,14 +243,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName());
|
||||
// Cancel any grace periods that were still active.
|
||||
for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) {
|
||||
// No cancellation is written if the grace period was not for a billable event.
|
||||
if (gracePeriod.hasBillingEvent()) {
|
||||
entitiesToSave.add(
|
||||
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId));
|
||||
}
|
||||
}
|
||||
|
||||
entitiesToSave.add(newDomain, historyEntry);
|
||||
EntityChanges entityChanges = flowCustomLogic.beforeSave(
|
||||
BeforeSaveParameters.newBuilder()
|
||||
|
||||
@@ -35,6 +35,7 @@ import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRIS
|
||||
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
|
||||
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -120,6 +121,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tmch.ClaimsListShard;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tldconfig.idn.IdnLabelValidator;
|
||||
import google.registry.util.Idn;
|
||||
import java.math.BigDecimal;
|
||||
@@ -309,7 +311,10 @@ public class DomainFlowUtils {
|
||||
Set<Key<HostResource>> nameservers)
|
||||
throws EppException {
|
||||
ImmutableList.Builder<Key<? extends EppResource>> keysToLoad = new ImmutableList.Builder<>();
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
contacts.stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
.map(VKey::getOfyKey)
|
||||
.forEach(keysToLoad::add);
|
||||
Optional.ofNullable(registrant).ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
@@ -355,7 +360,7 @@ public class DomainFlowUtils {
|
||||
contacts.stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getType, DesignatedContact::getContactKey));
|
||||
DesignatedContact::getType, contact -> contact.getContactKey().getOfyKey()));
|
||||
|
||||
// If any contact type has multiple contacts:
|
||||
if (contactsByType.asMap().values().stream().anyMatch(v -> v.size() > 1)) {
|
||||
@@ -978,7 +983,7 @@ public class DomainFlowUtils {
|
||||
for (DesignatedContact contact : contacts) {
|
||||
builder.add(
|
||||
ForeignKeyedDesignatedContact.create(
|
||||
contact.getType(), ofy().load().key(contact.getContactKey()).now().getContactId()));
|
||||
contact.getType(), tm().load(contact.getContactKey()).getContactId()));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import static google.registry.flows.domain.DomainFlowUtils.addSecDnsExtensionIfP
|
||||
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.loadForeignKeyedDesignatedContacts;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -102,16 +101,16 @@ public final class DomainInfoFlow implements Flow {
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder().setDomain(domain).build());
|
||||
// Prefetch all referenced resources. Calling values() blocks until loading is done.
|
||||
// We do nameservers separately since they've been converted to VKey.
|
||||
tm().load(domain.getNameservers());
|
||||
ofy().load().values(domain.getReferencedContacts()).values();
|
||||
tm().load(domain.getReferencedContacts());
|
||||
// Registrars can only see a few fields on unauthorized domains.
|
||||
// This is a policy decision that is left up to us by the rfcs.
|
||||
DomainInfoData.Builder infoBuilder = DomainInfoData.newBuilder()
|
||||
.setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName())
|
||||
.setRepoId(domain.getRepoId())
|
||||
.setCurrentSponsorClientId(domain.getCurrentSponsorClientId())
|
||||
.setRegistrant(ofy().load().key(domain.getRegistrant()).now().getContactId());
|
||||
DomainInfoData.Builder infoBuilder =
|
||||
DomainInfoData.newBuilder()
|
||||
.setFullyQualifiedDomainName(domain.getFullyQualifiedDomainName())
|
||||
.setRepoId(domain.getRepoId())
|
||||
.setCurrentSponsorClientId(domain.getCurrentSponsorClientId())
|
||||
.setRegistrant(tm().load(domain.getRegistrant()).getContactId());
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid.
|
||||
if (clientId.equals(domain.getCurrentSponsorClientId()) || authInfo.isPresent()) {
|
||||
|
||||
@@ -60,6 +60,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
import google.registry.model.domain.DomainCommand.Update.AddRemove;
|
||||
@@ -256,7 +257,12 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.collect(toImmutableSet()))
|
||||
.addContacts(add.getContacts())
|
||||
.removeContacts(remove.getContacts())
|
||||
.setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant()))
|
||||
.setRegistrant(
|
||||
firstNonNull(
|
||||
change.getRegistrant() != null
|
||||
? VKey.createOfy(ContactResource.class, change.getRegistrant())
|
||||
: null,
|
||||
domain.getRegistrant()))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
return domainBuilder.build();
|
||||
}
|
||||
@@ -269,7 +275,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
|
||||
private void validateNewState(DomainBase newDomain) throws EppException {
|
||||
validateNoDuplicateContacts(newDomain.getContacts());
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant().getOfyKey(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.flows.FlowUtils.validateClientIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.failfastForAsyncDelete;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -82,17 +81,6 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject HostDeleteFlow() {}
|
||||
|
||||
/**
|
||||
* Hack to convert DomainBase's nameserver VKey's to Ofy Key's.
|
||||
*
|
||||
* <p>We currently need this because {@code failfastForAsyncDelete()} checks to see if a name is
|
||||
* in the ofy keys and is used for both nameservers and contacts. When we convert contacts to
|
||||
* VKey's, we can remove this and do the conversion in {@code failfastForAsyncDelete()}.
|
||||
*/
|
||||
private static ImmutableSet<Key<HostResource>> getNameserverOfyKeys(DomainBase domain) {
|
||||
return domain.getNameservers().stream().map(key -> key.getOfyKey()).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final EppResponse run() throws EppException {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
@@ -100,7 +88,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
validateClientIsLoggedIn(clientId);
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
failfastForAsyncDelete(targetId, now, HostResource.class, HostDeleteFlow::getNameserverOfyKeys);
|
||||
failfastForAsyncDelete(targetId, now, HostResource.class, DomainBase::getNameservers);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
if (!isSuperuser) {
|
||||
|
||||
@@ -70,9 +70,11 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
|
||||
/** The ID of the registrar that is currently sponsoring this resource. */
|
||||
@Index
|
||||
@Column(nullable = false)
|
||||
String currentSponsorClientId;
|
||||
|
||||
/** The ID of the registrar that created this resource. */
|
||||
@Column(nullable = false)
|
||||
String creationClientId;
|
||||
|
||||
/**
|
||||
@@ -88,7 +90,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
// Map the method to XML, not the field, because if we map the field (with an adaptor class) it
|
||||
// will never be omitted from the xml even if the timestamp inside creationTime is null and we
|
||||
// return null from the adaptor. (Instead it gets written as an empty tag.)
|
||||
@Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
@Column(nullable = false)
|
||||
@Index
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
/**
|
||||
* The time when this resource was or will be deleted.
|
||||
@@ -131,7 +135,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||
@Transient
|
||||
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
|
||||
|
||||
public final String getRepoId() {
|
||||
public String getRepoId() {
|
||||
return repoId;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
@@ -28,6 +29,8 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -37,7 +40,7 @@ import org.joda.time.DateTime;
|
||||
* as scoped on {@link EntityGroupRoot}.
|
||||
*/
|
||||
@Entity
|
||||
public class Cursor extends ImmutableObject {
|
||||
public class Cursor extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
/** The types of cursors, used as the string id field for each cursor in Datastore. */
|
||||
public enum CursorType {
|
||||
@@ -134,6 +137,11 @@ public class Cursor extends ImmutableObject {
|
||||
return CursorType.valueOf(String.join("_", id.subList(1, id.size())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // Cursors are not converted since they are ephemeral
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the type of the scoped object (or null) matches the required type for the specified
|
||||
* cursor (or null, if the cursor is a global cursor).
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
|
||||
package google.registry.model.common;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
|
||||
/**
|
||||
* The root key for the entity group which is known as the cross-tld entity group for historical
|
||||
@@ -34,7 +37,7 @@ import google.registry.model.BackupGroupRoot;
|
||||
* the entity group for the single namespace where global data applicable for all TLDs lived.
|
||||
*/
|
||||
@Entity
|
||||
public class EntityGroupRoot extends BackupGroupRoot {
|
||||
public class EntityGroupRoot extends BackupGroupRoot implements DatastoreEntity {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
@@ -44,4 +47,9 @@ public class EntityGroupRoot extends BackupGroupRoot {
|
||||
public static Key<EntityGroupRoot> getCrossTldKey() {
|
||||
return Key.create(EntityGroupRoot.class, "cross-tld");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@ package google.registry.model.contact;
|
||||
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import google.registry.model.eppcommon.Address;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* EPP Contact Address
|
||||
*
|
||||
* <p>This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its
|
||||
* address. The fields are all defined in parent class {@link Address}, but the subclass is still
|
||||
* necessary to pick up the contact namespace.
|
||||
* <p>This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its address.
|
||||
* The fields are all defined in parent class {@link Address}, but the subclass is still necessary
|
||||
* to pick up the contact namespace.
|
||||
*
|
||||
* <p>This does not implement {@code Overlayable} because it is intended to be bulk replaced on
|
||||
* update.
|
||||
@@ -30,6 +31,7 @@ import google.registry.model.eppcommon.Address;
|
||||
* @see PostalInfo
|
||||
*/
|
||||
@Embed
|
||||
@Embeddable
|
||||
public class ContactAddress extends Address {
|
||||
|
||||
/** Builder for {@link ContactAddress}. */
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/** A version of authInfo specifically for contacts. */
|
||||
@Embed
|
||||
@javax.persistence.Embeddable
|
||||
@XmlType(namespace = "urn:ietf:params:xml:ns:contact-1.0")
|
||||
public class ContactAuthInfo extends AuthInfo {
|
||||
public static ContactAuthInfo create(PasswordAuth pw) {
|
||||
|
||||
@@ -16,17 +16,19 @@ package google.registry.model.contact;
|
||||
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import google.registry.model.eppcommon.PhoneNumber;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* EPP Contact Phone Number
|
||||
*
|
||||
* <p>This class is embedded inside a {@link ContactResource} hold the phone number of an EPP
|
||||
* contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
|
||||
* contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
|
||||
* still necessary to pick up the contact namespace.
|
||||
*
|
||||
* @see ContactResource
|
||||
*/
|
||||
@Embed
|
||||
@Embeddable
|
||||
public class ContactPhoneNumber extends PhoneNumber {
|
||||
|
||||
/** Builder for {@link ContactPhoneNumber}. */
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.IgnoreSave;
|
||||
import com.googlecode.objectify.annotation.Index;
|
||||
@@ -30,9 +31,17 @@ import google.registry.model.annotations.ExternalMessagingName;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.contact.PostalInfo.Type;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.persistence.AttributeOverride;
|
||||
import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Transient;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -43,9 +52,20 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@javax.persistence.Entity(name = "Contact")
|
||||
@javax.persistence.Table(
|
||||
name = "Contact",
|
||||
indexes = {
|
||||
@javax.persistence.Index(columnList = "creationTime"),
|
||||
@javax.persistence.Index(columnList = "currentSponsorClientId"),
|
||||
@javax.persistence.Index(columnList = "deletionTime"),
|
||||
@javax.persistence.Index(columnList = "contactId", unique = true),
|
||||
@javax.persistence.Index(columnList = "searchName")
|
||||
})
|
||||
@ExternalMessagingName("contact")
|
||||
public class ContactResource extends EppResource implements
|
||||
ForeignKeyedEppResource, ResourceWithTransferData {
|
||||
@WithStringVKey
|
||||
public class ContactResource extends EppResource
|
||||
implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData {
|
||||
|
||||
/**
|
||||
* Unique identifier for this contact.
|
||||
@@ -61,13 +81,55 @@ public class ContactResource extends EppResource implements
|
||||
* US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}.
|
||||
*/
|
||||
@IgnoreSave(IfNull.class)
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "name", column = @Column(name = "addr_local_name")),
|
||||
@AttributeOverride(name = "org", column = @Column(name = "addr_local_org")),
|
||||
@AttributeOverride(name = "type", column = @Column(name = "addr_local_type")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine1",
|
||||
column = @Column(name = "addr_local_street_line1")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine2",
|
||||
column = @Column(name = "addr_local_street_line2")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine3",
|
||||
column = @Column(name = "addr_local_street_line3")),
|
||||
@AttributeOverride(name = "address.city", column = @Column(name = "addr_local_city")),
|
||||
@AttributeOverride(name = "address.state", column = @Column(name = "addr_local_state")),
|
||||
@AttributeOverride(name = "address.zip", column = @Column(name = "addr_local_zip")),
|
||||
@AttributeOverride(
|
||||
name = "address.countryCode",
|
||||
column = @Column(name = "addr_local_country_code"))
|
||||
})
|
||||
PostalInfo localizedPostalInfo;
|
||||
|
||||
/**
|
||||
* Internationalized postal info for the contact. Personal info; cleared by
|
||||
* {@link Builder#wipeOut}.
|
||||
* Internationalized postal info for the contact. Personal info; cleared by {@link
|
||||
* Builder#wipeOut}.
|
||||
*/
|
||||
@IgnoreSave(IfNull.class)
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "name", column = @Column(name = "addr_i18n_name")),
|
||||
@AttributeOverride(name = "org", column = @Column(name = "addr_i18n_org")),
|
||||
@AttributeOverride(name = "type", column = @Column(name = "addr_i18n_type")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine1",
|
||||
column = @Column(name = "addr_i18n_street_line1")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine2",
|
||||
column = @Column(name = "addr_i18n_street_line2")),
|
||||
@AttributeOverride(
|
||||
name = "address.streetLine3",
|
||||
column = @Column(name = "addr_i18n_street_line3")),
|
||||
@AttributeOverride(name = "address.city", column = @Column(name = "addr_i18n_city")),
|
||||
@AttributeOverride(name = "address.state", column = @Column(name = "addr_i18n_state")),
|
||||
@AttributeOverride(name = "address.zip", column = @Column(name = "addr_i18n_zip")),
|
||||
@AttributeOverride(
|
||||
name = "address.countryCode",
|
||||
column = @Column(name = "addr_i18n_country_code"))
|
||||
})
|
||||
PostalInfo internationalizedPostalInfo;
|
||||
|
||||
/**
|
||||
@@ -80,10 +142,20 @@ public class ContactResource extends EppResource implements
|
||||
|
||||
/** Contact’s voice number. Personal info; cleared by {@link Builder#wipeOut}. */
|
||||
@IgnoreSave(IfNull.class)
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "phoneNumber", column = @Column(name = "voice_phone_number")),
|
||||
@AttributeOverride(name = "extension", column = @Column(name = "voice_phone_extension")),
|
||||
})
|
||||
ContactPhoneNumber voice;
|
||||
|
||||
/** Contact’s fax number. Personal info; cleared by {@link Builder#wipeOut}. */
|
||||
@IgnoreSave(IfNull.class)
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "phoneNumber", column = @Column(name = "fax_phone_number")),
|
||||
@AttributeOverride(name = "extension", column = @Column(name = "fax_phone_extension")),
|
||||
})
|
||||
ContactPhoneNumber fax;
|
||||
|
||||
/** Contact’s email address. Personal info; cleared by {@link Builder#wipeOut}. */
|
||||
@@ -91,10 +163,16 @@ public class ContactResource extends EppResource implements
|
||||
String email;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the contact. */
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
|
||||
@AttributeOverride(name = "pw.repoId", column = @Column(name = "auth_info_repo_id")),
|
||||
})
|
||||
ContactAuthInfo authInfo;
|
||||
|
||||
/** Data about any pending or past transfers on this contact. */
|
||||
TransferData transferData;
|
||||
// TODO(b/153363295): Figure out how to persist transfer data
|
||||
@Transient TransferData transferData;
|
||||
|
||||
/**
|
||||
* The time that this resource was last transferred.
|
||||
@@ -107,8 +185,22 @@ public class ContactResource extends EppResource implements
|
||||
// the wipeOut() function, so that data is not kept around for deleted contacts.
|
||||
|
||||
/** Disclosure policy. */
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(name = "name", column = @Column(name = "disclose_types_name")),
|
||||
@AttributeOverride(name = "org", column = @Column(name = "disclose_types_org")),
|
||||
@AttributeOverride(name = "addr", column = @Column(name = "disclose_types_addr")),
|
||||
@AttributeOverride(name = "flag", column = @Column(name = "disclose_mode_flag")),
|
||||
@AttributeOverride(name = "voice.marked", column = @Column(name = "disclose_show_voice")),
|
||||
@AttributeOverride(name = "fax.marked", column = @Column(name = "disclose_show_fax")),
|
||||
@AttributeOverride(name = "email.marked", column = @Column(name = "disclose_show_email"))
|
||||
})
|
||||
Disclose disclose;
|
||||
|
||||
public VKey<ContactResource> createVKey() {
|
||||
return VKey.createOfy(ContactResource.class, Key.create(this));
|
||||
}
|
||||
|
||||
public String getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,14 @@ import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.eppcommon.PresenceMarker;
|
||||
import java.util.List;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
/** The "discloseType" from {@link "http://tools.ietf.org/html/rfc5733"}. */
|
||||
@Embed
|
||||
@Embeddable
|
||||
@XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"})
|
||||
public class Disclose extends ImmutableObject {
|
||||
|
||||
@@ -36,11 +39,11 @@ public class Disclose extends ImmutableObject {
|
||||
|
||||
List<PostalInfoChoice> addr;
|
||||
|
||||
PresenceMarker voice;
|
||||
@Embedded PresenceMarker voice;
|
||||
|
||||
PresenceMarker fax;
|
||||
@Embedded PresenceMarker fax;
|
||||
|
||||
PresenceMarker email;
|
||||
@Embedded PresenceMarker email;
|
||||
|
||||
@XmlAttribute
|
||||
Boolean flag;
|
||||
|
||||
@@ -21,6 +21,9 @@ import google.registry.model.Buildable;
|
||||
import google.registry.model.Buildable.Overlayable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlEnumValue;
|
||||
@@ -29,10 +32,11 @@ import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
/**
|
||||
* Implementation of both "postalInfoType" and "chgPostalInfoType" from
|
||||
* {@link "http://tools.ietf.org/html/rfc5733"}.
|
||||
* Implementation of both "postalInfoType" and "chgPostalInfoType" from {@link
|
||||
* "http://tools.ietf.org/html/rfc5733"}.
|
||||
*/
|
||||
@Embed
|
||||
@Embeddable
|
||||
@XmlType(propOrder = {"name", "org", "address", "type"})
|
||||
public class PostalInfo extends ImmutableObject implements Overlayable<PostalInfo> {
|
||||
|
||||
@@ -53,6 +57,7 @@ public class PostalInfo extends ImmutableObject implements Overlayable<PostalInf
|
||||
@XmlElement(name = "addr")
|
||||
ContactAddress address;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@XmlAttribute
|
||||
Type type;
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@ 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;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.xml.bind.annotation.XmlEnumValue;
|
||||
|
||||
@@ -36,6 +38,9 @@ import javax.xml.bind.annotation.XmlEnumValue;
|
||||
* situation with hosts where client-side renames would make that data stale. However, we sometimes
|
||||
* rename contacts internally ourselves, and it's easier to use the same model for both cases.
|
||||
*
|
||||
* <p>This entity type is not persisted in Cloud SQL. The different roles are represented as
|
||||
* separate fields in the Domain table.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5731#section-2.2">RFC 5731 - EPP Domain Name Mapping
|
||||
* - Contact and Client Identifiers</a>
|
||||
*/
|
||||
@@ -58,22 +63,28 @@ public class DesignatedContact extends ImmutableObject {
|
||||
REGISTRANT
|
||||
}
|
||||
|
||||
public static DesignatedContact create(Type type, Key<ContactResource> contact) {
|
||||
public static DesignatedContact create(Type type, VKey<ContactResource> contact) {
|
||||
DesignatedContact instance = new DesignatedContact();
|
||||
instance.type = type;
|
||||
instance.contact = checkArgumentNotNull(contact, "Must specify contact key");
|
||||
instance.contactVKey = checkArgumentNotNull(contact, "Must specify contact key");
|
||||
instance.contact = contact.maybeGetOfyKey().orElse(null);
|
||||
return instance;
|
||||
}
|
||||
|
||||
Type type;
|
||||
|
||||
@Index Key<ContactResource> contact;
|
||||
@Ignore VKey<ContactResource> contactVKey;
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Key<ContactResource> getContactKey() {
|
||||
return contact;
|
||||
public VKey<ContactResource> getContactKey() {
|
||||
return contactVKey;
|
||||
}
|
||||
|
||||
public DesignatedContact reconstitute() {
|
||||
return create(type, VKey.createOfy(ContactResource.class, contact));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import google.registry.model.registry.Registry;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
@@ -78,6 +79,8 @@ import javax.persistence.AttributeOverrides;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.Transient;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Interval;
|
||||
@@ -105,7 +108,7 @@ import org.joda.time.Interval;
|
||||
})
|
||||
@ExternalMessagingName("domain")
|
||||
public class DomainBase extends EppResource
|
||||
implements ForeignKeyedEppResource, ResourceWithTransferData {
|
||||
implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData {
|
||||
|
||||
/** The max number of years that a domain can be registered for, as set by ICANN policy. */
|
||||
public static final int MAX_REGISTRATION_YEARS = 10;
|
||||
@@ -141,7 +144,10 @@ public class DomainBase extends EppResource
|
||||
*/
|
||||
@Index @ElementCollection @Transient Set<Key<HostResource>> nsHosts;
|
||||
|
||||
@Ignore @Transient Set<VKey<HostResource>> nsHostVKeys;
|
||||
@Ignore
|
||||
@ElementCollection
|
||||
@JoinTable(name = "DomainHost")
|
||||
Set<VKey<HostResource>> nsHostVKeys;
|
||||
|
||||
/**
|
||||
* The union of the contacts visible via {@link #getContacts} and {@link #getRegistrant}.
|
||||
@@ -150,6 +156,17 @@ public class DomainBase extends EppResource
|
||||
*/
|
||||
@Transient Set<DesignatedContact> allContacts;
|
||||
|
||||
/**
|
||||
* Contacts as they are stored in cloud SQL.
|
||||
*
|
||||
* <p>This information is duplicated in allContacts, and must be kept in sync with it.
|
||||
*/
|
||||
@Ignore VKey<ContactResource> adminContact;
|
||||
|
||||
@Ignore VKey<ContactResource> billingContact;
|
||||
@Ignore VKey<ContactResource> techContact;
|
||||
@Ignore VKey<ContactResource> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@AttributeOverrides({
|
||||
@@ -256,6 +273,34 @@ public class DomainBase extends EppResource
|
||||
nullToEmptyImmutableCopy(nsHosts).stream()
|
||||
.map(hostKey -> VKey.createOfy(HostResource.class, hostKey))
|
||||
.collect(toImmutableSet());
|
||||
|
||||
// Reconstitute all of the contacts so that they have VKeys.
|
||||
allContacts =
|
||||
allContacts.stream().map(contact -> contact.reconstitute()).collect(toImmutableSet());
|
||||
setContactFields(allContacts, true);
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
// Reconstitute the contact list.
|
||||
ImmutableSet.Builder<DesignatedContact> contactsBuilder =
|
||||
new ImmutableSet.Builder<DesignatedContact>();
|
||||
|
||||
if (registrantContact != null) {
|
||||
contactsBuilder.add(
|
||||
DesignatedContact.create(DesignatedContact.Type.REGISTRANT, registrantContact));
|
||||
}
|
||||
if (billingContact != null) {
|
||||
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.BILLING, billingContact));
|
||||
}
|
||||
if (techContact != null) {
|
||||
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.TECH, techContact));
|
||||
}
|
||||
if (adminContact != null) {
|
||||
contactsBuilder.add(DesignatedContact.create(DesignatedContact.Type.ADMIN, adminContact));
|
||||
}
|
||||
|
||||
allContacts = contactsBuilder.build();
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getSubordinateHosts() {
|
||||
@@ -510,13 +555,20 @@ public class DomainBase extends EppResource
|
||||
}
|
||||
|
||||
/** A key to the registrant who registered this domain. */
|
||||
public Key<ContactResource> getRegistrant() {
|
||||
return nullToEmpty(allContacts)
|
||||
.stream()
|
||||
.filter(IS_REGISTRANT)
|
||||
.findFirst()
|
||||
.get()
|
||||
.getContactKey();
|
||||
public VKey<ContactResource> getRegistrant() {
|
||||
return registrantContact;
|
||||
}
|
||||
|
||||
public VKey<ContactResource> getAdminContact() {
|
||||
return adminContact;
|
||||
}
|
||||
|
||||
public VKey<ContactResource> getBillingContact() {
|
||||
return billingContact;
|
||||
}
|
||||
|
||||
public VKey<ContactResource> getTechContact() {
|
||||
return techContact;
|
||||
}
|
||||
|
||||
/** Associated contacts for the domain (other than registrant). */
|
||||
@@ -532,7 +584,7 @@ public class DomainBase extends EppResource
|
||||
}
|
||||
|
||||
/** Returns all referenced contacts from this domain or application. */
|
||||
public ImmutableSet<Key<ContactResource>> getReferencedContacts() {
|
||||
public ImmutableSet<VKey<ContactResource>> getReferencedContacts() {
|
||||
return nullToEmptyImmutableCopy(allContacts)
|
||||
.stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
@@ -544,6 +596,37 @@ public class DomainBase extends EppResource
|
||||
return tld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the individual contact fields from {@code contacts}.
|
||||
*
|
||||
* <p>The registrant field is only set if {@code includeRegistrant} is true, as this field needs
|
||||
* to be set in some circumstances but not in others.
|
||||
*/
|
||||
private void setContactFields(Set<DesignatedContact> contacts, boolean includeRegistrant) {
|
||||
|
||||
// Set the individual contact fields.
|
||||
for (DesignatedContact contact : contacts) {
|
||||
switch (contact.getType()) {
|
||||
case BILLING:
|
||||
billingContact = contact.getContactKey();
|
||||
break;
|
||||
case TECH:
|
||||
techContact = contact.getContactKey();
|
||||
break;
|
||||
case ADMIN:
|
||||
adminContact = contact.getContactKey();
|
||||
break;
|
||||
case REGISTRANT:
|
||||
if (includeRegistrant) {
|
||||
registrantContact = contact.getContactKey();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown contact resource type: " + contact.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
|
||||
private static final Predicate<DesignatedContact> IS_REGISTRANT =
|
||||
(DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type);
|
||||
@@ -588,7 +671,11 @@ public class DomainBase extends EppResource
|
||||
|
||||
checkArgumentNotNull(
|
||||
emptyToNull(instance.fullyQualifiedDomainName), "Missing fullyQualifiedDomainName");
|
||||
checkArgument(instance.allContacts.stream().anyMatch(IS_REGISTRANT), "Missing registrant");
|
||||
if (instance.getRegistrant() == null
|
||||
&& instance.allContacts.stream().anyMatch(IS_REGISTRANT)) {
|
||||
throw new IllegalArgumentException("registrant is null but is in allContacts");
|
||||
}
|
||||
checkArgumentNotNull(instance.getRegistrant(), "Missing registrant");
|
||||
instance.tld = getTldFromDomainName(instance.fullyQualifiedDomainName);
|
||||
return super.build();
|
||||
}
|
||||
@@ -606,11 +693,14 @@ public class DomainBase extends EppResource
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public Builder setRegistrant(Key<ContactResource> registrant) {
|
||||
public Builder setRegistrant(VKey<ContactResource> registrant) {
|
||||
// Replace the registrant contact inside allContacts.
|
||||
getInstance().allContacts = union(
|
||||
getInstance().getContacts(),
|
||||
DesignatedContact.create(Type.REGISTRANT, checkArgumentNotNull(registrant)));
|
||||
|
||||
// Set the registrant field specifically.
|
||||
getInstance().registrantContact = registrant;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
@@ -668,12 +758,16 @@ public class DomainBase extends EppResource
|
||||
|
||||
public Builder setContacts(ImmutableSet<DesignatedContact> contacts) {
|
||||
checkArgument(contacts.stream().noneMatch(IS_REGISTRANT), "Registrant cannot be a contact");
|
||||
|
||||
// Replace the non-registrant contacts inside allContacts.
|
||||
getInstance().allContacts =
|
||||
Streams.concat(
|
||||
nullToEmpty(getInstance().allContacts).stream().filter(IS_REGISTRANT),
|
||||
contacts.stream())
|
||||
.collect(toImmutableSet());
|
||||
|
||||
// Set the individual fields.
|
||||
getInstance().setContactFields(contacts, false);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import google.registry.model.eppinput.ResourceCommand.ResourceUpdate;
|
||||
import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -188,7 +189,7 @@ public class DomainCommand {
|
||||
now);
|
||||
for (DesignatedContact contact : contacts) {
|
||||
if (DesignatedContact.Type.REGISTRANT.equals(contact.getType())) {
|
||||
clone.registrant = contact.getContactKey();
|
||||
clone.registrant = contact.getContactKey().getOfyKey();
|
||||
clone.contacts = forceEmptyToNull(difference(contacts, contact));
|
||||
break;
|
||||
}
|
||||
@@ -439,8 +440,10 @@ public class DomainCommand {
|
||||
loadByForeignKeysCached(foreignKeys.build(), ContactResource.class, now);
|
||||
ImmutableSet.Builder<DesignatedContact> linkedContacts = new ImmutableSet.Builder<>();
|
||||
for (ForeignKeyedDesignatedContact contact : contacts) {
|
||||
linkedContacts.add(DesignatedContact.create(
|
||||
contact.type, loadedContacts.get(contact.contactId)));
|
||||
linkedContacts.add(
|
||||
DesignatedContact.create(
|
||||
contact.type,
|
||||
VKey.createOfy(ContactResource.class, loadedContacts.get(contact.contactId))));
|
||||
}
|
||||
return linkedContacts.build();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
@@ -31,17 +33,21 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
* "e164Type" type from {@link "http://tools.ietf.org/html/draft-lozano-tmch-smd"}.
|
||||
*
|
||||
* <blockquote>
|
||||
*
|
||||
* <p>"Contact telephone number structure is derived from structures defined in [ITU.E164.2005].
|
||||
* Telephone numbers described in this mapping are character strings that MUST begin with a plus
|
||||
* sign ("+", ASCII value 0x002B), followed by a country code defined in [ITU.E164.2005], followed
|
||||
* by a dot (".", ASCII value 0x002E), followed by a sequence of digits representing the telephone
|
||||
* number. An optional "x" attribute is provided to note telephone extension information."
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @see google.registry.model.contact.ContactPhoneNumber
|
||||
* @see google.registry.model.mark.MarkPhoneNumber
|
||||
*/
|
||||
@XmlTransient
|
||||
@Embeddable
|
||||
@MappedSuperclass
|
||||
public class PhoneNumber extends ImmutableObject {
|
||||
|
||||
@XmlValue
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model.eppcommon;
|
||||
import com.googlecode.objectify.annotation.Embed;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,7 @@ import javax.xml.bind.annotation.XmlTransient;
|
||||
* {@code <foo></foo>}, and will unmarshal always to {@code <foo/>}.
|
||||
*/
|
||||
@Embed
|
||||
@Embeddable
|
||||
public class PresenceMarker extends ImmutableObject implements Serializable {
|
||||
@XmlTransient
|
||||
boolean marked = true;
|
||||
|
||||
@@ -34,24 +34,30 @@ import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithStringVKey;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.ElementCollection;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A persistable Host resource including mutable and non-mutable fields.
|
||||
*
|
||||
* <p>A host's {@link TransferData} is stored on the superordinate domain. Non-subordinate hosts
|
||||
* <p>A host's {@link TransferData} is stored on the superordinate domain. Non-subordinate hosts
|
||||
* don't carry a full set of TransferData; all they have is lastTransferTime.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5732">RFC 5732</a>
|
||||
*/
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@javax.persistence.Entity
|
||||
@ExternalMessagingName("host")
|
||||
public class HostResource extends EppResource implements ForeignKeyedEppResource {
|
||||
@WithStringVKey
|
||||
public class HostResource extends EppResource
|
||||
implements DatastoreAndSqlEntity, ForeignKeyedEppResource {
|
||||
|
||||
/**
|
||||
* Fully qualified hostname, which is a unique identifier for this host.
|
||||
@@ -64,8 +70,7 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource
|
||||
String fullyQualifiedHostName;
|
||||
|
||||
/** IP Addresses for this host. Can be null if this is an external host. */
|
||||
@Index
|
||||
Set<InetAddress> inetAddresses;
|
||||
@Index @ElementCollection Set<InetAddress> inetAddresses;
|
||||
|
||||
/** The superordinate domain of this host, or null if this is an external host. */
|
||||
@Index
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ContiguousSet;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Range;
|
||||
@@ -33,6 +34,8 @@ import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
@@ -51,7 +54,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.COMMIT_LOGS)
|
||||
public class CommitLogBucket extends ImmutableObject implements Buildable {
|
||||
public class CommitLogBucket extends ImmutableObject implements Buildable, DatastoreEntity {
|
||||
|
||||
/**
|
||||
* Ranges from 1 to {@link RegistryConfig#getCommitLogBucketCount()}, inclusive; starts at 1 since
|
||||
@@ -70,6 +73,11 @@ public class CommitLogBucket extends ImmutableObject implements Buildable {
|
||||
return lastWrittenTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the specified bucket ID.
|
||||
*
|
||||
|
||||
@@ -27,6 +27,8 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -44,7 +46,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.COMMIT_LOGS)
|
||||
public class CommitLogCheckpoint extends ImmutableObject {
|
||||
public class CommitLogCheckpoint extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
/** Shared singleton parent entity for commit log checkpoints. */
|
||||
@Parent
|
||||
@@ -71,6 +73,11 @@ public class CommitLogCheckpoint extends ImmutableObject {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CommitLogCheckpoint for the given wall time and bucket checkpoint times, specified as
|
||||
* a map from bucket ID to bucket commit timestamp.
|
||||
|
||||
@@ -17,12 +17,15 @@ package google.registry.model.ofy;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -30,7 +33,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.COMMIT_LOGS)
|
||||
public class CommitLogCheckpointRoot extends ImmutableObject {
|
||||
public class CommitLogCheckpointRoot extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
public static final long SINGLETON_ID = 1; // There is always exactly one of these.
|
||||
|
||||
@@ -49,6 +52,11 @@ public class CommitLogCheckpointRoot extends ImmutableObject {
|
||||
return lastWrittenTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
public static CommitLogCheckpointRoot loadRoot() {
|
||||
CommitLogCheckpointRoot root = ofy().load().key(getKey()).now();
|
||||
return root == null ? new CommitLogCheckpointRoot() : root;
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model.ofy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
@@ -25,6 +26,8 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -38,7 +41,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.COMMIT_LOGS)
|
||||
public class CommitLogManifest extends ImmutableObject {
|
||||
public class CommitLogManifest extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
/** Commit log manifests are parented on a random bucket. */
|
||||
@Parent
|
||||
@@ -67,6 +70,11 @@ public class CommitLogManifest extends ImmutableObject {
|
||||
return nullToEmptyImmutableCopy(deletions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
public static CommitLogManifest create(
|
||||
Key<CommitLogBucket> parent, DateTime commitTime, Set<Key<?>> deletions) {
|
||||
CommitLogManifest instance = new CommitLogManifest();
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.appengine.api.datastore.KeyFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
@@ -28,11 +29,13 @@ import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
|
||||
/** Representation of a saved entity in a {@link CommitLogManifest} (not deletes). */
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.COMMIT_LOGS)
|
||||
public class CommitLogMutation extends ImmutableObject {
|
||||
public class CommitLogMutation extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
/** The manifest this belongs to. */
|
||||
@Parent
|
||||
@@ -58,6 +61,11 @@ public class CommitLogMutation extends ImmutableObject {
|
||||
return createFromPbBytes(entityProtoBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // not persisted in SQL
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new mutation entity created from an @Entity ImmutableObject instance.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.model.ofy;
|
||||
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -22,6 +23,7 @@ import com.googlecode.objectify.Key;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.StreamSupport;
|
||||
@@ -93,42 +95,45 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
|
||||
@Override
|
||||
public void saveNew(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
getOfy().save().entity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAllNew(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
getOfy().save().entities(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdate(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
getOfy().save().entity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNewOrUpdateAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
getOfy().save().entities(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
getOfy().save().entity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
getOfy().save().entities(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkExists(Object entity) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
return getOfy().load().key(Key.create(entity)).now() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean checkExists(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
return getOfy().load().key(key.getOfyKey()).now() != null;
|
||||
}
|
||||
|
||||
// TODO: add tests for these methods. They currently have some degree of test coverage because
|
||||
@@ -136,8 +141,17 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
// VKey instead of by ofy Key. But ideally, there should be one set of TransactionManager
|
||||
// interface tests that are applied to both the datastore and SQL implementations.
|
||||
@Override
|
||||
public <T> Optional<T> load(VKey<T> key) {
|
||||
return Optional.of(getOfy().load().key(key.getOfyKey()).now());
|
||||
public <T> Optional<T> maybeLoad(VKey<T> key) {
|
||||
return Optional.ofNullable(getOfy().load().key(key.getOfyKey()).now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T load(VKey<T> key) {
|
||||
T result = getOfy().load().key(key.getOfyKey()).now();
|
||||
if (result == null) {
|
||||
throw new NoSuchElementException(key.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,16 +165,12 @@ public class DatastoreTransactionManager implements TransactionManager {
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAll(Class<T> clazz) {
|
||||
// We can do a ofy().load().type(clazz), but this doesn't work in a transaction.
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int delete(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
|
||||
public <T> void delete(VKey<T> key) {
|
||||
getOfy().delete().key(key.getOfyKey()).now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFac
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.EntityClasses;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
|
||||
import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
|
||||
@@ -130,6 +131,7 @@ public class ObjectifyService {
|
||||
new InetAddressTranslatorFactory(),
|
||||
new MoneyStringTranslatorFactory(),
|
||||
new ReadableInstantUtcTranslatorFactory(),
|
||||
new VKeyTranslatorFactory<ContactResource>(ContactResource.class),
|
||||
new VKeyTranslatorFactory<HostResource>(HostResource.class),
|
||||
new UpdateAutoTimestampTranslatorFactory())) {
|
||||
factory().getTranslators().add(translatorFactory);
|
||||
@@ -151,11 +153,14 @@ public class ObjectifyService {
|
||||
String kind = Key.getKind(clazz);
|
||||
boolean registered = factory().getMetadata(kind) != null;
|
||||
if (clazz.isAnnotationPresent(Entity.class)) {
|
||||
// Objectify silently ignores re-registrations for a given kind string, even if the classes
|
||||
// being registered are distinct. Throw an exception if that would happen here.
|
||||
checkState(!registered,
|
||||
// Objectify silently replaces current registration for a given kind string when a different
|
||||
// class is registered again for this kind. For simplicity's sake, throw an exception on any
|
||||
// re-registration.
|
||||
checkState(
|
||||
!registered,
|
||||
"Kind '%s' already registered, cannot register new @Entity %s",
|
||||
kind, clazz.getCanonicalName());
|
||||
kind,
|
||||
clazz.getCanonicalName());
|
||||
} else if (clazz.isAnnotationPresent(EntitySubclass.class)) {
|
||||
// Ensure that any @EntitySubclass classes have also had their parent @Entity registered,
|
||||
// which Objectify nominally requires but doesn't enforce in 4.x (though it may in 5.x).
|
||||
|
||||
@@ -73,6 +73,7 @@ import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.registrar.Registrar.BillingAccountEntry.CurrencyMapper;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.Comparator;
|
||||
@@ -107,11 +108,12 @@ import org.joda.time.DateTime;
|
||||
columnList = "ianaIdentifier",
|
||||
name = "registrar_iana_identifier_idx"),
|
||||
})
|
||||
public class Registrar extends ImmutableObject implements Buildable, Jsonifiable {
|
||||
public class Registrar extends ImmutableObject
|
||||
implements Buildable, DatastoreAndSqlEntity, Jsonifiable {
|
||||
|
||||
/** Represents the type of a registrar entity. */
|
||||
public enum Type {
|
||||
/** A real-world, third-party registrar. Should have non-null IANA and billing IDs. */
|
||||
/** A real-world, third-party registrar. Should have non-null IANA and billing IDs. */
|
||||
REAL(Objects::nonNull),
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,7 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.schema.replay.DatastoreAndSqlEntity;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -68,7 +69,8 @@ import javax.persistence.Transient;
|
||||
@javax.persistence.Index(columnList = "gaeUserId", name = "registrarpoc_gae_user_id_idx")
|
||||
})
|
||||
// TODO(shicong): Rename the class name to RegistrarPoc after database migration
|
||||
public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
||||
public class RegistrarContact extends ImmutableObject
|
||||
implements DatastoreAndSqlEntity, Jsonifiable {
|
||||
|
||||
@Parent @Transient Key<Registrar> parent;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.hash.BloomFilter;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.googlecode.objectify.Key;
|
||||
@@ -41,6 +42,8 @@ import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -53,26 +56,28 @@ import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* A premium list entity, persisted to Datastore, that is used to check domain label prices.
|
||||
*/
|
||||
/** A premium list entity, persisted to Datastore, that is used to check domain label prices. */
|
||||
@ReportedOn
|
||||
@Entity
|
||||
public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.PremiumListEntry> {
|
||||
public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.PremiumListEntry>
|
||||
implements DatastoreEntity {
|
||||
|
||||
/** Stores the revision key for the set of currently used premium list entry entities. */
|
||||
Key<PremiumListRevision> revisionKey;
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // PremiumList is dual-written
|
||||
}
|
||||
|
||||
/** Virtual parent entity for premium list entry entities associated with a single revision. */
|
||||
@ReportedOn
|
||||
@Entity
|
||||
public static class PremiumListRevision extends ImmutableObject {
|
||||
|
||||
@Parent
|
||||
Key<PremiumList> parent;
|
||||
@Parent Key<PremiumList> parent;
|
||||
|
||||
@Id
|
||||
long revisionId;
|
||||
@Id long revisionId;
|
||||
|
||||
/**
|
||||
* A Bloom filter that is used to determine efficiently and quickly whether a label might be
|
||||
@@ -249,7 +254,7 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
@ReportedOn
|
||||
@Entity
|
||||
public static class PremiumListEntry extends DomainLabelEntry<Money, PremiumListEntry>
|
||||
implements Buildable {
|
||||
implements Buildable, DatastoreEntity {
|
||||
|
||||
@Parent
|
||||
Key<PremiumListRevision> parent;
|
||||
@@ -266,6 +271,11 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link PremiumListEntry} objects, since they are immutable. */
|
||||
public static class Builder extends DomainLabelEntry.Builder<PremiumListEntry, Builder> {
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.MapDifference;
|
||||
@@ -45,6 +46,8 @@ import com.googlecode.objectify.mapper.Mapper;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.DomainLabelMetrics.MetricsReservedListMatch;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.schema.tld.ReservedList.ReservedEntry;
|
||||
import google.registry.schema.tld.ReservedListDao;
|
||||
import java.util.List;
|
||||
@@ -59,7 +62,8 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
public final class ReservedList
|
||||
extends BaseDomainLabelList<ReservationType, ReservedList.ReservedListEntry> {
|
||||
extends BaseDomainLabelList<ReservationType, ReservedList.ReservedListEntry> implements
|
||||
DatastoreEntity {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -68,6 +72,11 @@ public final class ReservedList
|
||||
|
||||
boolean shouldPublish = true;
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // ReservedList is dual-written
|
||||
}
|
||||
|
||||
/**
|
||||
* A reserved list entry entity, persisted to Datastore, that represents a single label and its
|
||||
* reservation type.
|
||||
|
||||
@@ -16,20 +16,21 @@ package google.registry.model.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
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.schema.server.LockDao;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.Serializable;
|
||||
@@ -49,7 +50,7 @@ import org.joda.time.Duration;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.TRANSIENT)
|
||||
public class Lock extends ImmutableObject implements Serializable {
|
||||
public class Lock extends ImmutableObject implements DatastoreEntity, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 756397280691684645L;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
@@ -197,22 +198,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
|
||||
// Checking if an unexpired lock still exists - if so, the lock can't be acquired.
|
||||
Lock lock = ofy().load().type(Lock.class).id(lockId).now();
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<google.registry.schema.server.Lock> cloudSqlLockOptional;
|
||||
if (tld == null) {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName);
|
||||
} else {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName, tld);
|
||||
}
|
||||
LockDao.compare(Optional.ofNullable(lock), cloudSqlLockOptional);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Issue loading and comparing lock from Cloud SQL");
|
||||
}
|
||||
if (lock != null) {
|
||||
logger.atInfo().log(
|
||||
"Loaded existing lock: %s for request: %s", lock.lockId, lock.requestLogId);
|
||||
@@ -237,35 +222,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// don't need to be backed up.
|
||||
ofy().saveWithoutBackup().entity(newLock);
|
||||
|
||||
// create and save the lock to Cloud SQL
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
google.registry.schema.server.Lock cloudSqlLock;
|
||||
if (tld == null) {
|
||||
cloudSqlLock =
|
||||
google.registry.schema.server.Lock.createGlobal(
|
||||
resourceName,
|
||||
requestStatusChecker.getLogId(),
|
||||
now,
|
||||
leaseLength);
|
||||
} else {
|
||||
cloudSqlLock =
|
||||
google.registry.schema.server.Lock.create(
|
||||
resourceName,
|
||||
tld,
|
||||
requestStatusChecker.getLogId(),
|
||||
now,
|
||||
leaseLength);
|
||||
}
|
||||
LockDao.save(cloudSqlLock);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error saving lock to Cloud SQL: %s", newLock);
|
||||
}
|
||||
|
||||
return AcquireResult.create(now, lock, newLock, lockState);
|
||||
});
|
||||
|
||||
@@ -284,44 +240,12 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// this can happen if release() is called around the expiration time and the lock
|
||||
// expires underneath us.
|
||||
Lock loadedLock = ofy().load().type(Lock.class).id(lockId).now();
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<google.registry.schema.server.Lock> cloudSqlLockOptional;
|
||||
if (tld == null) {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName);
|
||||
} else {
|
||||
cloudSqlLockOptional = LockDao.load(resourceName, tld);
|
||||
}
|
||||
LockDao.compare(Optional.ofNullable(loadedLock), cloudSqlLockOptional);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Issue loading and comparing lock from Cloud SQL");
|
||||
}
|
||||
if (Lock.this.equals(loadedLock)) {
|
||||
// Use noBackupOfy() so that we don't create a commit log entry for deleting the
|
||||
// lock.
|
||||
logger.atInfo().log("Deleting lock: %s", lockId);
|
||||
ofy().deleteWithoutBackup().entity(Lock.this);
|
||||
|
||||
// Remove the lock from Cloud SQL
|
||||
try {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
if (tld == null) {
|
||||
LockDao.delete(resourceName);
|
||||
} else {
|
||||
LockDao.delete(resourceName, tld);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error deleting lock from Cloud SQL: %s", loadedLock);
|
||||
}
|
||||
|
||||
lockMetrics.recordRelease(
|
||||
resourceName, tld, new Duration(acquiredTime, tm().getTransactionTime()));
|
||||
} else {
|
||||
@@ -335,4 +259,9 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // Locks are not converted since they are ephemeral
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.MapDifference;
|
||||
import com.google.common.collect.MapDifference.ValueDifference;
|
||||
@@ -44,6 +45,8 @@ import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.model.annotations.VirtualEntity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.schema.tmch.ClaimsList;
|
||||
import google.registry.schema.tmch.ClaimsListDao;
|
||||
import google.registry.util.CollectionUtils;
|
||||
@@ -75,7 +78,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
|
||||
public class ClaimsListShard extends ImmutableObject {
|
||||
public class ClaimsListShard extends ImmutableObject implements DatastoreEntity {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -289,10 +292,15 @@ public class ClaimsListShard extends ImmutableObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // ClaimsLists are dually written
|
||||
}
|
||||
|
||||
/** Virtual parent entity for claims list shards of a specific revision. */
|
||||
@Entity
|
||||
@VirtualEntity
|
||||
public static class ClaimsListRevision extends ImmutableObject {
|
||||
public static class ClaimsListRevision extends ImmutableObject implements DatastoreEntity {
|
||||
@Parent
|
||||
Key<ClaimsListSingleton> parent;
|
||||
|
||||
@@ -311,6 +319,11 @@ public class ClaimsListShard extends ImmutableObject {
|
||||
public static Key<ClaimsListRevision> createKey() {
|
||||
return createKey(new ClaimsListSingleton());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // ClaimsLists are dually written
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +332,7 @@ public class ClaimsListShard extends ImmutableObject {
|
||||
*/
|
||||
@Entity
|
||||
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
|
||||
public static class ClaimsListSingleton extends CrossTldSingleton {
|
||||
public static class ClaimsListSingleton extends CrossTldSingleton implements DatastoreEntity {
|
||||
Key<ClaimsListRevision> activeRevision;
|
||||
|
||||
static ClaimsListSingleton create(Key<ClaimsListRevision> revision) {
|
||||
@@ -332,6 +345,11 @@ public class ClaimsListShard extends ImmutableObject {
|
||||
public void setActiveRevision(Key<ClaimsListRevision> revision) {
|
||||
activeRevision = revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(); // ClaimsLists are dually written
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
@@ -31,6 +32,7 @@ import javax.persistence.PostUpdate;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.PreRemove;
|
||||
import javax.persistence.PreUpdate;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/**
|
||||
* A listener class to invoke entity callbacks in cases where Hibernate doesn't invoke the callback
|
||||
@@ -167,10 +169,12 @@ public class EntityCallbacksListener {
|
||||
|
||||
private Stream<Object> findEmbeddedProperties(Object object, Class<?> clazz) {
|
||||
return Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(field -> !field.isAnnotationPresent(Transient.class))
|
||||
.filter(
|
||||
field ->
|
||||
field.isAnnotationPresent(Embedded.class)
|
||||
|| field.getType().isAnnotationPresent(Embeddable.class))
|
||||
.filter(field -> !Modifier.isStatic(field.getModifiers()))
|
||||
.map(field -> getFieldObject(field, object))
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.persistence;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import java.util.Optional;
|
||||
@@ -41,20 +42,31 @@ public class VKey<T> extends ImmutableObject {
|
||||
this.primaryKey = primaryKey;
|
||||
}
|
||||
|
||||
public static <T> VKey<T> createSql(Class<? extends T> kind, Object primaryKey) {
|
||||
return new VKey(kind, null, primaryKey);
|
||||
/** Creates a {@link VKey} which only contains the sql primary key. */
|
||||
public static <T> VKey<T> createSql(Class<? extends T> kind, Object sqlKey) {
|
||||
checkArgumentNotNull(kind, "kind must not be null");
|
||||
checkArgumentNotNull(sqlKey, "sqlKey must not be null");
|
||||
return new VKey(kind, null, sqlKey);
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} which only contains the ofy primary key. */
|
||||
public static <T> VKey<T> createOfy(
|
||||
Class<? extends T> kind, com.googlecode.objectify.Key<T> ofyKey) {
|
||||
Class<? extends T> kind, com.googlecode.objectify.Key<? extends T> ofyKey) {
|
||||
checkArgumentNotNull(kind, "kind must not be null");
|
||||
checkArgumentNotNull(ofyKey, "ofyKey must not be null");
|
||||
return new VKey(kind, ofyKey, null);
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} which only contains both sql and ofy primary key. */
|
||||
public static <T> VKey<T> create(
|
||||
Class<? extends T> kind, Object primaryKey, com.googlecode.objectify.Key ofyKey) {
|
||||
return new VKey(kind, ofyKey, primaryKey);
|
||||
Class<? extends T> kind, Object sqlKey, com.googlecode.objectify.Key ofyKey) {
|
||||
checkArgumentNotNull(kind, "kind must not be null");
|
||||
checkArgumentNotNull(sqlKey, "sqlKey must not be null");
|
||||
checkArgumentNotNull(ofyKey, "ofyKey must not be null");
|
||||
return new VKey(kind, ofyKey, sqlKey);
|
||||
}
|
||||
|
||||
/** Returns the type of the entity. */
|
||||
public Class<? extends T> getKind() {
|
||||
return this.kind;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is long type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithLongVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* LongVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Annotation for {@link Entity} which id is string type and needs an {@link AttributeConverter} for
|
||||
* its VKey.
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface WithStringVKey {
|
||||
/**
|
||||
* Sets the suffix of the class name for the {@link AttributeConverter} generated by
|
||||
* StringVKeyProcessor. If not set, the suffix will be the type name of the VKey. Note that the
|
||||
* class name will be "VKeyConverter_" concatenated with the suffix.
|
||||
*/
|
||||
String classNameSuffix() default "";
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import avro.shaded.com.google.common.collect.Maps;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.registry.Registry.BillingCostTransition;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Converter;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* JPA converter for storing/retrieving {@link TimedTransitionProperty <Money, BillingCostTransition
|
||||
* >} objects.
|
||||
*/
|
||||
@Converter(autoApply = true)
|
||||
public class BillingCostTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<Money, BillingCostTransition> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, BillingCostTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, Money> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(DateTime.parse(entry.getKey()), Money.parse(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<BillingCostTransition> getTimedTransitionSubclass() {
|
||||
return BillingCostTransition.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import google.registry.model.contact.Disclose.PostalInfoChoice;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
import java.util.List;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link List < PostalInfoChoice >}. */
|
||||
@Converter(autoApply = true)
|
||||
public class PostalInfoChoiceListConverter extends StringListConverterBase<PostalInfoChoice> {
|
||||
|
||||
@Override
|
||||
String toString(PostalInfoChoice element) {
|
||||
return element.getType().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
PostalInfoChoice fromString(String value) {
|
||||
return PostalInfoChoice.create(PostalInfo.Type.valueOf(value));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.persistence.converter.StringMapDescriptor.StringMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Base JPA converter for {@link TimedTransitionProperty} objects that are stored in a column with
|
||||
* data type of hstore in the database.
|
||||
*/
|
||||
public abstract class TimedTransitionPropertyConverterBase<K, V extends TimedTransition<K>>
|
||||
implements AttributeConverter<TimedTransitionProperty<K, V>, StringMap> {
|
||||
|
||||
abstract Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<DateTime, V> entry);
|
||||
|
||||
abstract Map.Entry<DateTime, K> convertToEntityMapEntry(Map.Entry<String, String> entry);
|
||||
|
||||
abstract Class<V> getTimedTransitionSubclass();
|
||||
|
||||
@Override
|
||||
public StringMap convertToDatabaseColumn(@Nullable TimedTransitionProperty<K, V> attribute) {
|
||||
return attribute == null
|
||||
? null
|
||||
: StringMap.create(
|
||||
attribute.entrySet().stream()
|
||||
.map(this::convertToDatabaseMapEntry)
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedTransitionProperty<K, V> convertToEntityAttribute(@Nullable StringMap dbData) {
|
||||
if (dbData == null) {
|
||||
return null;
|
||||
}
|
||||
Map<DateTime, K> map =
|
||||
dbData.getMap().entrySet().stream()
|
||||
.map(this::convertToEntityMapEntry)
|
||||
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
return TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.copyOf(map), getTimedTransitionSubclass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.Registry.TldStateTransition;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Converter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* JPA converter for storing/retrieving {@link TimedTransitionProperty<TldState,
|
||||
* TldStateTransition>} objects.
|
||||
*/
|
||||
@Converter(autoApply = true)
|
||||
public class TldStateTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<TldState, TldStateTransition> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, TldStateTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, TldState> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(DateTime.parse(entry.getKey()), TldState.valueOf(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<TldStateTransition> getTimedTransitionSubclass() {
|
||||
return TldStateTransition.class;
|
||||
}
|
||||
}
|
||||
@@ -18,24 +18,20 @@ import google.registry.persistence.VKey;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* Converts VKey to a string column.
|
||||
*/
|
||||
public abstract class VKeyConverter<T> implements AttributeConverter<VKey<T>, String> {
|
||||
/** Converts VKey to a string column. */
|
||||
public abstract class VKeyConverter<T, C> implements AttributeConverter<VKey<? extends T>, C> {
|
||||
@Override
|
||||
@Nullable
|
||||
public String convertToDatabaseColumn(@Nullable VKey<T> attribute) {
|
||||
return attribute == null ? null : (String) attribute.getSqlKey();
|
||||
public C convertToDatabaseColumn(@Nullable VKey<? extends T> attribute) {
|
||||
return attribute == null ? null : (C) attribute.getSqlKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public VKey<T> convertToEntityAttribute(@Nullable String dbData) {
|
||||
public VKey<? extends T> convertToEntityAttribute(@Nullable C dbData) {
|
||||
return dbData == null ? null : VKey.createSql(getAttributeClass(), dbData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class of the attribute.
|
||||
*/
|
||||
/** Returns the class of the attribute. */
|
||||
protected abstract Class<T> getAttributeClass();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/** Sub-interface of {@link TransactionManager} which defines JPA related methods. */
|
||||
@@ -21,4 +22,7 @@ public interface JpaTransactionManager extends TransactionManager {
|
||||
|
||||
/** Returns the {@link EntityManager} for the current request. */
|
||||
EntityManager getEntityManager();
|
||||
|
||||
/** Deletes the entity by its id, throws exception if the entity is not deleted. */
|
||||
public abstract <T> void assertDelete(VKey<T> key);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
@Override
|
||||
public void assertInTransaction() {
|
||||
if (!inTransaction()) {
|
||||
throw new PersistenceException("Not in a transaction");
|
||||
throw new IllegalStateException("Not in a transaction");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,12 +232,23 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> load(VKey<T> key) {
|
||||
public <T> Optional<T> maybeLoad(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
return Optional.ofNullable(getEntityManager().find(key.getKind(), key.getSqlKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T load(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
T result = getEntityManager().find(key.getKind(), key.getSqlKey());
|
||||
if (result == null) {
|
||||
throw new NoSuchElementException(key.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> load(Iterable<VKey<T>> keys) {
|
||||
checkArgumentNotNull(keys, "keys must be specified");
|
||||
@@ -267,8 +278,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> int delete(VKey<T> key) {
|
||||
private <T> int internalDelete(VKey<T> key) {
|
||||
checkArgumentNotNull(key, "key must be specified");
|
||||
assertInTransaction();
|
||||
EntityType<?> entityType = getEntityType(key.getKind());
|
||||
@@ -280,9 +290,14 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
return query.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void delete(VKey<T> key) {
|
||||
internalDelete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
if (delete(key) != 1) {
|
||||
if (internalDelete(key) != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Error deleting the entity of the key: %s", key.getSqlKey()));
|
||||
}
|
||||
|
||||
@@ -108,7 +108,10 @@ public interface TransactionManager {
|
||||
<T> boolean checkExists(VKey<T> key);
|
||||
|
||||
/** Loads the entity by its id, returns empty if the entity doesn't exist. */
|
||||
<T> Optional<T> load(VKey<T> key);
|
||||
<T> Optional<T> maybeLoad(VKey<T> key);
|
||||
|
||||
/** Loads the entity by its id, throws NoSuchElementException if it doesn't exist. */
|
||||
<T> T load(VKey<T> key);
|
||||
|
||||
/**
|
||||
* Leads the set of entities by their key id.
|
||||
@@ -120,9 +123,6 @@ public interface TransactionManager {
|
||||
/** Loads all entities of the given type, returns empty if there is no such entity. */
|
||||
<T> ImmutableList<T> loadAll(Class<T> clazz);
|
||||
|
||||
/** Deletes the entity by its id, returns the number of deleted entity. */
|
||||
<T> int delete(VKey<T> key);
|
||||
|
||||
/** Deletes the entity by its id, throws exception if the entity is not deleted. */
|
||||
<T> void assertDelete(VKey<T> key);
|
||||
/** Deletes the entity by its id. */
|
||||
<T> void delete(VKey<T> key);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.rdap.RdapDataStructures.Event;
|
||||
import google.registry.rdap.RdapDataStructures.EventAction;
|
||||
import google.registry.rdap.RdapDataStructures.Link;
|
||||
@@ -346,7 +347,12 @@ public class RdapJsonFormatter {
|
||||
ImmutableSet.copyOf(tm().load(domainBase.getNameservers()));
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
Map<Key<ContactResource>, ContactResource> loadedContacts =
|
||||
ofy().load().keys(domainBase.getReferencedContacts());
|
||||
ofy()
|
||||
.load()
|
||||
.keys(
|
||||
domainBase.getReferencedContacts().stream()
|
||||
.map(VKey::getOfyKey)
|
||||
.collect(toImmutableSet()));
|
||||
// RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and MAY
|
||||
// have others. We also add the BILLING.
|
||||
//
|
||||
@@ -361,7 +367,7 @@ public class RdapJsonFormatter {
|
||||
.sorted(DESIGNATED_CONTACT_ORDERING)
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getContactKey, DesignatedContact::getType));
|
||||
contact -> contact.getContactKey().getOfyKey(), DesignatedContact::getType));
|
||||
|
||||
for (Key<ContactResource> contactKey : contactsToRoles.keySet()) {
|
||||
Set<RdapEntity.Role> roles =
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
package google.registry.rde;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
@@ -30,6 +29,7 @@ import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Idn;
|
||||
import google.registry.xjc.domain.XjcDomainContactAttrType;
|
||||
import google.registry.xjc.domain.XjcDomainContactType;
|
||||
@@ -167,11 +167,11 @@ final class DomainBaseToXjcConverter {
|
||||
// o An OPTIONAL <registrant> element that contain the identifier for
|
||||
// the human or organizational social information object associated
|
||||
// as the holder of the domain name object.
|
||||
Key<ContactResource> registrant = model.getRegistrant();
|
||||
VKey<ContactResource> registrant = model.getRegistrant();
|
||||
if (registrant == null) {
|
||||
logger.atWarning().log("Domain %s has no registrant contact.", domainName);
|
||||
} else {
|
||||
ContactResource registrantContact = ofy().load().key(registrant).now();
|
||||
ContactResource registrantContact = tm().load(registrant);
|
||||
checkState(
|
||||
registrantContact != null,
|
||||
"Registrant contact %s on domain %s does not exist",
|
||||
@@ -304,7 +304,7 @@ final class DomainBaseToXjcConverter {
|
||||
"Contact key for type %s is null on domain %s",
|
||||
model.getType(),
|
||||
domainName);
|
||||
ContactResource contact = ofy().load().key(model.getContactKey()).now();
|
||||
ContactResource contact = tm().load(model.getContactKey());
|
||||
checkState(
|
||||
contact != null,
|
||||
"Contact %s on domain %s does not exist",
|
||||
|
||||
@@ -16,10 +16,13 @@ package google.registry.schema.cursor;
|
||||
|
||||
import static com.google.appengine.api.search.checkers.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.schema.cursor.Cursor.CursorId;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.Serializable;
|
||||
import java.time.ZonedDateTime;
|
||||
@@ -38,7 +41,7 @@ import org.joda.time.DateTime;
|
||||
@Entity
|
||||
@Table
|
||||
@IdClass(CursorId.class)
|
||||
public class Cursor {
|
||||
public class Cursor implements SqlEntity {
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
@@ -100,6 +103,11 @@ public class Cursor {
|
||||
return lastUpdateTime.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // Cursors are not converted since they are ephemeral
|
||||
}
|
||||
|
||||
static class CursorId extends ImmutableObject implements Serializable {
|
||||
|
||||
public CursorType type;
|
||||
|
||||
@@ -19,10 +19,13 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.toZonedDateTime;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
@@ -73,7 +76,7 @@ import org.joda.time.Duration;
|
||||
@Index(name = "idx_registry_lock_verification_code", columnList = "verificationCode"),
|
||||
@Index(name = "idx_registry_lock_registrar_id", columnList = "registrarId")
|
||||
})
|
||||
public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||
public final class RegistryLock extends ImmutableObject implements Buildable, SqlEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@@ -230,6 +233,11 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // not stored in Datastore
|
||||
}
|
||||
|
||||
/** Builder for {@link google.registry.schema.domain.RegistryLock}. */
|
||||
public static class Builder extends Buildable.Builder<RegistryLock> {
|
||||
public Builder() {}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 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.schema.replay;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/** An entity that has the same Java object representation in SQL and Datastore. */
|
||||
public interface DatastoreAndSqlEntity extends DatastoreEntity, SqlEntity {
|
||||
|
||||
@Override
|
||||
default ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
default ImmutableList<SqlEntity> toSqlEntities() {
|
||||
return ImmutableList.of(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 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.schema.replay;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* An object that can be stored in Datastore and serialized using Objectify's {@link
|
||||
* com.googlecode.objectify.cmd.Saver#toEntity(Object)} code.
|
||||
*
|
||||
* <p>This is used when replaying {@link google.registry.model.ofy.CommitLogManifest}s to import
|
||||
* transactions and data into the secondary SQL store during the first, Datastore-primary, phase of
|
||||
* the migration.
|
||||
*/
|
||||
public interface DatastoreEntity {
|
||||
|
||||
ImmutableList<SqlEntity> toSqlEntities();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2020 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.schema.replay;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* An object that can be stored in Cloud SQL using {@link
|
||||
* javax.persistence.EntityManager#persist(Object)}
|
||||
*
|
||||
* <p>This will be used when replaying SQL transactions into Datastore, during the second,
|
||||
* SQL-primary, phase of the migration from Datastore to SQL.
|
||||
*/
|
||||
public interface SqlEntity {
|
||||
|
||||
ImmutableList<DatastoreEntity> toDatastoreEntities();
|
||||
}
|
||||
@@ -16,7 +16,10 @@ package google.registry.schema.server;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import google.registry.schema.server.Lock.LockId;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.Serializable;
|
||||
@@ -40,7 +43,7 @@ import org.joda.time.Duration;
|
||||
@Entity
|
||||
@Table
|
||||
@IdClass(LockId.class)
|
||||
public class Lock {
|
||||
public class Lock implements SqlEntity {
|
||||
|
||||
/** The resource name used to create the lock. */
|
||||
@Column(nullable = false)
|
||||
@@ -116,6 +119,11 @@ public class Lock {
|
||||
return new Lock(resourceName, GLOBAL, requestLogId, acquiredTime, leaseLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // Locks are not converted since they are ephemeral
|
||||
}
|
||||
|
||||
static class LockId extends ImmutableObject implements Serializable {
|
||||
|
||||
String resourceName;
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
package google.registry.schema.tld;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import javax.persistence.Column;
|
||||
@@ -27,7 +30,7 @@ import javax.persistence.Id;
|
||||
* <p>These are not persisted directly, but rather, using {@link PremiumList#getLabelsToPrices()}.
|
||||
*/
|
||||
@Entity
|
||||
public class PremiumEntry extends ImmutableObject implements Serializable {
|
||||
public class PremiumEntry extends ImmutableObject implements Serializable, SqlEntity {
|
||||
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
@@ -41,4 +44,9 @@ public class PremiumEntry extends ImmutableObject implements Serializable {
|
||||
String domainLabel;
|
||||
|
||||
private PremiumEntry() {}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // PremiumList is dually-written
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@ import static com.google.common.base.Charsets.US_ASCII;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.hash.Funnels.stringFunnel;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.hash.BloomFilter;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -49,7 +52,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "name", name = "premiumlist_name_idx")})
|
||||
public class PremiumList {
|
||||
public class PremiumList implements SqlEntity {
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@@ -140,4 +143,9 @@ public class PremiumList {
|
||||
public BloomFilter<String> getBloomFilter() {
|
||||
return bloomFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // PremiumList is dual-written
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class PremiumListUtils {
|
||||
|
||||
Map<String, BigDecimal> priceAmounts =
|
||||
Maps.transformValues(prices, ple -> ple.getValue().getAmount());
|
||||
return google.registry.schema.tld.PremiumList.create(name, currency, priceAmounts);
|
||||
return PremiumList.create(name, currency, priceAmounts);
|
||||
}
|
||||
|
||||
private PremiumListUtils() {}
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.registry.label.ReservationType;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -53,7 +55,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "name", name = "reservedlist_name_idx")})
|
||||
public class ReservedList extends ImmutableObject {
|
||||
public class ReservedList extends ImmutableObject implements SqlEntity {
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@@ -76,6 +78,11 @@ public class ReservedList extends ImmutableObject {
|
||||
@MapKeyColumn(name = "domainLabel")
|
||||
private Map<String, ReservedEntry> labelsToReservations;
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // ReservedList is dual-written\
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class ReservedEntry extends ImmutableObject {
|
||||
@Column(nullable = false)
|
||||
|
||||
@@ -18,8 +18,11 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.util.DateTimeUtils.toJodaDateTime;
|
||||
import static google.registry.util.DateTimeUtils.toZonedDateTime;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.schema.replay.DatastoreEntity;
|
||||
import google.registry.schema.replay.SqlEntity;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -46,7 +49,7 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
@Entity
|
||||
@Table
|
||||
public class ClaimsList extends ImmutableObject {
|
||||
public class ClaimsList extends ImmutableObject implements SqlEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column
|
||||
@@ -105,4 +108,9 @@ public class ClaimsList extends ImmutableObject {
|
||||
public Optional<String> getClaimKey(String label) {
|
||||
return Optional.ofNullable(labelsToKeys.get(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
|
||||
return ImmutableList.of(); // ClaimsList is dual-written
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +153,8 @@ public final class DomainLockUtils {
|
||||
/**
|
||||
* Creates and applies a lock in one step.
|
||||
*
|
||||
* <p>This should only be used for admin actions, e.g. Nomulus tool commands or relocks.
|
||||
* Note: in the case of relocks, isAdmin is determined by the previous lock.
|
||||
* <p>This should only be used for admin actions, e.g. Nomulus tool commands or relocks. Note: in
|
||||
* the case of relocks, isAdmin is determined by the previous lock.
|
||||
*/
|
||||
public RegistryLock administrativelyApplyLock(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
@@ -175,7 +175,7 @@ public final class DomainLockUtils {
|
||||
|
||||
/**
|
||||
* Creates and applies an unlock in one step.
|
||||
*
|
||||
*
|
||||
* <p>This should only be used for admin actions, e.g. Nomulus tool commands.
|
||||
*/
|
||||
public RegistryLock administrativelyApplyUnlock(
|
||||
|
||||
@@ -132,10 +132,8 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
jcommander.parse(args);
|
||||
} catch (ParameterException e) {
|
||||
// If we failed to fully parse the command but at least found a valid command name, show only
|
||||
// the usage for that command. Otherwise, show full usage. Either way, rethrow the error.
|
||||
if (jcommander.getParsedCommand() == null) {
|
||||
jcommander.usage();
|
||||
} else {
|
||||
// the usage for that command.
|
||||
if (jcommander.getParsedCommand() != null) {
|
||||
jcommander.usage(jcommander.getParsedCommand());
|
||||
}
|
||||
// Don't rethrow if we said: nomulus command --help
|
||||
@@ -144,8 +142,13 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (showAllCommands) {
|
||||
String parsedCommand = jcommander.getParsedCommand();
|
||||
// Show the list of all commands either if requested or if no subcommand name was specified
|
||||
// (which does not throw a ParameterException parse error above).
|
||||
if (showAllCommands || parsedCommand == null) {
|
||||
if (parsedCommand == null) {
|
||||
System.out.println("The list of available subcommands is:");
|
||||
}
|
||||
commands.keySet().forEach(System.out::println);
|
||||
return;
|
||||
}
|
||||
@@ -161,8 +164,7 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
// retrieving the first (and, by virtue of our usage, only) object from it.
|
||||
Command command =
|
||||
(Command)
|
||||
Iterables.getOnlyElement(
|
||||
jcommander.getCommands().get(jcommander.getParsedCommand()).getObjects());
|
||||
Iterables.getOnlyElement(jcommander.getCommands().get(parsedCommand).getObjects());
|
||||
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
||||
|
||||
try {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.util.CollectionUtils.findDuplicates;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
@@ -37,6 +38,13 @@ import org.joda.time.format.DateTimeFormatter;
|
||||
@Parameters(separators = " =", commandDescription = "Renew domain(s) via EPP.")
|
||||
final class RenewDomainCommand extends MutatingEppToolCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description =
|
||||
"The registrar to execute as and bill the renewal to; otherwise each domain's sponsoring"
|
||||
+ " registrar. Renewals by non-sponsoring registrars require --superuser as well.")
|
||||
String clientId;
|
||||
|
||||
@Parameter(
|
||||
names = {"-p", "--period"},
|
||||
description = "Number of years to renew the registration for (defaults to 1).")
|
||||
@@ -63,7 +71,7 @@ final class RenewDomainCommand extends MutatingEppToolCommand {
|
||||
setSoyTemplate(RenewDomainSoyInfo.getInstance(), RenewDomainSoyInfo.RENEWDOMAIN);
|
||||
DomainBase domain = domainOptional.get();
|
||||
addSoyRecord(
|
||||
domain.getCurrentSponsorClientId(),
|
||||
isNullOrEmpty(clientId) ? domain.getCurrentSponsorClientId() : clientId,
|
||||
new SoyMapData(
|
||||
"domainName", domain.getFullyQualifiedDomainName(),
|
||||
"expirationDate", domain.getRegistrationExpirationTime().toString(DATE_FORMATTER),
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
@@ -291,11 +291,9 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
|
||||
ImmutableSet<String> getContactsOfType(
|
||||
DomainBase domainBase, final DesignatedContact.Type contactType) {
|
||||
return domainBase
|
||||
.getContacts()
|
||||
.stream()
|
||||
return domainBase.getContacts().stream()
|
||||
.filter(contact -> contact.getType().equals(contactType))
|
||||
.map(contact -> ofy().load().key(contact.getContactKey()).now().getContactId())
|
||||
.map(contact -> tm().load(contact.getContactKey()).getContactId())
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +186,7 @@ public final class RegistrarFormFields {
|
||||
.build();
|
||||
|
||||
public static final FormField<String, String> REGISTRY_LOCK_EMAIL_ADDRESS_FIELD =
|
||||
FormFields.EMAIL
|
||||
.asBuilderNamed("registryLockEmailAddress")
|
||||
.build();
|
||||
FormFields.EMAIL.asBuilderNamed("registryLockEmailAddress").build();
|
||||
|
||||
public static final FormField<Boolean, Boolean> CONTACT_VISIBLE_IN_WHOIS_AS_ADMIN_FIELD =
|
||||
FormField.named("visibleInWhoisAsAdmin", Boolean.class)
|
||||
|
||||
@@ -391,7 +391,8 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
* @throws FormException if the checks fail.
|
||||
*/
|
||||
void checkContactRequirements(
|
||||
Set<RegistrarContact> existingContacts, Set<RegistrarContact> updatedContacts) {
|
||||
ImmutableSet<RegistrarContact> existingContacts,
|
||||
ImmutableSet<RegistrarContact> updatedContacts) {
|
||||
// Check that no two contacts use the same email address.
|
||||
Set<String> emails = new HashSet<>();
|
||||
for (RegistrarContact contact : updatedContacts) {
|
||||
@@ -435,7 +436,12 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
throw new ContactRequirementException(
|
||||
"An abuse contact visible in domain WHOIS query must be designated");
|
||||
}
|
||||
checkContactRegistryLockRequirements(existingContacts, updatedContacts);
|
||||
}
|
||||
|
||||
private static void checkContactRegistryLockRequirements(
|
||||
ImmutableSet<RegistrarContact> existingContacts,
|
||||
ImmutableSet<RegistrarContact> updatedContacts) {
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
for (RegistrarContact updatedContact : updatedContacts) {
|
||||
if (updatedContact.isRegistryLockAllowed()
|
||||
@@ -463,6 +469,31 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any previously-existing contacts with registry lock enabled cannot be deleted
|
||||
existingContacts.stream()
|
||||
.filter(RegistrarContact::isRegistryLockAllowed)
|
||||
.forEach(
|
||||
contact -> {
|
||||
Optional<RegistrarContact> updatedContactOptional =
|
||||
updatedContacts.stream()
|
||||
.filter(
|
||||
updatedContact ->
|
||||
updatedContact.getEmailAddress().equals(contact.getEmailAddress()))
|
||||
.findFirst();
|
||||
if (!updatedContactOptional.isPresent()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot delete the contact %s that has registry lock enabled",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
if (!updatedContactOptional.get().isRegistryLockAllowed()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot remove the ability to use registry lock on the contact %s",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,6 @@ import static google.registry.xml.UtcDateTimeAdapter.getFormattedString;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactPhoneNumber;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
@@ -35,6 +34,7 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.translators.EnumToAttributeAdapter.EppEnum;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -128,7 +128,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
}
|
||||
|
||||
/** Returns the contact of the given type. */
|
||||
private Optional<Key<ContactResource>> getContactReference(Type type) {
|
||||
private Optional<VKey<ContactResource>> getContactReference(Type type) {
|
||||
Optional<DesignatedContact> contactOfType =
|
||||
domain.getContacts().stream().filter(d -> d.getType() == type).findFirst();
|
||||
return contactOfType.map(DesignatedContact::getContactKey);
|
||||
@@ -149,14 +149,14 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
|
||||
/** Emit the contact entry of the given type. */
|
||||
DomainEmitter emitContact(
|
||||
String contactType, Optional<Key<ContactResource>> contact, boolean preferUnicode) {
|
||||
String contactType, Optional<VKey<ContactResource>> contact, boolean preferUnicode) {
|
||||
if (!contact.isPresent()) {
|
||||
return this;
|
||||
}
|
||||
// If we refer to a contact that doesn't exist, that's a bug. It means referential integrity
|
||||
// has somehow been broken. We skip the rest of this contact, but log it to hopefully bring it
|
||||
// someone's attention.
|
||||
ContactResource contactResource = EppResource.loadCached(contact.get());
|
||||
ContactResource contactResource = EppResource.loadCached(contact.get().getOfyKey());
|
||||
if (contactResource == null) {
|
||||
logger.atSevere().log(
|
||||
"(BUG) Broken reference found from domain %s to contact %s",
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
* Move tests to another (sub)project. This is not a big problem, but feels unnatural.
|
||||
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
|
||||
-->
|
||||
<class>google.registry.model.contact.ContactResource</class>
|
||||
<class>google.registry.model.domain.DomainBase</class>
|
||||
<class>google.registry.model.host.HostResource</class>
|
||||
<class>google.registry.model.registrar.Registrar</class>
|
||||
<class>google.registry.model.registrar.RegistrarContact</class>
|
||||
<class>google.registry.schema.domain.RegistryLock</class>
|
||||
@@ -33,6 +35,7 @@
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
|
||||
<!-- Customized type converters -->
|
||||
<class>google.registry.persistence.converter.BillingCostTransitionConverter</class>
|
||||
<class>google.registry.persistence.converter.BloomFilterConverter</class>
|
||||
<class>google.registry.persistence.converter.CidrAddressBlockListConverter</class>
|
||||
<class>google.registry.persistence.converter.CreateAutoTimestampConverter</class>
|
||||
@@ -40,13 +43,19 @@
|
||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.converter.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.converter.DurationConverter</class>
|
||||
<class>google.registry.persistence.converter.PostalInfoChoiceListConverter</class>
|
||||
<class>google.registry.persistence.converter.RegistrarPocSetConverter</class>
|
||||
<class>google.registry.persistence.converter.StatusValueSetConverter</class>
|
||||
<class>google.registry.persistence.converter.StringListConverter</class>
|
||||
<class>google.registry.persistence.converter.StringSetConverter</class>
|
||||
<class>google.registry.persistence.converter.TldStateTransitionConverter</class>
|
||||
<class>google.registry.persistence.converter.UpdateAutoTimestampConverter</class>
|
||||
<class>google.registry.persistence.converter.ZonedDateTimeConverter</class>
|
||||
|
||||
<!-- Generated converters for VKey -->
|
||||
<class>google.registry.model.host.VKeyConverter_HostResource</class>
|
||||
<class>google.registry.model.contact.VKeyConverter_ContactResource</class>
|
||||
|
||||
<!-- TODO(weiminyu): check out application-layer validation. -->
|
||||
<validation-mode>NONE</validation-mode>
|
||||
</persistence-unit>
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
{param label: 'Email' /}
|
||||
{param namePrefix: $namePrefix /}
|
||||
{param name: 'emailAddress' /}
|
||||
{param disabled: not $readonly and $item['emailAddress'] != null /}
|
||||
{/call}
|
||||
{call registry.soy.forms.inputFieldRow data="all"}
|
||||
{param label: 'Phone' /}
|
||||
|
||||
@@ -64,8 +64,8 @@ public class DumpGoldenSchemaCommand extends PostgresqlCommand {
|
||||
if (result.getExitCode() != 0) {
|
||||
throw new RuntimeException(result.toString());
|
||||
}
|
||||
result = postgresContainer.execInContainer(
|
||||
"cp", CONTAINER_MOUNT_POINT_TMP, CONTAINER_MOUNT_POINT);
|
||||
result =
|
||||
postgresContainer.execInContainer("cp", CONTAINER_MOUNT_POINT_TMP, CONTAINER_MOUNT_POINT);
|
||||
if (result.getExitCode() != 0) {
|
||||
throw new RuntimeException(result.toString());
|
||||
}
|
||||
|
||||
@@ -50,7 +50,11 @@ import org.junit.runners.JUnit4;
|
||||
public class ExportCommitLogDiffActionTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
/** Local GCS service available for testing. */
|
||||
private final GcsService gcsService = GcsServiceFactory.createGcsService();
|
||||
|
||||
@@ -71,7 +71,11 @@ public class RestoreCommitLogsActionTest {
|
||||
final GcsService gcsService = createGcsService();
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
|
||||
@@ -190,7 +190,9 @@ public class DeleteContactsAndHostsActionTest
|
||||
.hasDeletionTime(END_OF_TIME);
|
||||
DomainBase domainReloaded =
|
||||
loadByForeignKey(DomainBase.class, "example.tld", clock.nowUtc()).get();
|
||||
assertThat(domainReloaded.getReferencedContacts()).contains(Key.create(contactUpdated));
|
||||
// We have to check for the objectify key here, specifically, as the SQL side of the key does
|
||||
// not get persisted.
|
||||
assertThat(domainReloaded.getReferencedContacts()).contains(contactUpdated.createVKey());
|
||||
HistoryEntry historyEntry =
|
||||
getOnlyHistoryEntryOfType(contactUpdated, HistoryEntry.Type.CONTACT_DELETE_FAILURE);
|
||||
assertPollMessageFor(
|
||||
|
||||
@@ -18,6 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.registry.Registry.TldState.PREDELEGATION;
|
||||
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
|
||||
@@ -26,6 +27,7 @@ import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.createTlds;
|
||||
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.testing.DomainBaseSubject.assertAboutDomains;
|
||||
import static google.registry.testing.EppMetricSubject.assertThat;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
@@ -84,19 +86,311 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00.0Z",
|
||||
"EXDATE", "2002-06-01T00:02:00.0Z"));
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-07T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2000-06-06T00:02:00Z"));
|
||||
|
||||
// Delete domain example.tld after its add grace period has expired.
|
||||
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-07-01T00:02:00Z")
|
||||
.hasResponse("generic_success_action_pending_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-07-03T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_redemptionperiod_wildcard.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date doesn't change because the deletion didn't cancel any charges.
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2000-07-01T00:02:00Z"));
|
||||
|
||||
// Restore the domain.
|
||||
assertThatCommand("domain_update_restore_request.xml")
|
||||
.atTime("2000-07-01T00:03:00Z")
|
||||
.hasResponse("generic_success_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-07-02T00:03:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// TODO(mcilwain): The exp. date should be restored back to 2002-06-01T00:02:00Z,
|
||||
// but this is old behavior of being 1 year after the moment of the restore.
|
||||
"EXDATE", "2001-07-01T00:03:00Z",
|
||||
"UPDATE", "2000-07-01T00:03:00Z"));
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainDeleteRestore_duringAutorenewGracePeriod() throws Exception {
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
|
||||
|
||||
// Create domain example.tld
|
||||
assertThatCommand(
|
||||
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-01T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-07T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2000-06-06T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2002-06-07T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_graceperiod.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date has advanced 1 year because of autorenew.
|
||||
"EXDATE", "2003-06-01T00:02:00Z",
|
||||
// This is the time of the autorenew.
|
||||
"UPDATE", "2002-06-01T00:02:00Z",
|
||||
"GRACEPERIOD", "autoRenewPeriod"));
|
||||
|
||||
// Delete domain example.tld during its autorenew grace period.
|
||||
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2002-07-01T00:02:00Z")
|
||||
.hasResponse("generic_success_action_pending_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2002-07-03T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_redemptionperiod_wildcard.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date reverts back to what it was originally because the deletion
|
||||
// canceled out the autorenew.
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2002-07-01T00:02:00Z"));
|
||||
|
||||
// Restore the domain.
|
||||
assertThatCommand("domain_update_restore_request.xml")
|
||||
.atTime("2002-07-05T00:03:00Z")
|
||||
.hasResponse("generic_success_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2002-07-07T00:03:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// TODO(mcilwain): The exp. date should be 2003-06-01T00:02:00Z, the same as its
|
||||
// value prior to the deletion, because the year that was taken off when the
|
||||
// autorenew was canceled will be re-added in renewal during the restore.
|
||||
// For now though, the current behavior is 1 year after restore.
|
||||
"EXDATE", "2003-07-05T00:03:00Z",
|
||||
"UPDATE", "2002-07-05T00:03:00Z"));
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainDeleteRestore_duringRenewalGracePeriod() throws Exception {
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
|
||||
|
||||
// Create domain example.tld
|
||||
assertThatCommand(
|
||||
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-01T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-07T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2000-06-06T00:02:00Z"));
|
||||
|
||||
assertThatCommand(
|
||||
"domain_renew.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXPDATE", "2002-06-01", "YEARS", "3"))
|
||||
.atTime("2000-06-08T00:00:00Z")
|
||||
.hasResponse(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-10T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_graceperiod.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date is 5 years in total after the create.
|
||||
"EXDATE", "2005-06-01T00:02:00Z",
|
||||
// This is the time of the renew.
|
||||
"UPDATE", "2000-06-08T00:00:00Z",
|
||||
"GRACEPERIOD", "renewPeriod"));
|
||||
|
||||
// Delete domain example.tld during its renew grace period.
|
||||
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-12T00:00:00Z")
|
||||
.hasResponse("generic_success_action_pending_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-13T00:00:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_redemptionperiod_wildcard.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date reverts back to what it was originally because the deletion
|
||||
// canceled out the 3-year renewal.
|
||||
"EXDATE", "2002-06-01T00:02:00Z",
|
||||
"UPDATE", "2000-06-12T00:00:00Z"));
|
||||
|
||||
// Restore the domain.
|
||||
assertThatCommand("domain_update_restore_request.xml")
|
||||
.atTime("2000-06-20T00:00:00Z")
|
||||
.hasResponse("generic_success_response.xml");
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-21T00:00:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_inactive.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// TODO(mcilwain): The exp. date should be 2002-06-01T00:02:00Z, which is the
|
||||
// current registration expiration time on the (deleted) domain, but for now is
|
||||
// 1 year after restore.
|
||||
"EXDATE", "2001-06-20T00:00:00Z",
|
||||
"UPDATE", "2000-06-20T00:00:00Z"));
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainDelete_duringAddAndRenewalGracePeriod_deletesImmediately()
|
||||
throws Exception {
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
|
||||
|
||||
DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z");
|
||||
// Create domain example.tld
|
||||
assertThatCommand(
|
||||
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime(createTime)
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-02T00:02:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_addperiod_wildcard.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
"EXDATE", "2002-06-01T00:02:00Z"));
|
||||
|
||||
DateTime renewTime = DateTime.parse("2000-06-03T00:00:00Z");
|
||||
assertThatCommand(
|
||||
"domain_renew.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXPDATE", "2002-06-01", "YEARS", "3"))
|
||||
.atTime(renewTime)
|
||||
.hasResponse(
|
||||
"domain_renew_response.xml",
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-06-01T00:02:00Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-03T03:00:00Z")
|
||||
.hasResponse(
|
||||
"domain_info_response_graceperiod_add_and_renew.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "example.tld",
|
||||
"CRDATE", "2000-06-01T00:02:00Z",
|
||||
// The exp. date is 5 years in total after the create.
|
||||
"EXDATE", "2005-06-01T00:02:00Z",
|
||||
// This is the time of the renew.
|
||||
"UPDATE", "2000-06-03T00:00:00Z"));
|
||||
|
||||
DomainBase domain =
|
||||
loadByForeignKey(DomainBase.class, "example.tld", DateTime.parse("2000-06-03T04:00:00Z"))
|
||||
.get();
|
||||
|
||||
DateTime deleteTime = DateTime.parse("2000-06-04T00:00:00Z");
|
||||
// Delete domain example.tld during both grace periods.
|
||||
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-04T00:00:00Z")
|
||||
.hasResponse("generic_success_response.xml");
|
||||
|
||||
// Verify that it is immediately non-existent.
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
|
||||
.atTime("2000-06-04T00:01:00Z")
|
||||
.hasResponse(
|
||||
"response_error.xml",
|
||||
ImmutableMap.of(
|
||||
"CODE", "2303", "MSG", "The domain with given ID (example.tld) doesn't exist."));
|
||||
|
||||
// The expected one-time billing event, that should have an associated Cancellation.
|
||||
OneTime oneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime);
|
||||
OneTime oneTimeRenewBillingEvent = makeOneTimeRenewBillingEvent(domain, renewTime);
|
||||
|
||||
// Verify that the OneTime billing event associated with the domain creation is canceled.
|
||||
assertBillingEventsForResource(
|
||||
domain,
|
||||
// There should be one-time billing events for the create and the renew.
|
||||
oneTimeCreateBillingEvent,
|
||||
oneTimeRenewBillingEvent,
|
||||
// There should be two ended recurring billing events, one each from the create and renew.
|
||||
// (The former was ended by the renew and the latter was ended by the delete.)
|
||||
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), renewTime),
|
||||
makeRecurringRenewBillingEvent(domain, createTime.plusYears(5), deleteTime),
|
||||
// There should be Cancellations offsetting both of the one-times.
|
||||
makeCancellationBillingEventForCreate(
|
||||
domain, oneTimeCreateBillingEvent, createTime, deleteTime),
|
||||
makeCancellationBillingEventForRenew(
|
||||
domain, oneTimeRenewBillingEvent, renewTime, deleteTime));
|
||||
|
||||
// Verify that the registration expiration time was set back to the creation time, because the
|
||||
// entire cost of registration was refunded. We have to do this through the DB instead of EPP
|
||||
// because domains deleted during the add grace period vanish immediately as far as the world
|
||||
// outside our system is concerned.
|
||||
DomainBase deletedDomain = ofy().load().entity(domain).now();
|
||||
assertAboutDomains().that(deletedDomain).hasRegistrationExpirationTime(createTime);
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@@ -143,9 +437,16 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
||||
oneTimeCreateBillingEvent,
|
||||
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime),
|
||||
// Check for the existence of a cancellation for the given one-time billing event.
|
||||
makeCancellationBillingEventFor(
|
||||
makeCancellationBillingEventForCreate(
|
||||
domain, oneTimeCreateBillingEvent, createTime, deleteTime));
|
||||
|
||||
// Verify that the registration expiration time was set back to the creation time, because the
|
||||
// entire cost of registration was refunded. We have to do this through the DB instead of EPP
|
||||
// because domains deleted during the add grace period vanish immediately as far as the world
|
||||
// outside our system is concerned.
|
||||
DomainBase deletedDomain = ofy().load().entity(domain).now();
|
||||
assertAboutDomains().that(deletedDomain).hasRegistrationExpirationTime(createTime);
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@@ -268,7 +569,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
|
||||
expectedCreateEapBillingEvent,
|
||||
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime),
|
||||
// ... and verify that the create one-time billing event was canceled ...
|
||||
makeCancellationBillingEventFor(
|
||||
makeCancellationBillingEventForCreate(
|
||||
domain, expectedOneTimeCreateBillingEvent, createTime, deleteTime));
|
||||
// ... but there was NOT a Cancellation for the EAP fee, as this would fail if additional
|
||||
// billing events were present.
|
||||
|
||||
@@ -284,20 +284,42 @@ public class EppTestCase extends ShardableTestCase {
|
||||
.setCost(Money.parse("USD 26.00"))
|
||||
.setPeriodYears(2)
|
||||
.setEventTime(createTime)
|
||||
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
|
||||
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getAddGracePeriodLength()))
|
||||
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Makes a one-time billing event corresponding to the given domain's renewal. */
|
||||
protected static BillingEvent.OneTime makeOneTimeRenewBillingEvent(
|
||||
DomainBase domain, DateTime renewTime) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setTargetId(domain.getFullyQualifiedDomainName())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setCost(Money.parse("USD 33.00"))
|
||||
.setPeriodYears(3)
|
||||
.setEventTime(renewTime)
|
||||
.setBillingTime(renewTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
|
||||
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Makes a recurring billing event corresponding to the given domain's creation. */
|
||||
protected static BillingEvent.Recurring makeRecurringCreateBillingEvent(
|
||||
DomainBase domain, DateTime eventTime, DateTime endTime) {
|
||||
return makeRecurringCreateBillingEvent(
|
||||
return makeRecurringBillingEvent(
|
||||
domain, getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE), eventTime, endTime);
|
||||
}
|
||||
|
||||
/** Makes a recurring billing event corresponding to the given domain's renewal. */
|
||||
protected static BillingEvent.Recurring makeRecurringRenewBillingEvent(
|
||||
DomainBase domain, DateTime eventTime, DateTime endTime) {
|
||||
return makeRecurringBillingEvent(
|
||||
domain, getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW), eventTime, endTime);
|
||||
}
|
||||
|
||||
/** Makes a recurring billing event corresponding to the given history entry. */
|
||||
protected static BillingEvent.Recurring makeRecurringCreateBillingEvent(
|
||||
protected static BillingEvent.Recurring makeRecurringBillingEvent(
|
||||
DomainBase domain, HistoryEntry historyEntry, DateTime eventTime, DateTime endTime) {
|
||||
return new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
@@ -311,22 +333,33 @@ public class EppTestCase extends ShardableTestCase {
|
||||
}
|
||||
|
||||
/** Makes a cancellation billing event cancelling out the given domain create billing event. */
|
||||
protected static BillingEvent.Cancellation makeCancellationBillingEventFor(
|
||||
DomainBase domain,
|
||||
OneTime billingEventToCancel,
|
||||
DateTime createTime,
|
||||
DateTime deleteTime) {
|
||||
protected static BillingEvent.Cancellation makeCancellationBillingEventForCreate(
|
||||
DomainBase domain, OneTime billingEventToCancel, DateTime createTime, DateTime deleteTime) {
|
||||
return new BillingEvent.Cancellation.Builder()
|
||||
.setTargetId(domain.getFullyQualifiedDomainName())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setEventTime(deleteTime)
|
||||
.setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel))
|
||||
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
|
||||
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getAddGracePeriodLength()))
|
||||
.setReason(Reason.CREATE)
|
||||
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Makes a cancellation billing event cancelling out the given domain renew billing event. */
|
||||
protected static BillingEvent.Cancellation makeCancellationBillingEventForRenew(
|
||||
DomainBase domain, OneTime billingEventToCancel, DateTime renewTime, DateTime deleteTime) {
|
||||
return new BillingEvent.Cancellation.Builder()
|
||||
.setTargetId(domain.getFullyQualifiedDomainName())
|
||||
.setClientId(domain.getCurrentSponsorClientId())
|
||||
.setEventTime(deleteTime)
|
||||
.setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel))
|
||||
.setBillingTime(renewTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
|
||||
.setReason(Reason.RENEW)
|
||||
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Key to the actual one-time create billing event associated with a domain's creation.
|
||||
*
|
||||
|
||||
@@ -151,7 +151,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
|
||||
newDomainBase(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(TIME_BEFORE_FLOW)
|
||||
.setRegistrant(Key.create(contact))
|
||||
.setRegistrant(contact.createVKey())
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.build();
|
||||
earlierHistoryEntry =
|
||||
@@ -391,7 +391,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
|
||||
GracePeriod.create(GracePeriodStatus.TRANSFER, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
|
||||
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
|
||||
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
|
||||
DateTime originalExpirationTime = domain.getRegistrationExpirationTime();
|
||||
DateTime expectedExpirationTime = domain.getRegistrationExpirationTime().minusYears(2);
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile(responseFilename, substitutions));
|
||||
DomainBase resource = reloadResourceByForeignKey();
|
||||
@@ -413,7 +413,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
|
||||
// deletion time, that means once it passes the domain will experience a "phantom autorenew"
|
||||
// where the expirationTime advances and the grace period appears, but since the delete flow
|
||||
// closed the autorenew recurrences immediately, there are no other autorenew effects.
|
||||
assertAboutDomains().that(resource).hasRegistrationExpirationTime(originalExpirationTime);
|
||||
assertAboutDomains().that(resource).hasRegistrationExpirationTime(expectedExpirationTime);
|
||||
// All existing grace periods that were for billable actions should cause cancellations.
|
||||
assertAutorenewClosedAndCancellationCreatedFor(
|
||||
renewBillingEvent, getOnlyHistoryEntryOfType(resource, DOMAIN_DELETE));
|
||||
@@ -700,7 +700,9 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
|
||||
newDomainBase("example1.tld")
|
||||
.asBuilder()
|
||||
.setRegistrant(
|
||||
Key.create(loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get()))
|
||||
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())
|
||||
.get()
|
||||
.createVKey())
|
||||
.setNameservers(ImmutableSet.of(host.createKey()))
|
||||
.setDeletionTime(START_OF_TIME)
|
||||
.build());
|
||||
|
||||
@@ -112,11 +112,11 @@ public class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Dom
|
||||
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
|
||||
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
|
||||
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
|
||||
.setRegistrant(Key.create(registrant))
|
||||
.setRegistrant(registrant.createVKey())
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, Key.create(contact)),
|
||||
DesignatedContact.create(Type.TECH, Key.create(contact))))
|
||||
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, contact.createVKey())))
|
||||
.setNameservers(
|
||||
inactive ? null : ImmutableSet.of(host1.createKey(), host2.createKey()))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
|
||||
|
||||
@@ -153,6 +153,8 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
doSuccessfulTest(
|
||||
responseFilename,
|
||||
renewalYears,
|
||||
"TheRegistrar",
|
||||
UserPrivileges.NORMAL,
|
||||
substitutions,
|
||||
Money.of(USD, 11).multipliedBy(renewalYears));
|
||||
}
|
||||
@@ -160,13 +162,16 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
private void doSuccessfulTest(
|
||||
String responseFilename,
|
||||
int renewalYears,
|
||||
String renewalClientId,
|
||||
UserPrivileges userPrivileges,
|
||||
Map<String, String> substitutions,
|
||||
Money totalRenewCost)
|
||||
throws Exception {
|
||||
assertTransactionalFlow(true);
|
||||
DateTime currentExpiration = reloadResourceByForeignKey().getRegistrationExpirationTime();
|
||||
DateTime newExpiration = currentExpiration.plusYears(renewalYears);
|
||||
runFlowAssertResponse(loadFile(responseFilename, substitutions));
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, userPrivileges, loadFile(responseFilename, substitutions));
|
||||
DomainBase domain = reloadResourceByForeignKey();
|
||||
HistoryEntry historyEntryDomainRenew =
|
||||
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RENEW);
|
||||
@@ -183,13 +188,13 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
.and()
|
||||
.hasLastEppUpdateTime(clock.nowUtc())
|
||||
.and()
|
||||
.hasLastEppUpdateClientId("TheRegistrar");
|
||||
.hasLastEppUpdateClientId(renewalClientId);
|
||||
assertAboutHistoryEntries().that(historyEntryDomainRenew).hasPeriodYears(renewalYears);
|
||||
BillingEvent.OneTime renewBillingEvent =
|
||||
new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setClientId("TheRegistrar")
|
||||
.setClientId(renewalClientId)
|
||||
.setCost(totalRenewCost)
|
||||
.setPeriodYears(renewalYears)
|
||||
.setEventTime(clock.nowUtc())
|
||||
@@ -233,7 +238,7 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.RENEW,
|
||||
clock.nowUtc().plus(Registry.get("tld").getRenewGracePeriodLength()),
|
||||
"TheRegistrar",
|
||||
renewalClientId,
|
||||
null),
|
||||
renewBillingEvent));
|
||||
}
|
||||
@@ -256,6 +261,19 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-04-03T22:00:00.0Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_recurringClientIdIsSame_whenSuperuserOverridesRenewal() throws Exception {
|
||||
persistDomain();
|
||||
setClientIdForFlow("NewRegistrar");
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response.xml",
|
||||
5,
|
||||
"NewRegistrar",
|
||||
UserPrivileges.SUPERUSER,
|
||||
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-04-03T22:00:00.0Z"),
|
||||
Money.of(USD, 55));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_customLogicFee() throws Exception {
|
||||
// The "costly-renew" domain has an additional RENEW fee of 100 from custom logic on top of the
|
||||
@@ -269,7 +287,13 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
"FEE", "111.00");
|
||||
setEppInput("domain_renew_fee.xml", customFeeMap);
|
||||
persistDomain();
|
||||
doSuccessfulTest("domain_renew_response_fee.xml", 1, customFeeMap, Money.of(USD, 111));
|
||||
doSuccessfulTest(
|
||||
"domain_renew_response_fee.xml",
|
||||
1,
|
||||
"TheRegistrar",
|
||||
UserPrivileges.NORMAL,
|
||||
customFeeMap,
|
||||
Money.of(USD, 111));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -687,7 +711,7 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
|
||||
@Test
|
||||
public void testFailure_unauthorizedClient() throws Exception {
|
||||
sessionMetadata.setClientId("NewRegistrar");
|
||||
setClientIdForFlow("NewRegistrar");
|
||||
persistActiveDomain(getUniqueIdFromCommand());
|
||||
EppException thrown = assertThrows(ResourceNotOwnedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -695,7 +719,7 @@ public class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, D
|
||||
|
||||
@Test
|
||||
public void testSuccess_superuserUnauthorizedClient() throws Exception {
|
||||
sessionMetadata.setClientId("NewRegistrar");
|
||||
setClientIdForFlow("NewRegistrar");
|
||||
persistDomain();
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE,
|
||||
|
||||
@@ -19,8 +19,8 @@ import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatastoreHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
@@ -135,10 +135,10 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.TECH, Key.create(mak21Contact)),
|
||||
DesignatedContact.create(Type.ADMIN, Key.create(mak21Contact)),
|
||||
DesignatedContact.create(Type.BILLING, Key.create(mak21Contact))))
|
||||
.setRegistrant(Key.create(mak21Contact))
|
||||
DesignatedContact.create(Type.TECH, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, mak21Contact.createVKey()),
|
||||
DesignatedContact.create(Type.BILLING, mak21Contact.createVKey())))
|
||||
.setRegistrant(mak21Contact.createVKey())
|
||||
.setNameservers(ImmutableSet.of(host.createKey()))
|
||||
.build());
|
||||
historyEntryDomainCreate =
|
||||
@@ -160,8 +160,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact)),
|
||||
DesignatedContact.create(Type.ADMIN, Key.create(unusedContact))))
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.ADMIN, unusedContact.createVKey())))
|
||||
.setNameservers(ImmutableSet.of(host.createKey()))
|
||||
.build());
|
||||
historyEntryDomainCreate =
|
||||
@@ -298,7 +298,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
contactsBuilder.add(
|
||||
DesignatedContact.create(
|
||||
DesignatedContact.Type.values()[i % 4],
|
||||
Key.create(persistActiveContact(String.format("max_test_%d", i)))));
|
||||
persistActiveContact(String.format("max_test_%d", i)).createVKey()));
|
||||
}
|
||||
ImmutableList<DesignatedContact> contacts = contactsBuilder.build();
|
||||
persistResource(
|
||||
@@ -319,8 +319,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
assertThat(domain.getNameservers()).hasSize(13);
|
||||
// getContacts does not return contacts of type REGISTRANT, so check these separately.
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
assertThat(ofy().load().key(domain.getRegistrant()).now().getContactId())
|
||||
.isEqualTo("max_test_7");
|
||||
assertThat(tm().load(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
|
||||
assertNoBillingEvents();
|
||||
assertDnsTasksEnqueued("example.tld");
|
||||
}
|
||||
@@ -403,7 +402,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
persistResource(
|
||||
newDomainBase(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setRegistrant(Key.create(sh8013))
|
||||
.setRegistrant(sh8013.createVKey())
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
@@ -415,7 +414,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
persistReferencedEntities();
|
||||
ContactResource sh8013 =
|
||||
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get();
|
||||
Key<ContactResource> sh8013Key = Key.create(sh8013);
|
||||
VKey<ContactResource> sh8013Key = sh8013.createVKey();
|
||||
persistResource(
|
||||
newDomainBase(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
@@ -866,8 +865,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.setContacts(
|
||||
DesignatedContact.create(
|
||||
Type.TECH,
|
||||
Key.create(
|
||||
loadByForeignKey(ContactResource.class, "foo", clock.nowUtc()).get())))
|
||||
loadByForeignKey(ContactResource.class, "foo", clock.nowUtc())
|
||||
.get()
|
||||
.createVKey()))
|
||||
.build());
|
||||
EppException thrown = assertThrows(DuplicateContactForRoleException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1077,8 +1077,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.setContacts(
|
||||
DesignatedContact.create(
|
||||
Type.TECH,
|
||||
Key.create(
|
||||
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc()).get())))
|
||||
loadByForeignKey(ContactResource.class, "sh8013", clock.nowUtc())
|
||||
.get()
|
||||
.createVKey()))
|
||||
.build());
|
||||
EppException thrown = assertThrows(AddRemoveSameValueException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1093,8 +1094,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
||||
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
EppException thrown = assertThrows(MissingAdminContactException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1109,8 +1110,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, Key.create(sh8013Contact)),
|
||||
DesignatedContact.create(Type.TECH, Key.create(sh8013Contact))))
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
EppException thrown = assertThrows(MissingTechnicalContactException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -1223,7 +1224,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
|
||||
.build());
|
||||
runFlow();
|
||||
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId())
|
||||
assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
@@ -1237,10 +1238,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.getContacts()
|
||||
.forEach(
|
||||
contact -> {
|
||||
assertThat(ofy().load().key(contact.getContactKey()).now().getContactId())
|
||||
.isEqualTo("mak21");
|
||||
assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("mak21");
|
||||
});
|
||||
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId())
|
||||
assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
.isEqualTo("mak21");
|
||||
|
||||
runFlow();
|
||||
@@ -1249,10 +1249,9 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
|
||||
.getContacts()
|
||||
.forEach(
|
||||
contact -> {
|
||||
assertThat(ofy().load().key(contact.getContactKey()).now().getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
assertThat(tm().load(contact.getContactKey()).getContactId()).isEqualTo("sh8013");
|
||||
});
|
||||
assertThat(ofy().load().key(reloadResourceByForeignKey().getRegistrant()).now().getContactId())
|
||||
assertThat(tm().load(reloadResourceByForeignKey().getRegistrant()).getContactId())
|
||||
.isEqualTo("sh8013");
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.googlecode.objectify.ObjectifyService;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -35,19 +33,18 @@ import org.junit.runners.JUnit4;
|
||||
public class CreateAutoTimestampTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
/** Timestamped class. */
|
||||
@Entity
|
||||
@Entity(name = "CatTestEntity")
|
||||
public static class TestObject extends CrossTldSingleton {
|
||||
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ObjectifyService.register(TestObject.class);
|
||||
}
|
||||
|
||||
private TestObject reload() {
|
||||
return ofy().load().entity(new TestObject()).now();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.ObjectifyService;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
@@ -39,7 +38,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -50,12 +48,11 @@ import org.junit.runners.JUnit4;
|
||||
public class ImmutableObjectTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
@Before
|
||||
public void register() {
|
||||
ObjectifyService.register(ValueObject.class);
|
||||
}
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(ValueObject.class)
|
||||
.build();
|
||||
|
||||
/** Simple subclass of ImmutableObject. */
|
||||
public static class SimpleObject extends ImmutableObject {
|
||||
|
||||
@@ -19,12 +19,10 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.googlecode.objectify.ObjectifyService;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -35,19 +33,18 @@ import org.junit.runners.JUnit4;
|
||||
public class UpdateAutoTimestampTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
/** Timestamped class. */
|
||||
@Entity
|
||||
@Entity(name = "UatTestEntity")
|
||||
public static class TestObject extends CrossTldSingleton {
|
||||
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ObjectifyService.register(TestObject.class);
|
||||
}
|
||||
|
||||
private TestObject reload() {
|
||||
return ofy().load().entity(new TestObject()).now();
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@ package google.registry.model.contact;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.ContactResourceSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatastoreHelper.cloneAndSetAutoTimestamps;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
@@ -37,87 +40,118 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import google.registry.persistence.VKey;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link ContactResource}. */
|
||||
public class ContactResourceTest extends EntityTestCase {
|
||||
ContactResource originalContact;
|
||||
ContactResource contactResource;
|
||||
|
||||
@Before
|
||||
public ContactResourceTest() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
createTld("foobar");
|
||||
originalContact =
|
||||
new ContactResource.Builder()
|
||||
.setContactId("contact_id")
|
||||
.setRepoId("1-FOOBAR")
|
||||
.setCreationClientId("registrar1")
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateClientId("registrar2")
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setPersistedCurrentSponsorClientId("registrar3")
|
||||
.setLocalizedPostalInfo(
|
||||
new PostalInfo.Builder()
|
||||
.setType(Type.LOCALIZED)
|
||||
.setAddress(
|
||||
new ContactAddress.Builder()
|
||||
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
|
||||
.setCity("New York")
|
||||
.setState("NY")
|
||||
.setZip("10011")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.build())
|
||||
.setInternationalizedPostalInfo(
|
||||
new PostalInfo.Builder()
|
||||
.setType(Type.INTERNATIONALIZED)
|
||||
.setAddress(
|
||||
new ContactAddress.Builder()
|
||||
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
|
||||
.setCity("New York")
|
||||
.setState("NY")
|
||||
.setZip("10011")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.build())
|
||||
.setVoiceNumber(new ContactPhoneNumber.Builder().setPhoneNumber("867-5309").build())
|
||||
.setFaxNumber(
|
||||
new ContactPhoneNumber.Builder()
|
||||
.setPhoneNumber("867-5309")
|
||||
.setExtension("1000")
|
||||
.build())
|
||||
.setEmailAddress("jenny@example.com")
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("passw0rd")))
|
||||
.setDisclose(
|
||||
new Disclose.Builder()
|
||||
.setVoice(new PresenceMarker())
|
||||
.setEmail(new PresenceMarker())
|
||||
.setFax(new PresenceMarker())
|
||||
.setFlag(true)
|
||||
.setAddrs(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.setNames(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.setOrgs(ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.build())
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.OK))
|
||||
.setTransferData(
|
||||
new TransferData.Builder()
|
||||
.setGainingClientId("gaining")
|
||||
.setLosingClientId("losing")
|
||||
.setPendingTransferExpirationTime(fakeClock.nowUtc())
|
||||
.setServerApproveEntities(
|
||||
ImmutableSet.of(Key.create(BillingEvent.OneTime.class, 1)))
|
||||
.setTransferRequestTime(fakeClock.nowUtc())
|
||||
.setTransferStatus(TransferStatus.SERVER_APPROVED)
|
||||
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
|
||||
.build())
|
||||
.build();
|
||||
// Set up a new persisted ContactResource entity.
|
||||
contactResource =
|
||||
persistResource(
|
||||
cloneAndSetAutoTimestamps(
|
||||
new ContactResource.Builder()
|
||||
.setContactId("contact_id")
|
||||
.setRepoId("1-FOOBAR")
|
||||
.setCreationClientId("a registrar")
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateClientId("another registrar")
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setPersistedCurrentSponsorClientId("a third registrar")
|
||||
.setLocalizedPostalInfo(
|
||||
new PostalInfo.Builder()
|
||||
.setType(Type.LOCALIZED)
|
||||
.setAddress(
|
||||
new ContactAddress.Builder()
|
||||
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
|
||||
.setCity("New York")
|
||||
.setState("NY")
|
||||
.setZip("10011")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.build())
|
||||
.setInternationalizedPostalInfo(
|
||||
new PostalInfo.Builder()
|
||||
.setType(Type.INTERNATIONALIZED)
|
||||
.setAddress(
|
||||
new ContactAddress.Builder()
|
||||
.setStreet(ImmutableList.of("111 8th Ave", "4th Floor"))
|
||||
.setCity("New York")
|
||||
.setState("NY")
|
||||
.setZip("10011")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.build())
|
||||
.setVoiceNumber(
|
||||
new ContactPhoneNumber.Builder().setPhoneNumber("867-5309").build())
|
||||
.setFaxNumber(
|
||||
new ContactPhoneNumber.Builder()
|
||||
.setPhoneNumber("867-5309")
|
||||
.setExtension("1000")
|
||||
.build())
|
||||
.setEmailAddress("jenny@example.com")
|
||||
.setAuthInfo(ContactAuthInfo.create(PasswordAuth.create("passw0rd")))
|
||||
.setDisclose(
|
||||
new Disclose.Builder()
|
||||
.setVoice(new PresenceMarker())
|
||||
.setEmail(new PresenceMarker())
|
||||
.setFax(new PresenceMarker())
|
||||
.setFlag(true)
|
||||
.setAddrs(
|
||||
ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.setNames(
|
||||
ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.setOrgs(
|
||||
ImmutableList.of(PostalInfoChoice.create(Type.INTERNATIONALIZED)))
|
||||
.build())
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.OK))
|
||||
.setTransferData(
|
||||
new TransferData.Builder()
|
||||
.setGainingClientId("gaining")
|
||||
.setLosingClientId("losing")
|
||||
.setPendingTransferExpirationTime(fakeClock.nowUtc())
|
||||
.setServerApproveEntities(
|
||||
ImmutableSet.of(Key.create(BillingEvent.OneTime.class, 1)))
|
||||
.setTransferRequestTime(fakeClock.nowUtc())
|
||||
.setTransferStatus(TransferStatus.SERVER_APPROVED)
|
||||
.setTransferRequestTrid(Trid.create("client-trid", "server-trid"))
|
||||
.build())
|
||||
.build()));
|
||||
contactResource = persistResource(cloneAndSetAutoTimestamps(originalContact));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloudSqlPersistence_failWhenViolateForeignKeyConstraint() {
|
||||
assertThrowForeignKeyViolation(() -> jpaTm().transact(() -> jpaTm().saveNew(originalContact)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloudSqlPersistence_succeed() {
|
||||
saveRegistrar("registrar1");
|
||||
saveRegistrar("registrar2");
|
||||
saveRegistrar("registrar3");
|
||||
jpaTm().transact(() -> jpaTm().saveNew(originalContact));
|
||||
ContactResource persisted =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm()
|
||||
.load(VKey.createSql(ContactResource.class, originalContact.getRepoId())));
|
||||
// TODO(b/153378849): Remove the hard code for postal info after resolving the issue that
|
||||
// @PostLoad doesn't work in Address
|
||||
ContactResource fixed =
|
||||
originalContact
|
||||
.asBuilder()
|
||||
.setCreationTime(persisted.getCreationTime())
|
||||
.setInternationalizedPostalInfo(persisted.getInternationalizedPostalInfo())
|
||||
.setLocalizedPostalInfo(persisted.getLocalizedPostalInfo())
|
||||
.setTransferData(null)
|
||||
.build();
|
||||
assertThat(persisted).isEqualTo(fixed);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,17 +16,21 @@ package google.registry.model.domain;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.secdns.DelegationSignerData;
|
||||
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
|
||||
import google.registry.testing.DatastoreEntityExtension;
|
||||
@@ -52,22 +56,32 @@ public class DomainBaseSqlTest {
|
||||
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
|
||||
|
||||
DomainBase domain;
|
||||
Key<ContactResource> contactKey;
|
||||
Key<ContactResource> contact2Key;
|
||||
VKey<ContactResource> contactKey;
|
||||
VKey<ContactResource> contact2Key;
|
||||
VKey<HostResource> host1VKey;
|
||||
HostResource host;
|
||||
ContactResource contact;
|
||||
ContactResource contact2;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
contactKey = Key.create(ContactResource.class, "contact_id1");
|
||||
contact2Key = Key.create(ContactResource.class, "contact_id2");
|
||||
saveRegistrar("registrar1");
|
||||
saveRegistrar("registrar2");
|
||||
saveRegistrar("registrar3");
|
||||
contactKey = VKey.createSql(ContactResource.class, "contact_id1");
|
||||
contact2Key = VKey.createSql(ContactResource.class, "contact_id2");
|
||||
|
||||
host1VKey = VKey.createSql(HostResource.class, "host1");
|
||||
|
||||
domain =
|
||||
new DomainBase.Builder()
|
||||
.setFullyQualifiedDomainName("example.com")
|
||||
.setRepoId("4-COM")
|
||||
.setCreationClientId("a registrar")
|
||||
.setCreationClientId("registrar1")
|
||||
.setLastEppUpdateTime(fakeClock.nowUtc())
|
||||
.setLastEppUpdateClientId("AnotherRegistrar")
|
||||
.setLastEppUpdateClientId("registrar2")
|
||||
.setLastTransferTime(fakeClock.nowUtc())
|
||||
.setNameservers(host1VKey)
|
||||
.setStatusValues(
|
||||
ImmutableSet.of(
|
||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||
@@ -79,7 +93,7 @@ public class DomainBaseSqlTest {
|
||||
.setRegistrant(contactKey)
|
||||
.setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
|
||||
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
|
||||
.setPersistedCurrentSponsorClientId("losing")
|
||||
.setPersistedCurrentSponsorClientId("registrar3")
|
||||
.setRegistrationExpirationTime(fakeClock.nowUtc().plusYears(1))
|
||||
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
|
||||
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
@@ -87,6 +101,16 @@ public class DomainBaseSqlTest {
|
||||
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
|
||||
.setSmdId("smdid")
|
||||
.build();
|
||||
|
||||
host =
|
||||
new HostResource.Builder()
|
||||
.setRepoId("host1")
|
||||
.setFullyQualifiedHostName("ns1.example.com")
|
||||
.setCreationClientId("registrar1")
|
||||
.setPersistedCurrentSponsorClientId("registrar2")
|
||||
.build();
|
||||
contact = makeContact("contact_id1");
|
||||
contact2 = makeContact("contact_id2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,9 +118,17 @@ public class DomainBaseSqlTest {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Persist the contacts. Note that these need to be persisted before the domain
|
||||
// otherwise we get a foreign key constraint error.
|
||||
jpaTm().saveNew(contact);
|
||||
jpaTm().saveNew(contact2);
|
||||
|
||||
// Persist the domain.
|
||||
EntityManager em = jpaTm().getEntityManager();
|
||||
em.persist(domain);
|
||||
jpaTm().saveNew(domain);
|
||||
|
||||
// Persist the host. This does _not_ need to be persisted before the domain,
|
||||
// presumably because its relationship is stored in a join table.
|
||||
jpaTm().saveNew(host);
|
||||
});
|
||||
|
||||
jpaTm()
|
||||
@@ -106,13 +138,11 @@ public class DomainBaseSqlTest {
|
||||
EntityManager em = jpaTm().getEntityManager();
|
||||
DomainBase result = em.find(DomainBase.class, "4-COM");
|
||||
|
||||
// Fix contacts, grace period and DS data, since we can't persist them yet.
|
||||
// Fix grace period and DS data, since we can't persist them yet.
|
||||
result =
|
||||
result
|
||||
.asBuilder()
|
||||
.setRegistrant(contactKey)
|
||||
.setContacts(
|
||||
ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
|
||||
.setDsData(
|
||||
ImmutableSet.of(
|
||||
DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
@@ -125,4 +155,42 @@ public class DomainBaseSqlTest {
|
||||
assertThat(result).isEqualTo(org);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHostForeignKeyConstraints() {
|
||||
assertThrowForeignKeyViolation(
|
||||
() -> {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Persist the domain without the associated host object.
|
||||
jpaTm().saveNew(contact);
|
||||
jpaTm().saveNew(contact2);
|
||||
jpaTm().saveNew(domain);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContactForeignKeyConstraints() {
|
||||
assertThrowForeignKeyViolation(
|
||||
() -> {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
// Persist the domain without the associated contact objects.
|
||||
jpaTm().saveNew(domain);
|
||||
jpaTm().saveNew(host);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static ContactResource makeContact(String repoId) {
|
||||
return new ContactResource.Builder()
|
||||
.setRepoId(repoId)
|
||||
.setCreationClientId("registrar1")
|
||||
.setTransferData(new TransferData.Builder().build())
|
||||
.setPersistedCurrentSponsorClientId("registrar1")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,20 +80,20 @@ public class DomainBaseTest extends EntityTestCase {
|
||||
.setRepoId("1-COM")
|
||||
.build())
|
||||
.createKey();
|
||||
Key<ContactResource> contact1Key =
|
||||
Key.create(
|
||||
persistResource(
|
||||
VKey<ContactResource> contact1Key =
|
||||
persistResource(
|
||||
new ContactResource.Builder()
|
||||
.setContactId("contact_id1")
|
||||
.setRepoId("2-COM")
|
||||
.build()));
|
||||
Key<ContactResource> contact2Key =
|
||||
Key.create(
|
||||
persistResource(
|
||||
.build())
|
||||
.createVKey();
|
||||
VKey<ContactResource> contact2Key =
|
||||
persistResource(
|
||||
new ContactResource.Builder()
|
||||
.setContactId("contact_id2")
|
||||
.setRepoId("3-COM")
|
||||
.build()));
|
||||
.build())
|
||||
.createVKey();
|
||||
Key<HistoryEntry> historyEntryKey =
|
||||
Key.create(persistResource(new HistoryEntry.Builder().setParent(domainKey).build()));
|
||||
oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1);
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright 2019 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.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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.persistence.VKey;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectRule;
|
||||
import java.util.NoSuchElementException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class DatastoreTransactionManagerTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
private final TestEntity theEntity = new TestEntity("theEntity", "foo");
|
||||
private final ImmutableList<TestEntity> moreEntities =
|
||||
ImmutableList.of(
|
||||
new TestEntity("entity1", "foo"),
|
||||
new TestEntity("entity2", "bar"),
|
||||
new TestEntity("entity3", "qux"));
|
||||
|
||||
@RegisterExtension public InjectRule inject = new InjectRule();
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withClock(fakeClock)
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestEntity.class)
|
||||
.build();
|
||||
|
||||
public DatastoreTransactionManagerTest() {}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
inject.setStaticField(Ofy.class, "clock", fakeClock);
|
||||
}
|
||||
|
||||
// TODO(mmuller): The tests below are just copy-pasted from JpaTransactionManagerImplTest
|
||||
// (excluding the CompoundId tests and native query tests, which are not relevant to datastore,
|
||||
// and the test methods using "count" which doesn't work for datastore, as well as tests for
|
||||
// functionality that doesn't exist in datastore, like failures based on whether a newly saved or
|
||||
// updated object exists or not). We need to merge these into a single test suite, but first we
|
||||
// should move the JpaUnitTestRule functionality into AppEngineTest and migrate the whole thing
|
||||
// to junit5.
|
||||
|
||||
@Test
|
||||
public void inTransaction_returnsCorrespondingResult() {
|
||||
assertThat(tm().inTransaction()).isFalse();
|
||||
tm().transact(() -> assertThat(tm().inTransaction()).isTrue());
|
||||
assertThat(tm().inTransaction()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertInTransaction_throwsExceptionWhenNotInTransaction() {
|
||||
assertThrows(IllegalStateException.class, () -> tm().assertInTransaction());
|
||||
tm().transact(() -> tm().assertInTransaction());
|
||||
assertThrows(IllegalStateException.class, () -> tm().assertInTransaction());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTransactionTime_throwsExceptionWhenNotInTransaction() {
|
||||
FakeClock txnClock = fakeClock;
|
||||
txnClock.advanceOneMilli();
|
||||
assertThrows(IllegalStateException.class, () -> tm().getTransactionTime());
|
||||
tm().transact(() -> assertThat(tm().getTransactionTime()).isEqualTo(txnClock.nowUtc()));
|
||||
assertThrows(IllegalStateException.class, () -> tm().getTransactionTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transact_hasNoEffectWithPartialSuccess() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() ->
|
||||
tm()
|
||||
.transact(
|
||||
() -> {
|
||||
tm().saveNew(theEntity);
|
||||
throw new RuntimeException();
|
||||
}));
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transact_reusesExistingTransaction() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().transact(() -> tm().saveNew(theEntity)));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNew_succeeds() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isTrue();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().load(theEntity.key()))).isEqualTo(theEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveAllNew_succeeds() {
|
||||
moreEntities.forEach(
|
||||
entity -> assertThat(tm().transact(() -> tm().checkExists(entity))).isFalse());
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveAllNew(moreEntities));
|
||||
fakeClock.advanceOneMilli();
|
||||
moreEntities.forEach(
|
||||
entity -> assertThat(tm().transact(() -> tm().checkExists(entity))).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNewOrUpdate_persistsNewEntity() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNewOrUpdate(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isTrue();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().load(theEntity.key()))).isEqualTo(theEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNewOrUpdate_updatesExistingEntity() {
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
TestEntity persisted = tm().transact(() -> tm().load(theEntity.key()));
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
theEntity.data = "bar";
|
||||
tm().transact(() -> tm().saveNewOrUpdate(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
persisted = tm().transact(() -> tm().load(theEntity.key()));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(persisted.data).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNewOrUpdateAll_succeeds() {
|
||||
moreEntities.forEach(
|
||||
entity -> assertThat(tm().transact(() -> tm().checkExists(entity))).isFalse());
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNewOrUpdateAll(moreEntities));
|
||||
fakeClock.advanceOneMilli();
|
||||
moreEntities.forEach(
|
||||
entity -> assertThat(tm().transact(() -> tm().checkExists(entity))).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void update_succeeds() {
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().load(VKey.createOfy(TestEntity.class, Key.create(theEntity))));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
theEntity.data = "bar";
|
||||
tm().transact(() -> tm().update(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
persisted = tm().transact(() -> tm().load(theEntity.key()));
|
||||
assertThat(persisted.data).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void load_succeeds() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
TestEntity persisted = tm().transact(() -> tm().load(theEntity.key()));
|
||||
assertThat(persisted.name).isEqualTo("theEntity");
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void load_throwsOnMissingElement() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThrows(
|
||||
NoSuchElementException.class, () -> tm().transact(() -> tm().load(theEntity.key())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeLoad_succeeds() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
TestEntity persisted = tm().transact(() -> tm().maybeLoad(theEntity.key()).get());
|
||||
assertThat(persisted.name).isEqualTo("theEntity");
|
||||
assertThat(persisted.data).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeLoad_nonExistentObject() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().maybeLoad(theEntity.key())).isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete_succeeds() {
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().saveNew(theEntity));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isTrue();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().delete(theEntity.key()));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete_returnsZeroWhenNoEntity() {
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
fakeClock.advanceOneMilli();
|
||||
tm().transact(() -> tm().delete(theEntity.key()));
|
||||
fakeClock.advanceOneMilli();
|
||||
assertThat(tm().transact(() -> tm().checkExists(theEntity))).isFalse();
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
private static class TestEntity extends ImmutableObject {
|
||||
@Id private String name;
|
||||
|
||||
private String data;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(String name, String data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public VKey<TestEntity> key() {
|
||||
return VKey.createOfy(TestEntity.class, Key.create(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ package google.registry.model.ofy;
|
||||
|
||||
import static com.google.appengine.api.datastore.EntityTranslator.convertToPb;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.googlecode.objectify.ObjectifyService.register;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.CommitLogBucket.getBucketKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
@@ -47,7 +46,11 @@ import org.junit.runners.JUnit4;
|
||||
public class OfyCommitLogTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestVirtualObject.class, Root.class, Child.class)
|
||||
.build();
|
||||
|
||||
@Rule
|
||||
public final InjectRule inject = new InjectRule();
|
||||
@@ -56,8 +59,6 @@ public class OfyCommitLogTest {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
register(Root.class);
|
||||
register(Child.class);
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.model.server.Lock.LockState;
|
||||
import google.registry.schema.server.LockDao;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectRule;
|
||||
@@ -65,19 +64,16 @@ public class LockTest {
|
||||
Optional<Lock> lock = Lock.acquire(RESOURCE_NAME, tld, leaseLength, requestStatusChecker, true);
|
||||
verify(Lock.lockMetrics).recordAcquire(RESOURCE_NAME, tld, expectedLockState);
|
||||
verifyNoMoreInteractions(Lock.lockMetrics);
|
||||
assertThat(LockDao.load(RESOURCE_NAME, tld)).isPresent();
|
||||
Lock.lockMetrics = null;
|
||||
return lock;
|
||||
}
|
||||
|
||||
private void release(Lock lock, String expectedTld, long expectedMillis) {
|
||||
assertThat(LockDao.load(RESOURCE_NAME, expectedTld)).isPresent();
|
||||
Lock.lockMetrics = mock(LockMetrics.class);
|
||||
lock.release();
|
||||
verify(Lock.lockMetrics)
|
||||
.recordRelease(RESOURCE_NAME, expectedTld, Duration.millis(expectedMillis));
|
||||
verifyNoMoreInteractions(Lock.lockMetrics);
|
||||
assertThat(LockDao.load(RESOURCE_NAME, expectedTld)).isEmpty();
|
||||
Lock.lockMetrics = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import static org.joda.time.Duration.standardHours;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.ObjectifyService;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.model.ofy.CommitLogManifest;
|
||||
@@ -45,13 +44,17 @@ public class CommitLogRevisionsTranslatorFactoryTest {
|
||||
|
||||
private static final DateTime START_TIME = DateTime.parse("2000-01-01TZ");
|
||||
|
||||
@Entity
|
||||
@Entity(name = "ClrtfTestEntity")
|
||||
public static class TestObject extends CrossTldSingleton {
|
||||
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
public final AppEngineRule appEngine =
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
@Rule
|
||||
public final InjectRule inject = new InjectRule();
|
||||
@@ -60,7 +63,6 @@ public class CommitLogRevisionsTranslatorFactoryTest {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ObjectifyService.register(TestObject.class);
|
||||
inject.setStaticField(Ofy.class, "clock", clock);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,14 +69,14 @@ public class EntityCallbacksListenerTest {
|
||||
checkAll(updated, 0, 1, 0, 1);
|
||||
|
||||
TestEntity testLoad =
|
||||
jpaTm().transact(() -> jpaTm().load(VKey.createSql(TestEntity.class, "id"))).get();
|
||||
jpaTm().transact(() -> jpaTm().load(VKey.createSql(TestEntity.class, "id")));
|
||||
checkAll(testLoad, 0, 0, 0, 1);
|
||||
|
||||
TestEntity testRemove =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
TestEntity removed = jpaTm().load(VKey.createSql(TestEntity.class, "id")).get();
|
||||
TestEntity removed = jpaTm().load(VKey.createSql(TestEntity.class, "id"));
|
||||
jpaTm().getEntityManager().remove(removed);
|
||||
return removed;
|
||||
});
|
||||
|
||||
@@ -28,15 +28,19 @@ public class VKeyTest {
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngineRule =
|
||||
AppEngineRule.builder().withDatastoreAndCloudSql().build();
|
||||
AppEngineRule.builder()
|
||||
.withDatastoreAndCloudSql()
|
||||
.withOfyTestEntities(TestObject.class)
|
||||
.build();
|
||||
|
||||
public VKeyTest() {}
|
||||
|
||||
@Test
|
||||
public void testOptionalAccessors() {
|
||||
VKey<TestObject> key = VKey.create(TestObject.class, null, null);
|
||||
assertThat(key.maybeGetSqlKey().isPresent()).isFalse();
|
||||
assertThat(key.maybeGetOfyKey().isPresent()).isFalse();
|
||||
VKey<TestObject> key =
|
||||
VKey.create(TestObject.class, "foo", Key.create(TestObject.create("foo")));
|
||||
assertThat(key.maybeGetSqlKey().isPresent()).isTrue();
|
||||
assertThat(key.maybeGetOfyKey().isPresent()).isTrue();
|
||||
|
||||
Key<TestObject> ofyKey = Key.create(TestObject.create("foo"));
|
||||
assertThat(VKey.createOfy(TestObject.class, ofyKey).maybeGetOfyKey().get()).isEqualTo(ofyKey);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.registry.Registry.BillingCostTransition;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link BillingCostTransitionConverter}. */
|
||||
public class BillingCostTransitionConverterTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaUnitTestRule jpa =
|
||||
new JpaTestRules.Builder()
|
||||
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
|
||||
.withEntityClass(TestEntity.class)
|
||||
.buildUnitTestRule();
|
||||
|
||||
private static final ImmutableSortedMap<DateTime, Money> values =
|
||||
ImmutableSortedMap.of(
|
||||
START_OF_TIME,
|
||||
Money.of(USD, 8),
|
||||
DateTime.parse("2001-01-01T00:00:00.0Z"),
|
||||
Money.of(USD, 0));
|
||||
|
||||
@Test
|
||||
void roundTripConversion_returnsSameTimedTransitionProperty() {
|
||||
TimedTransitionProperty<Money, BillingCostTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, BillingCostTransition.class);
|
||||
TestEntity testEntity = new TestEntity(timedTransitionProperty);
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
|
||||
TestEntity persisted =
|
||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.timedTransitionProperty).containsExactlyEntriesIn(timedTransitionProperty);
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
private static class TestEntity extends ImmutableObject {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<Money, BillingCostTransition> timedTransitionProperty;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(
|
||||
TimedTransitionProperty<Money, BillingCostTransition> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import com.google.common.hash.BloomFilter;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import org.junit.Rule;
|
||||
@@ -50,6 +51,7 @@ public class BloomFilterConverterTest {
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
||||
@EntityForTesting
|
||||
public static class TestEntity extends ImmutableObject {
|
||||
|
||||
@Id String name = "id";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user