Compare commits

...
Author SHA1 Message Date
Lai JiangandGitHub 5e596bb389 Upgrade to Gradle 6.4 (#589) 2020-05-14 14:57:24 -04:00
Lai JiangandGitHub f62fd82803 Log information about SSL connection from the client (#586) 2020-05-14 09:38:33 -04:00
sarahcaseybotandGitHub b7353ef338 Add TimedTransitionProperty Converters (#561)
* Add TimedTldStateTransitionMapConverter

* Move timedTransitions to a base class and add BillingCostTransitionConverter

* Add test of TimedTransitionPropertyConverterBase

* clean up tests

* Switch tests to JUnit 5

* Make JpaUnitTestRule an extension
2020-05-12 11:46:19 -04:00
Michael MullerandGitHub 832e1ce047 Implement all DatastoreTransactionManager methods (#581)
* Implement all DatastoreTransactionManager methods

In the course of this:

- Make assertDelete() specific to JpaTransactionManager, remove the return
  value from delete()
- Converter "in transaction" assertion to IllegalStateException, which is less
  JPA specific.

* Upgraded DatastoreTransactionManagerTest to junit5
2020-05-11 17:17:57 -04:00
0xflotusandGitHub 8087f5bbca (docs): fixed small errors (#572)
* Update first-steps-tutorial.md

* Update proxy-setup.md (#1)

* Update registrar-faq.md (#2)

* Update first-steps-tutorial.md
2020-05-11 10:01:47 -04:00
Ben McIlwainandGitHub 7f3dbfb62f Reflect refunded billing events on deletion in expiration time (#579)
* Reflect refunded billing events on deletion in expiration time

This doesn't make any change at the time of the domain deletion itself, but it
will matter if the domain is then undeleted, because we need to know what
expiration date to restore, and if there were any renew or autorenew charges
that were refunded by the deletion because they were in a grace period, they
shouldn't be coming back during the restore.

* Add tests for new expiration date behavior

* Add handling of add/renew grace period overlap
2020-05-08 21:51:20 -04:00
Michael MullerandGitHub 04f429c4d6 Convert DomainBase's contacts to VKeys (#574)
* Convert DomainBase's contacts to VKeys

Convert usage of DomainBase contacts from Key to VKey.  This is the same
change as done for nameserver hosts, as it affects all external interfaces.
As with nameserver hosts, we preserve the existing representation so as not to
afffect the datastore representation.
2020-05-07 11:19:15 -04:00
Lai JiangandGitHub 40a4c3101c Fix test failures on certain machines (#571)
On certain machines (one of mine) the outcastTest consistently fails due
to the following error:

java.lang.NoClassDefFoundError: Could not initialize class
google.registry.persistence.transaction.JpaTestRules$JpaIntegrationTestRule

If I reduce maxparallelForks to 3 it consistently passes. This issue was
mentioned here:

https://discuss.gradle.org/t/junit-test-fails-with-noclassdeffounderror-only-when-maxparallelforks-1/6047

But this post was 8 years old and no solution was identified.
2020-05-04 11:38:54 -04:00
Michael MullerandGitHub e2dfb6488d Improve return value semantices for tm().load() (#576)
Since we rarely (if ever) want to check the result of a single element load,
make TransactionManager.load(VKey) return non-optional, non-nullable and just
throw a NoSuchElementException if the entity is not found.

Also add a maybeLoad() that does return an optional in case we ever want to do
this (exists() should suffice for an existence check).
2020-05-04 10:49:36 -04:00
66 changed files with 1834 additions and 268 deletions
+4 -1
View File
@@ -709,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
@@ -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()
@@ -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) {
@@ -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,6 +31,8 @@ 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;
@@ -60,6 +63,7 @@ import org.joda.time.DateTime;
@javax.persistence.Index(columnList = "searchName")
})
@ExternalMessagingName("contact")
@WithStringVKey
public class ContactResource extends EppResource
implements DatastoreAndSqlEntity, ForeignKeyedEppResource, ResourceWithTransferData {
@@ -193,6 +197,10 @@ public class ContactResource extends EppResource
})
Disclose disclose;
public VKey<ContactResource> createVKey() {
return VKey.createOfy(ContactResource.class, Key.create(this));
}
public String getContactId() {
return contactId;
}
@@ -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));
}
}
@@ -80,6 +80,7 @@ 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;
@@ -155,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({
@@ -261,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() {
@@ -515,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). */
@@ -537,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)
@@ -549,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);
@@ -593,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();
}
@@ -611,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();
}
@@ -673,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();
}
@@ -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);
@@ -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,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;
}
}
@@ -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",
@@ -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());
}
}
@@ -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",
@@ -35,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>
@@ -47,11 +48,13 @@
<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>
@@ -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")))
@@ -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");
}
@@ -140,8 +140,7 @@ public class ContactResourceTest extends EntityTestCase {
.transact(
() ->
jpaTm()
.load(VKey.createSql(ContactResource.class, originalContact.getRepoId())))
.get();
.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 =
@@ -22,7 +22,6 @@ 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;
@@ -30,6 +29,7 @@ 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;
@@ -56,15 +56,20 @@ 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");
@@ -104,23 +109,26 @@ public class DomainBaseSqlTest {
.setCreationClientId("registrar1")
.setPersistedCurrentSponsorClientId("registrar2")
.build();
contact = makeContact("contact_id1");
contact2 = makeContact("contact_id2");
}
@Test
public void testDomainBasePersistence() {
saveRegistrar("registrar1");
saveRegistrar("registrar2");
saveRegistrar("registrar3");
jpaTm()
.transact(
() -> {
// Persist the domain.
EntityManager em = jpaTm().getEntityManager();
em.persist(domain);
// 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 host.
em.persist(host);
// Persist the 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()
@@ -130,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})))
@@ -151,16 +157,40 @@ public class DomainBaseSqlTest {
}
@Test
public void testForeignKeyConstraints() {
public void testHostForeignKeyConstraints() {
assertThrowForeignKeyViolation(
() -> {
jpaTm()
.transact(
() -> {
// Persist the domain without the associated host object.
EntityManager em = jpaTm().getEntityManager();
em.persist(domain);
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));
}
}
}
@@ -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;
});
@@ -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;
}
}
}
@@ -0,0 +1,181 @@
// 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.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import google.registry.model.ImmutableObject;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import java.util.Map;
import javax.persistence.Converter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link TimedTransitionPropertyConverterBase}. */
public class TimedTransitionPropertyConverterBaseTest {
@RegisterExtension
public final JpaUnitTestRule jpa =
new JpaTestRules.Builder()
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
.withEntityClass(TestTimedTransitionPropertyConverter.class, TestEntity.class)
.buildUnitTestRule();
private static final DateTime DATE_1 = DateTime.parse("2001-01-01T00:00:00.000Z");
private static final DateTime DATE_2 = DateTime.parse("2002-01-01T00:00:00.000Z");
private static final ImmutableSortedMap<DateTime, String> VALUES =
ImmutableSortedMap.of(
START_OF_TIME, "val1",
DATE_1, "val2",
DATE_2, "val3");
private static final TimedTransitionProperty<String, TestTransition> TIMED_TRANSITION_PROPERTY =
TimedTransitionProperty.fromValueMap(VALUES, TestTransition.class);
@Test
void roundTripConversion_returnsSameTimedTransitionProperty() {
TestEntity testEntity = new TestEntity(TIMED_TRANSITION_PROPERTY);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.property).containsExactlyEntriesIn(TIMED_TRANSITION_PROPERTY);
}
@Test
void testUpdateColumn_succeeds() {
TestEntity testEntity = new TestEntity(TIMED_TRANSITION_PROPERTY);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.property).containsExactlyEntriesIn(TIMED_TRANSITION_PROPERTY);
ImmutableSortedMap<DateTime, String> newValues = ImmutableSortedMap.of(START_OF_TIME, "val4");
persisted.property = TimedTransitionProperty.fromValueMap(newValues, TestTransition.class);
jpaTm().transact(() -> jpaTm().getEntityManager().merge(persisted));
TestEntity updated =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(updated.property.toValueMap()).isEqualTo(newValues);
}
@Test
void testNullValue_writesAndReadsNullSuccessfully() {
TestEntity testEntity = new TestEntity(null);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.property).isNull();
}
@Test
void testNativeQuery_succeeds() {
executeNativeQuery(
"INSERT INTO \"TestEntity\" (name, property) VALUES ('id',"
+ " 'val1=>1970-01-01T00:00:00.000Z, val2=>2001-01-01T00:00:00.000Z')");
assertThat(
getSingleResultFromNativeQuery(
"SELECT property -> 'val1' FROM \"TestEntity\" WHERE name = 'id'"))
.isEqualTo(START_OF_TIME.toString());
assertThat(
getSingleResultFromNativeQuery(
"SELECT property -> 'val2' FROM \"TestEntity\" WHERE name = 'id'"))
.isEqualTo(DATE_1.toString());
executeNativeQuery(
"UPDATE \"TestEntity\" SET property = 'val3=>2002-01-01T00:00:00.000Z' WHERE name = 'id'");
assertThat(
getSingleResultFromNativeQuery(
"SELECT property -> 'val3' FROM \"TestEntity\" WHERE name = 'id'"))
.isEqualTo(DATE_2.toString());
executeNativeQuery("DELETE FROM \"TestEntity\" WHERE name = 'id'");
assertThrows(
NoResultException.class,
() ->
getSingleResultFromNativeQuery(
"SELECT property -> 'val3' FROM \"TestEntity\" WHERE name = 'id'"));
}
private static Object getSingleResultFromNativeQuery(String sql) {
return jpaTm()
.transact(() -> jpaTm().getEntityManager().createNativeQuery(sql).getSingleResult());
}
private static void executeNativeQuery(String sql) {
jpaTm().transact(() -> jpaTm().getEntityManager().createNativeQuery(sql).executeUpdate());
}
public static class TestTransition extends TimedTransition<String> {
private String transition;
@Override
public String getValue() {
return transition;
}
@Override
protected void setValue(String transition) {
this.transition = transition;
}
}
@Converter(autoApply = true)
private static class TestTimedTransitionPropertyConverter
extends TimedTransitionPropertyConverterBase<String, TestTransition> {
@Override
Map.Entry<DateTime, String> convertToEntityMapEntry(Map.Entry<String, String> entry) {
return Maps.immutableEntry(DateTime.parse(entry.getKey()), entry.getValue());
}
@Override
Class<TestTransition> getTimedTransitionSubclass() {
return TestTransition.class;
}
@Override
Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<DateTime, TestTransition> entry) {
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue());
}
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
TimedTransitionProperty<String, TestTransition> property;
private TestEntity() {}
private TestEntity(TimedTransitionProperty<String, TestTransition> timedTransitionProperty) {
this.property = timedTransitionProperty;
}
}
}
@@ -0,0 +1,80 @@
// 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 com.google.common.collect.ImmutableSortedMap;
import google.registry.model.ImmutableObject;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.Registry.TldStateTransition;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link TldStateTransitionConverter}. */
public class TldStateTransitionConverterTest {
@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, TldState> values =
ImmutableSortedMap.of(
START_OF_TIME,
TldState.PREDELEGATION,
DateTime.parse("2001-01-01T00:00:00.0Z"),
TldState.QUIET_PERIOD,
DateTime.parse("2002-01-01T00:00:00.0Z"),
TldState.PDT,
DateTime.parse("2003-01-01T00:00:00.0Z"),
TldState.GENERAL_AVAILABILITY);
@Test
void roundTripConversion_returnsSameTimedTransitionProperty() {
TimedTransitionProperty<TldState, TldStateTransition> timedTransitionProperty =
TimedTransitionProperty.fromValueMap(values, TldStateTransition.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<TldState, TldStateTransition> timedTransitionProperty;
private TestEntity() {}
private TestEntity(
TimedTransitionProperty<TldState, TldStateTransition> timedTransitionProperty) {
this.timedTransitionProperty = timedTransitionProperty;
}
}
}
@@ -60,10 +60,11 @@ public class JpaTestRules {
/**
* Junit rule for unit tests with JPA framework, when the underlying database is populated by the
* optional init script (which must not be the Nomulus Cloud SQL schema).
* optional init script (which must not be the Nomulus Cloud SQL schema). This rule can also be
* used as am extension for JUnit5 tests.
*/
public static class JpaUnitTestRule extends JpaTransactionManagerRule {
public static class JpaUnitTestRule extends JpaTransactionManagerRule
implements BeforeEachCallback, AfterEachCallback {
private JpaUnitTestRule(
Clock clock,
Optional<String> initScriptPath,
@@ -71,6 +72,16 @@ public class JpaTestRules {
ImmutableMap<String, String> userProperties) {
super(clock, initScriptPath, extraEntityClasses, userProperties);
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
this.before();
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
this.after();
}
}
/**
@@ -195,7 +206,9 @@ public class JpaTestRules {
return new JpaIntegrationWithCoverageExtension(buildIntegrationTestRule());
}
/** Builds a {@link JpaUnitTestRule} instance. */
/**
* Builds a {@link JpaUnitTestRule} instance that can also be used as an extension for JUnit5.
*/
public JpaUnitTestRule buildUnitTestRule() {
checkState(
!Objects.equals(GOLDEN_SCHEMA_SQL_PATH, initScript),
@@ -26,11 +26,11 @@ import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import google.registry.testing.FakeClock;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.NoSuchElementException;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import org.junit.Rule;
import org.junit.Test;
@@ -71,18 +71,18 @@ public class JpaTransactionManagerImplTest {
@Test
public void assertInTransaction_throwsExceptionWhenNotInTransaction() {
assertThrows(PersistenceException.class, () -> jpaTm().assertInTransaction());
assertThrows(IllegalStateException.class, () -> jpaTm().assertInTransaction());
jpaTm().transact(() -> jpaTm().assertInTransaction());
assertThrows(PersistenceException.class, () -> jpaTm().assertInTransaction());
assertThrows(IllegalStateException.class, () -> jpaTm().assertInTransaction());
}
@Test
public void getTransactionTime_throwsExceptionWhenNotInTransaction() {
FakeClock txnClock = fakeClock;
txnClock.advanceOneMilli();
assertThrows(PersistenceException.class, () -> jpaTm().getTransactionTime());
assertThrows(IllegalStateException.class, () -> jpaTm().getTransactionTime());
jpaTm().transact(() -> assertThat(jpaTm().getTransactionTime()).isEqualTo(txnClock.nowUtc()));
assertThrows(PersistenceException.class, () -> jpaTm().getTransactionTime());
assertThrows(IllegalStateException.class, () -> jpaTm().getTransactionTime());
}
@Test
@@ -147,7 +147,7 @@ public class JpaTransactionManagerImplTest {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isTrue();
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey).get())).isEqualTo(theEntity);
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey))).isEqualTo(theEntity);
}
@Test
@@ -155,7 +155,7 @@ public class JpaTransactionManagerImplTest {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isTrue();
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey).get())).isEqualTo(theEntity);
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey))).isEqualTo(theEntity);
assertThrows(RollbackException.class, () -> jpaTm().transact(() -> jpaTm().saveNew(theEntity)));
}
@@ -164,7 +164,7 @@ public class JpaTransactionManagerImplTest {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(compoundIdEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(compoundIdEntity));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(compoundIdEntity))).isTrue();
assertThat(jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey).get()))
assertThat(jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey)))
.isEqualTo(compoundIdEntity);
}
@@ -196,17 +196,17 @@ public class JpaTransactionManagerImplTest {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNewOrUpdate(theEntity));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isTrue();
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey).get())).isEqualTo(theEntity);
assertThat(jpaTm().transact(() -> jpaTm().load(theEntityKey))).isEqualTo(theEntity);
}
@Test
public void saveNewOrUpdate_updatesExistingEntity() {
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
TestEntity persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey)).get();
TestEntity persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey));
assertThat(persisted.data).isEqualTo("foo");
theEntity.data = "bar";
jpaTm().transact(() -> jpaTm().saveNewOrUpdate(theEntity));
persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey)).get();
persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey));
assertThat(persisted.data).isEqualTo("bar");
}
@@ -225,23 +225,22 @@ public class JpaTransactionManagerImplTest {
public void update_succeeds() {
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(TestEntity.class, "theEntity"))).get();
jpaTm().transact(() -> jpaTm().load(VKey.createSql(TestEntity.class, "theEntity")));
assertThat(persisted.data).isEqualTo("foo");
theEntity.data = "bar";
jpaTm().transact(() -> jpaTm().update(theEntity));
persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey)).get();
persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey));
assertThat(persisted.data).isEqualTo("bar");
}
@Test
public void updateCompoundIdEntity_succeeds() {
jpaTm().transact(() -> jpaTm().saveNew(compoundIdEntity));
TestCompoundIdEntity persisted =
jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey)).get();
TestCompoundIdEntity persisted = jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey));
assertThat(persisted.data).isEqualTo("foo");
compoundIdEntity.data = "bar";
jpaTm().transact(() -> jpaTm().update(compoundIdEntity));
persisted = jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey).get());
persisted = jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey));
assertThat(persisted.data).isEqualTo("bar");
}
@@ -285,17 +284,38 @@ public class JpaTransactionManagerImplTest {
public void load_succeeds() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
TestEntity persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey)).get();
TestEntity persisted = jpaTm().transact(() -> jpaTm().load(theEntityKey));
assertThat(persisted.name).isEqualTo("theEntity");
assertThat(persisted.data).isEqualTo("foo");
}
@Test
public void load_throwsOnMissingElement() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
assertThrows(
NoSuchElementException.class, () -> jpaTm().transact(() -> jpaTm().load(theEntityKey)));
}
@Test
public void maybeLoad_succeeds() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
TestEntity persisted = jpaTm().transact(() -> jpaTm().maybeLoad(theEntityKey).get());
assertThat(persisted.name).isEqualTo("theEntity");
assertThat(persisted.data).isEqualTo("foo");
}
@Test
public void maybeLoad_nonExistentObject() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
assertThat(jpaTm().transact(() -> jpaTm().maybeLoad(theEntityKey)).isPresent()).isFalse();
}
@Test
public void loadCompoundIdEntity_succeeds() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(compoundIdEntity))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(compoundIdEntity));
TestCompoundIdEntity persisted =
jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey)).get();
TestCompoundIdEntity persisted = jpaTm().transact(() -> jpaTm().load(compoundIdEntityKey));
assertThat(persisted.name).isEqualTo("compoundIdEntity");
assertThat(persisted.age).isEqualTo(10);
assertThat(persisted.data).isEqualTo("foo");
@@ -312,14 +332,14 @@ public class JpaTransactionManagerImplTest {
public void delete_succeeds() {
jpaTm().transact(() -> jpaTm().saveNew(theEntity));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isTrue();
assertThat(jpaTm().transact(() -> jpaTm().delete(theEntityKey))).isEqualTo(1);
jpaTm().transact(() -> jpaTm().delete(theEntityKey));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
}
@Test
public void delete_returnsZeroWhenNoEntity() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
assertThat(jpaTm().transact(() -> jpaTm().delete(theEntityKey))).isEqualTo(0);
jpaTm().transact(() -> jpaTm().delete(theEntityKey));
assertThat(jpaTm().transact(() -> jpaTm().checkExists(theEntity))).isFalse();
}
@@ -240,22 +240,22 @@ public class DomainBaseToXjcConverterTest {
ImmutableSet.of(
DesignatedContact.create(
DesignatedContact.Type.ADMIN,
Key.create(
makeContactResource(
makeContactResource(
clock,
"10-Q9JYB4C",
"5372808-IRL",
"be that word our sign in parting",
"BOFH@cat.みんな"))),
"BOFH@cat.みんな")
.createVKey()),
DesignatedContact.create(
DesignatedContact.Type.TECH,
Key.create(
makeContactResource(
makeContactResource(
clock,
"11-Q9JYB4C",
"5372808-TRL",
"bird or fiend!? i shrieked upstarting",
"bog@cat.みんな")))))
"bog@cat.みんな")
.createVKey())))
.setCreationClientId("LawyerCat")
.setCreationTimeForTest(DateTime.parse("1900-01-01T00:00:00Z"))
.setPersistedCurrentSponsorClientId("GetTheeBack")
@@ -273,9 +273,9 @@ public class DomainBaseToXjcConverterTest {
makeHostResource(clock, "4-Q9JYB4C", "ns2.cat.みんな", "bad:f00d:cafe::15:beef")
.createKey()))
.setRegistrant(
Key.create(
makeContactResource(
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")))
makeContactResource(
clock, "12-Q9JYB4C", "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
.createVKey())
.setRegistrationExpirationTime(DateTime.parse("1930-01-01T00:00:00Z"))
.setGracePeriods(
ImmutableSet.of(
@@ -63,9 +63,8 @@ final class RdeFixtures {
.setFullyQualifiedDomainName("example." + tld)
.setRepoId(generateNewDomainRoid(tld))
.setRegistrant(
Key.create(
makeContactResource(
clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")))
makeContactResource(clock, "5372808-ERL", "(◕‿◕) nevermore", "prophet@evil.みんな")
.createVKey())
.build();
HistoryEntry historyEntry =
persistResource(new HistoryEntry.Builder().setParent(domain).build());
@@ -90,20 +89,20 @@ final class RdeFixtures {
ImmutableSet.of(
DesignatedContact.create(
DesignatedContact.Type.ADMIN,
Key.create(
makeContactResource(
makeContactResource(
clock,
"5372808-IRL",
"be that word our sign in parting",
"BOFH@cat.みんな"))),
"BOFH@cat.みんな")
.createVKey()),
DesignatedContact.create(
DesignatedContact.Type.TECH,
Key.create(
makeContactResource(
makeContactResource(
clock,
"5372808-TRL",
"bird or fiend!? i shrieked upstarting",
"bog@cat.みんな")))))
"bog@cat.みんな")
.createVKey())))
.setCreationClientId("TheRegistrar")
.setPersistedCurrentSponsorClientId("TheRegistrar")
.setCreationTimeForTest(clock.nowUtc())
@@ -78,7 +78,7 @@ public class RegistrarDaoTest {
@Test
public void update_worksSuccessfully() {
jpaTm().transact(() -> jpaTm().saveNew(testRegistrar));
Registrar persisted = jpaTm().transact(() -> jpaTm().load(registrarKey)).get();
Registrar persisted = jpaTm().transact(() -> jpaTm().load(registrarKey));
assertThat(persisted.getRegistrarName()).isEqualTo("registrarName");
jpaTm()
.transact(
@@ -86,7 +86,7 @@ public class RegistrarDaoTest {
jpaTm()
.update(
persisted.asBuilder().setRegistrarName("changedRegistrarName").build()));
Registrar updated = jpaTm().transact(() -> jpaTm().load(registrarKey)).get();
Registrar updated = jpaTm().transact(() -> jpaTm().load(registrarKey));
assertThat(updated.getRegistrarName()).isEqualTo("changedRegistrarName");
}
@@ -102,7 +102,7 @@ public class RegistrarDaoTest {
public void load_worksSuccessfully() {
assertThat(jpaTm().transact(() -> jpaTm().checkExists(testRegistrar))).isFalse();
jpaTm().transact(() -> jpaTm().saveNew(testRegistrar));
Registrar persisted = jpaTm().transact(() -> jpaTm().load(registrarKey)).get();
Registrar persisted = jpaTm().transact(() -> jpaTm().load(registrarKey));
assertThat(persisted.getClientId()).isEqualTo("registrarId");
assertThat(persisted.getRegistrarName()).isEqualTo("registrarName");
@@ -27,7 +27,6 @@ import static google.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.OteStatsTestHelper;
import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactResource;
@@ -127,9 +126,9 @@ public enum Fixture {
.asBuilder()
.setContacts(
ImmutableSet.of(
DesignatedContact.create(ADMIN, Key.create(robert)),
DesignatedContact.create(BILLING, Key.create(google)),
DesignatedContact.create(TECH, Key.create(justine))))
DesignatedContact.create(ADMIN, robert.createVKey()),
DesignatedContact.create(BILLING, google.createVKey()),
DesignatedContact.create(TECH, justine.createVKey())))
.setNameservers(
ImmutableSet.of(
persistActiveHost("ns1.love.xn--q9jyb4c").createKey(),
@@ -141,9 +140,9 @@ public enum Fixture {
.asBuilder()
.setContacts(
ImmutableSet.of(
DesignatedContact.create(ADMIN, Key.create(robert)),
DesignatedContact.create(BILLING, Key.create(google)),
DesignatedContact.create(TECH, Key.create(justine))))
DesignatedContact.create(ADMIN, robert.createVKey()),
DesignatedContact.create(BILLING, google.createVKey()),
DesignatedContact.create(TECH, justine.createVKey())))
.setNameservers(
ImmutableSet.of(
persistActiveHost("ns1.linode.com").createKey(),
@@ -93,6 +93,7 @@ import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import google.registry.tmch.LordnTaskUtils;
import java.util.Arrays;
import java.util.List;
@@ -144,7 +145,7 @@ public class DatastoreHelper {
public static DomainBase newDomainBase(
String domainName, String repoId, ContactResource contact) {
Key<ContactResource> contactKey = Key.create(contact);
VKey<ContactResource> contactKey = contact.createVKey();
return new DomainBase.Builder()
.setRepoId(repoId)
.setFullyQualifiedDomainName(domainName)
@@ -487,11 +488,11 @@ public class DatastoreHelper {
.setCreationClientId("TheRegistrar")
.setCreationTimeForTest(creationTime)
.setRegistrationExpirationTime(expirationTime)
.setRegistrant(Key.create(contact))
.setRegistrant(contact.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())))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")))
.addGracePeriod(
GracePeriod.create(GracePeriodStatus.ADD, now.plusDays(10), "foo", null))
@@ -23,7 +23,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InetAddresses;
import com.googlecode.objectify.Key;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactPhoneNumber;
@@ -360,17 +359,17 @@ public final class FullFieldsTestEntityHelper {
StatusValue.SERVER_UPDATE_PROHIBITED))
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, "deadface")));
if (registrant != null) {
builder.setRegistrant(Key.create(registrant));
builder.setRegistrant(registrant.createVKey());
}
if ((admin != null) || (tech != null)) {
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
if (admin != null) {
contactsBuilder.add(DesignatedContact.create(
DesignatedContact.Type.ADMIN, Key.create(admin)));
contactsBuilder.add(
DesignatedContact.create(DesignatedContact.Type.ADMIN, admin.createVKey()));
}
if (tech != null) {
contactsBuilder.add(DesignatedContact.create(
DesignatedContact.Type.TECH, Key.create(tech)));
contactsBuilder.add(
DesignatedContact.create(DesignatedContact.Type.TECH, tech.createVKey()));
}
builder.setContacts(contactsBuilder.build());
}
@@ -24,7 +24,6 @@ import static google.registry.testing.DatastoreHelper.persistDomainAndEnqueueLor
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
import static org.junit.Assert.assertThrows;
import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.ofy.Ofy;
@@ -63,7 +62,7 @@ public class LordnTaskUtilsTest {
private DomainBase.Builder newDomainBuilder() {
return new DomainBase.Builder()
.setFullyQualifiedDomainName("fleece.example")
.setRegistrant(Key.create(persistActiveContact("jd1234")))
.setRegistrant(persistActiveContact("jd1234").createVKey())
.setSmdId("smdzzzz")
.setCreationClientId("TheRegistrar");
}
@@ -160,19 +160,19 @@ public class EppLifecycleToolsTest extends EppTestCase {
makeOneTimeCreateBillingEvent(domain, createTime),
renewBillingEvent,
// The initial autorenew billing event, which was closed at the time of the explicit renew.
makeRecurringCreateBillingEvent(
makeRecurringBillingEvent(
domain,
getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE),
createTime.plusYears(2),
DateTime.parse("2000-06-07T00:00:00.000Z")),
// The renew's autorenew billing event, which was closed at the time of the unrenew.
makeRecurringCreateBillingEvent(
makeRecurringBillingEvent(
domain,
getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW),
DateTime.parse("2006-06-01T00:02:00.000Z"),
DateTime.parse("2001-06-07T00:00:00.000Z")),
// The remaining active autorenew billing event which was created by the unrenew.
makeRecurringCreateBillingEvent(
makeRecurringBillingEvent(
domain,
getOnlyHistoryEntryOfType(domain, Type.SYNTHETIC),
DateTime.parse("2003-06-01T00:02:00.000Z"),
@@ -26,7 +26,6 @@ import static org.junit.Assert.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.eppcommon.StatusValue;
@@ -187,10 +186,10 @@ public class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomain
ContactResource adminContact2 = persistResource(newContactResource("crr-admin2"));
ContactResource techContact1 = persistResource(newContactResource("crr-tech1"));
ContactResource techContact2 = persistResource(newContactResource("crr-tech2"));
Key<ContactResource> adminResourceKey1 = Key.create(adminContact1);
Key<ContactResource> adminResourceKey2 = Key.create(adminContact2);
Key<ContactResource> techResourceKey1 = Key.create(techContact1);
Key<ContactResource> techResourceKey2 = Key.create(techContact2);
VKey<ContactResource> adminResourceKey1 = adminContact1.createVKey();
VKey<ContactResource> adminResourceKey2 = adminContact2.createVKey();
VKey<ContactResource> techResourceKey1 = techContact1.createVKey();
VKey<ContactResource> techResourceKey2 = techContact2.createVKey();
persistResource(
newDomainBase("example.tld")
@@ -53,7 +53,6 @@ public class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarC
assertThat(
jpaTm()
.transact(() -> jpaTm().load(VKey.createSql(Registrar.class, "NewRegistrar")))
.get()
.verifyPassword("some_password"))
.isTrue();
}
@@ -23,7 +23,6 @@ import static google.registry.whois.WhoisTestData.loadFile;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.contact.ContactAddress;
import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.ContactResource;
@@ -223,9 +222,9 @@ public class DomainWhoisResponseTest {
VKey<HostResource> hostResource1Key = hostResource1.createKey();
VKey<HostResource> hostResource2Key = hostResource2.createKey();
Key<ContactResource> registrantResourceKey = Key.create(registrant);
Key<ContactResource> adminResourceKey = Key.create(adminContact);
Key<ContactResource> techResourceKey = Key.create(techContact);
VKey<ContactResource> registrantResourceKey = registrant.createVKey();
VKey<ContactResource> adminResourceKey = adminContact.createVKey();
VKey<ContactResource> techResourceKey = techContact.createVKey();
domainBase =
persistResource(
@@ -0,0 +1,35 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>%CRDATE%</domain:crDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="addPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,37 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>2000-06-01T00:02:00Z</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="%GRACEPERIOD%"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,38 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>2000-06-01T00:02:00Z</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="renewPeriod"/>
<rgp:rgpStatus s="addPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,38 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="pendingDelete"/>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>%CRDATE%</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="redemptionPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
@@ -0,0 +1,38 @@
-- 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.
ALTER TABLE "Domain" ADD COLUMN admin_contact text;
ALTER TABLE "Domain" ADD COLUMN billing_contact text;
ALTER TABLE "Domain" ADD COLUMN registrant_contact text;
ALTER TABLE "Domain" ADD COLUMN tech_contact text;
alter table if exists "Domain"
add constraint fk_domain_admin_contact
foreign key (admin_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_billing_contact
foreign key (billing_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_registrant_contact
foreign key (registrant_contact)
references "Contact";
alter table if exists "Domain"
add constraint fk_domain_tech_contact
foreign key (tech_contact)
references "Contact";
@@ -100,8 +100,10 @@
last_epp_update_client_id text,
last_epp_update_time timestamptz,
statuses text[],
admin_contact text,
auth_info_repo_id text,
auth_info_value text,
billing_contact text,
fully_qualified_domain_name text,
idn_table_name text,
last_transfer_time timestamptz,
@@ -109,9 +111,11 @@
launch_notice_expiration_time timestamptz,
launch_notice_tcn_id text,
launch_notice_validator_id text,
registrant_contact text,
registration_expiration_time timestamptz,
smd_id text,
subordinate_hosts text[],
tech_contact text,
tld text,
primary key (repo_id)
);
@@ -165,7 +165,11 @@ CREATE TABLE public."Domain" (
registration_expiration_time timestamp with time zone,
smd_id text,
subordinate_hosts text[],
tld text
tld text,
admin_contact text,
billing_contact text,
registrant_contact text,
tech_contact text
);
@@ -747,6 +751,38 @@ ALTER TABLE ONLY public."Contact"
ADD CONSTRAINT fk93c185fx7chn68uv7nl6uv2s0 FOREIGN KEY (current_sponsor_client_id) REFERENCES public."Registrar"(client_id);
--
-- Name: Domain fk_domain_admin_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_admin_contact FOREIGN KEY (admin_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_billing_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_billing_contact FOREIGN KEY (billing_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_registrant_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_registrant_contact FOREIGN KEY (registrant_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: Domain fk_domain_tech_contact; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."Domain"
ADD CONSTRAINT fk_domain_tech_contact FOREIGN KEY (tech_contact) REFERENCES public."Contact"(repo_id);
--
-- Name: DomainHost fk_domainhost_host_valid; Type: FK CONSTRAINT; Schema: public; Owner: -
--
+2 -2
View File
@@ -52,7 +52,7 @@ Updated 1 entities.
* `create_tld` is the subcommand to create a TLD. The TLD name is "example"
which happens to be an ICANN reserved string, and therefore "example" can
never be created on the Internet at large.
* `--initial_tld_state` defines the intital state of the TLD.
* `--initial_tld_state` defines the initial state of the TLD.
`GENERAL_AVAILABILITY`, in the case of our example, allows you to
immediately create domain names by bypassing the sunrise and landrush domain
registration periods.
@@ -168,7 +168,7 @@ $ nomulus -e alpha create_domain fake.example --client acme --admins abcd1234 \
Where:
* `create_domain` is the subcommand to create a domain name. It accepts a
whitespace-separted list of domain names to be created
whitespace-separated list of domain names to be created
* `--client` is used to define the registrar.
* `--admins` is the administrative contact's id(s).
* `--techs` is the technical contact's id(s).
+1 -1
View File
@@ -523,7 +523,7 @@ $ kubectl create secret generic service-account \
--from-file=service-account-key.json=<service-account-key.json>
```
More details on using sevice account on GKE can be found
More details on using service account on GKE can be found
[here](https://cloud.google.com/kubernetes-engine/docs/tutorials/authenticating-to-cloud-platform).
Repeat the same step for all clusters you want to deploy to. Use `gcloud` to
+1 -1
View File
@@ -138,7 +138,7 @@ No, we do not require verification of zone configurations.
Yes, IPv6 addresses are supported.
**4.9 How many IP adresses can be listed on an in-bailiwick host object?**
**4.9 How many IP addresses can be listed on an in-bailiwick host object?**
Ten IP addresses can be listed on an in-bailiwick host object. This limit is for
both IPv4 and IPv6 addresses combined.
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -37,6 +37,7 @@ import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.function.Supplier;
import javax.net.ssl.SSLSession;
/**
* Adds a server side SSL handler to the channel pipeline.
@@ -108,9 +109,21 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
.addListener(
future -> {
if (future.isSuccess()) {
SSLSession sslSession = sslHandler.engine().getSession();
X509Certificate clientCertificate =
(X509Certificate)
sslHandler.engine().getSession().getPeerCertificates()[0];
(X509Certificate) sslSession.getPeerCertificates()[0];
logger.atInfo().log(
"--SSL Information--\n"
+ "Client Certificate Hash: %s\n"
+ "SSL Protocol: %s\n"
+ "Cipher Suite: %s\n"
+ "Not Before: %s\n"
+ "Not After: %s\n",
getCertificateHash(clientCertificate),
sslSession.getProtocol(),
sslSession.getCipherSuite(),
clientCertificate.getNotBefore(),
clientCertificate.getNotAfter());
try {
clientCertificate.checkValidity();
} catch (CertificateNotYetValidException | CertificateExpiredException e) {