Use END_OF_TIME as sentinel value for domain's autorenewEndTime (#949)

* Use END_OF_TIME as sentinel value for domain's autorenewEndTime

Datastore inequality queries don't work correctly for null; null is treated as
the lowest value possible which is definitely the opposite of the intended
meaning here.

This includes an @OnLoad for backfilling purposes using the ResaveAll mapreduce.
This commit is contained in:
Ben McIlwain 2021-02-01 15:29:51 -05:00 committed by GitHub
parent d10f53cfd6
commit 6d065e73ed
2 changed files with 48 additions and 18 deletions

View file

@ -14,6 +14,7 @@
package google.registry.model.domain; package google.registry.model.domain;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
@ -50,7 +51,6 @@ import com.googlecode.objectify.condition.IfNull;
import google.registry.flows.ResourceFlowUtils; import google.registry.flows.ResourceFlowUtils;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.EppResource.ResourceWithTransferData; import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.ImmutableObject.EmptySetToNull;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.common.EntityGroupRoot; import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
@ -66,6 +66,7 @@ import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.util.CollectionUtils; import google.registry.util.CollectionUtils;
import google.registry.util.DateTimeUtils;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -284,9 +285,10 @@ public class DomainContent extends EppResource
/** /**
* When the domain's autorenewal status will expire. * When the domain's autorenewal status will expire.
* *
* <p>This will be null for the vast majority of domains because all domains autorenew * <p>This will be {@link DateTimeUtils#END_OF_TIME} for the vast majority of domains because all
* indefinitely by default and autorenew can only be countermanded by administrators, typically * domains autorenew indefinitely by default and autorenew can only be countermanded by
* for reasons of the URS process or termination of a registrar for nonpayment. * administrators, typically for reasons of the URS process or termination of a registrar for
* nonpayment.
* *
* <p>When a domain is scheduled to not autorenew, this field is set to the current value of its * <p>When a domain is scheduled to not autorenew, this field is set to the current value of its
* {@link #registrationExpirationTime}, after which point the next invocation of a periodic * {@link #registrationExpirationTime}, after which point the next invocation of a periodic
@ -295,10 +297,16 @@ public class DomainContent extends EppResource
* difference domains that have reached their life and must be deleted now, and domains that * difference domains that have reached their life and must be deleted now, and domains that
* happen to be in the autorenew grace period now but should be deleted in roughly a year. * happen to be in the autorenew grace period now but should be deleted in roughly a year.
*/ */
@Nullable @Index DateTime autorenewEndTime; @Index DateTime autorenewEndTime;
@OnLoad @OnLoad
void load() { void load() {
// Back fill with correct END_OF_TIME sentinel value.
// TODO(mcilwain): Remove this once back-filling is complete.
if (autorenewEndTime == null) {
autorenewEndTime = END_OF_TIME;
}
// Reconstitute all of the contacts so that they have VKeys. // Reconstitute all of the contacts so that they have VKeys.
allContacts = allContacts =
allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet()); allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet());
@ -403,8 +411,20 @@ public class DomainContent extends EppResource
return smdId; return smdId;
} }
/**
* Returns the autorenew end time if there is one, otherwise empty.
*
* <p>Note that {@link DateTimeUtils#END_OF_TIME} is used as a sentinel value in the database
* representation to signify that autorenew doesn't end, and is mapped to empty here for the
* purposes of more legible business logic.
*/
public Optional<DateTime> getAutorenewEndTime() { public Optional<DateTime> getAutorenewEndTime() {
return Optional.ofNullable(autorenewEndTime); // TODO(mcilwain): Remove null handling for autorenewEndTime once data migration away from null
// is complete.
return Optional.ofNullable(
(autorenewEndTime == null || autorenewEndTime.equals(END_OF_TIME))
? null
: autorenewEndTime);
} }
@Override @Override
@ -779,6 +799,8 @@ public class DomainContent extends EppResource
} else { // There are nameservers, so make sure INACTIVE isn't there. } else { // There are nameservers, so make sure INACTIVE isn't there.
removeStatusValue(StatusValue.INACTIVE); removeStatusValue(StatusValue.INACTIVE);
} }
// If there is no autorenew end time, set it to END_OF_TIME.
instance.autorenewEndTime = firstNonNull(getInstance().autorenewEndTime, END_OF_TIME);
checkArgumentNotNull(emptyToNull(instance.fullyQualifiedDomainName), "Missing domainName"); checkArgumentNotNull(emptyToNull(instance.fullyQualifiedDomainName), "Missing domainName");
if (instance.getRegistrant() == null if (instance.getRegistrant() == null
@ -958,8 +980,15 @@ public class DomainContent extends EppResource
return thisCastToDerived(); return thisCastToDerived();
} }
/**
* Sets the autorenew end time, or clears it if empty is passed.
*
* <p>Note that {@link DateTimeUtils#END_OF_TIME} is used as a sentinel value in the database
* representation to signify that autorenew doesn't end, and is mapped to empty here for the
* purposes of more legible business logic.
*/
public B setAutorenewEndTime(Optional<DateTime> autorenewEndTime) { public B setAutorenewEndTime(Optional<DateTime> autorenewEndTime) {
getInstance().autorenewEndTime = autorenewEndTime.orElse(null); getInstance().autorenewEndTime = autorenewEndTime.orElse(END_OF_TIME);
return thisCastToDerived(); return thisCastToDerived();
} }

View file

@ -392,17 +392,18 @@ public final class FullFieldsTestEntityHelper {
Period period, Period period,
String reason, String reason,
DateTime modificationTime) { DateTime modificationTime) {
HistoryEntry.Builder builder = new HistoryEntry.Builder() HistoryEntry.Builder builder =
.setParent(resource) new HistoryEntry.Builder()
.setType(type) .setParent(resource)
.setPeriod(period) .setType(type)
.setXmlBytes("<xml></xml>".getBytes(UTF_8)) .setPeriod(period)
.setModificationTime(modificationTime) .setXmlBytes("<xml></xml>".getBytes(UTF_8))
.setClientId(resource.getPersistedCurrentSponsorClientId()) .setModificationTime(modificationTime)
.setTrid(Trid.create("ABC-123", "server-trid")) .setClientId(resource.getPersistedCurrentSponsorClientId())
.setBySuperuser(false) .setTrid(Trid.create("ABC-123", "server-trid"))
.setReason(reason) .setBySuperuser(false)
.setRequestedByRegistrar(false); .setReason(reason)
.setRequestedByRegistrar(false);
return builder.build(); return builder.build();
} }
} }