diff --git a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java index a99a873a1..95d1a4419 100644 --- a/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java +++ b/core/src/main/java/google/registry/model/common/DatabaseMigrationStateSchedule.java @@ -26,7 +26,6 @@ import com.google.common.flogger.FluentLogger; import google.registry.config.RegistryEnvironment; import google.registry.model.CacheUtils; import google.registry.model.annotations.DeleteAfterMigration; -import google.registry.model.common.TimedTransitionProperty.TimedTransition; import java.time.Duration; import java.util.Arrays; import javax.persistence.Entity; @@ -109,28 +108,13 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton { } } - public static class MigrationStateTransition extends TimedTransition { - private MigrationState migrationState; - - @Override - public MigrationState getValue() { - return migrationState; - } - - @Override - protected void setValue(MigrationState migrationState) { - this.migrationState = migrationState; - } - } - /** * Cache of the current migration schedule. The key is meaningless; this is essentially a memoized * Supplier that can be reset for testing purposes and after writes. */ @VisibleForTesting public static final LoadingCache< - Class, - TimedTransitionProperty> + Class, TimedTransitionProperty> // Each instance should cache the migration schedule for five minutes before reloading CACHE = CacheUtils.newCacheBuilder(Duration.ofMinutes(5)) @@ -185,33 +169,29 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton { // Default map to return if we have never saved any -- only use Datastore. @VisibleForTesting - public static final TimedTransitionProperty - DEFAULT_TRANSITION_MAP = - TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY), - MigrationStateTransition.class); + public static final TimedTransitionProperty DEFAULT_TRANSITION_MAP = + TimedTransitionProperty.fromValueMap( + ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY)); @VisibleForTesting - public TimedTransitionProperty migrationTransitions = - TimedTransitionProperty.forMapify( - MigrationState.DATASTORE_ONLY, MigrationStateTransition.class); + public TimedTransitionProperty migrationTransitions = + TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY); // Required for Objectify initialization private DatabaseMigrationStateSchedule() {} @VisibleForTesting public DatabaseMigrationStateSchedule( - TimedTransitionProperty migrationTransitions) { + TimedTransitionProperty migrationTransitions) { this.migrationTransitions = migrationTransitions; } /** Sets and persists to SQL the provided migration transition schedule. */ public static void set(ImmutableSortedMap migrationTransitionMap) { jpaTm().assertInTransaction(); - TimedTransitionProperty transitions = + TimedTransitionProperty transitions = TimedTransitionProperty.make( migrationTransitionMap, - MigrationStateTransition.class, VALID_STATE_TRANSITIONS, "validStateTransitions", MigrationState.DATASTORE_ONLY, @@ -222,7 +202,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton { } /** Loads the currently-set migration schedule from the cache, or the default if none exists. */ - public static TimedTransitionProperty get() { + public static TimedTransitionProperty get() { return CACHE.get(DatabaseMigrationStateSchedule.class); } @@ -233,7 +213,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton { /** Loads the currently-set migration schedule from SQL, or the default if none exists. */ @VisibleForTesting - static TimedTransitionProperty getUncached() { + static TimedTransitionProperty getUncached() { return jpaTm() .transactWithoutBackup( () -> { @@ -260,7 +240,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton { * time to the new map at the current time must also be valid. */ private static void validateTransitionAtCurrentTime( - TimedTransitionProperty newTransitions) { + TimedTransitionProperty newTransitions) { MigrationState currentValue = getUncached().getValueAtTime(jpaTm().getTransactionTime()); MigrationState nextCurrentValue = newTransitions.getValueAtTime(jpaTm().getTransactionTime()); checkArgument( diff --git a/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java b/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java index 65fab8076..fc50ffc04 100644 --- a/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java +++ b/core/src/main/java/google/registry/model/common/TimedTransitionProperty.java @@ -23,192 +23,80 @@ import static google.registry.util.DateTimeUtils.latestOf; import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSortedMap; -import com.google.common.collect.Maps; import com.google.common.collect.Ordering; -import com.googlecode.objectify.mapper.Mapper; -import google.registry.model.ImmutableObject; import google.registry.model.UnsafeSerializable; -import google.registry.util.TypeUtils; import java.io.Serializable; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import java.util.NavigableMap; -import java.util.TreeMap; import javax.annotation.Nullable; import org.joda.time.DateTime; /** * An entity property whose value transitions over time. Each value it takes on becomes active at a * corresponding instant, and remains active until the next transition occurs. At least one "start - * of time" value (corresponding to START_OF_TIME, i.e. the Unix epoch) must be provided so that the - * property will have a value for all possible times. + * of time" value (corresponding to {@code START_OF_TIME}, i.e. the Unix epoch) must be provided so + * that the property will have a value for all possible times. * - *

This concept is naturally represented by a sorted map of {@code DateTime} to {@code V}, but - * the App Engine Datastore cannot natively represent a map keyed on non-strings. Instead, we store - * an ordered list of transitions and use Objectify's @Mapify annotation to automatically recreate - * the sorted map on load from Datastore, which is used as a backing map for this property; the - * property itself also implements Map by way of extending ForwardingMap, so that this property can - * stored directly as the @Mapify field in the entity. - * - *

The type parameter {@code T} specifies a user-defined subclass of {@code TimedTransition} - * to use for storing the list of transitions. The user is given this choice of subclass so that the - * field of the value type stored in the transition can be given a customized name. + *

This concept is naturally represented by a sorted map of {@link DateTime} to {@link V}. This + * class implements {@link ForwardingMap} and stores the data in a backing map and exposes several + * convenient methods to extrapolate the value at arbitrary point in time. */ -public class TimedTransitionProperty< - V extends Serializable, T extends TimedTransitionProperty.TimedTransition> - extends ForwardingMap implements UnsafeSerializable { +public class TimedTransitionProperty extends ForwardingMap + implements UnsafeSerializable { + + private static final long serialVersionUID = -7274659848856323290L; /** - * A transition to a value of type {@code V} at a certain time. This superclass only has a field - * for the {@code DateTime}, which means that subclasses should supply the field of type {@code V} - * and implementations of the abstract getter and setter methods to access that field. This design - * is so that subclasses tagged with @Embed can define a custom field name for their value, for - * the purpose of backwards compatibility and better readability of the Datastore representation. - * - *

The public visibility of this class exists only so that it can be subclassed; clients should - * never call any methods on this class or attempt to access its members, but should instead treat - * it as a customizable implementation detail of {@code TimedTransitionProperty}. However, note - * that subclasses must also have public visibility so that they can be instantiated via - * reflection in a call to {@code fromValueMap}. + * Returns a new immutable {@link TimedTransitionProperty} representing the given map of {@link + * DateTime} to value {@link V}. */ - public abstract static class TimedTransition extends ImmutableObject - implements UnsafeSerializable { - /** The time at which this value becomes the active value. */ - private DateTime transitionTime; - - /** Returns the value that this transition will activate. */ - protected abstract V getValue(); - - /** Sets the value that will be activated at this transition's time. */ - protected abstract void setValue(V value); - } - - /** Mapper used with @Mapify extracting time from TimedTransition to use as key. */ - public static class TimeMapper implements Mapper> { - @Override - public DateTime getKey(TimedTransition transition) { - return transition.transitionTime; - } - } - - /** - * Converts the provided value map into the equivalent transition map, using transition objects of - * the given TimedTransition subclass. The value map must be sorted according to the natural - * ordering of its DateTime keys, and keys cannot be earlier than START_OF_TIME. - */ - // NB: The Class parameter could be eliminated by getting the class via reflection, but then - // the callsite cannot infer T, so unless you explicitly call this as .fromValueMap() it - // will default to using just TimedTransition, which fails at runtime. - private static > - NavigableMap makeTransitionMap( - ImmutableSortedMap valueMap, final Class timedTransitionSubclass) { + public static TimedTransitionProperty fromValueMap( + ImmutableSortedMap valueMap) { checkArgument( Ordering.natural().equals(valueMap.comparator()), "Timed transition value map must have transition time keys in chronological order"); - return Maps.transformEntries( - valueMap, - (DateTime transitionTime, V value) -> { - checkArgument( - !transitionTime.isBefore(START_OF_TIME), - "Timed transition times cannot be earlier than START_OF_TIME / Unix Epoch"); - T subclass = TypeUtils.instantiate(timedTransitionSubclass); - ((TimedTransition) subclass).transitionTime = transitionTime; - subclass.setValue(value); - return subclass; - }); + return new TimedTransitionProperty<>(valueMap); } /** - * Returns a new immutable {@code TimedTransitionProperty} representing the given map of DateTime - * to value, with transitions constructed using the given {@code TimedTransition} subclass. - * - *

This method should be the normal method for constructing a {@link TimedTransitionProperty}. + * Returns a new immutable {@link TimedTransitionProperty} with an initial value at {@code + * START_OF_TIME}. */ - public static > - TimedTransitionProperty fromValueMap( - ImmutableSortedMap valueMap, final Class timedTransitionSubclass) { - return new TimedTransitionProperty<>(ImmutableSortedMap.copyOf( - makeTransitionMap(valueMap, timedTransitionSubclass))); + public static TimedTransitionProperty withInitialValue( + V initialValue) { + return fromValueMap(ImmutableSortedMap.of(START_OF_TIME, initialValue)); } /** - * Returns a new immutable {@code TimedTransitionProperty} containing the same transitions as the - * current object, up to but not including the desired date. All transitions on or after that date - * will be deleted. + * Validates a new set of transitions and returns the resulting {@link TimedTransitionProperty}. * - * @param asOfDate the date before which transitions should be retained - */ - public TimedTransitionProperty copyUntilJustBefore(DateTime asOfDate) { - return new TimedTransitionProperty<>(backingMap.headMap(asOfDate, false)); - } - - /** - * Returns a new immutable {@code TimedTransitionProperty} containing the same transitions as the - * current object, plus the additional specified transition. - * - * @param transitionTime the time of the new transition - * @param transitionValue the value of the new transition - * @param transitionClass the class of transitions in this map - * @param allowedTransitions map of all possible state-to-state transitions - * @param allowedTransitionMapName transition map description string for error messages - */ - public TimedTransitionProperty copyWithAddedTransition( - DateTime transitionTime, - V transitionValue, - Class transitionClass, - ImmutableMultimap allowedTransitions, - String allowedTransitionMapName) { - ImmutableSortedMap currentMap = toValueMap(); - checkArgument( - transitionTime.isAfter(currentMap.lastKey()), - "New transitions can only be appended after the last previous transition."); - Map newInnerMap = new HashMap<>(currentMap); - newInnerMap.put(transitionTime, transitionValue); - ImmutableSortedMap newMap = - ImmutableSortedMap.copyOf(newInnerMap); - validateTimedTransitionMap(newMap, allowedTransitions, allowedTransitionMapName); - return fromValueMap(newMap, transitionClass); - } - - /** - * Validates a new set of transitions and returns the resulting TimedTransitionProperty map. - * - * @param newTransitions map from date time to transition value - * @param transitionClass the class of transitions in this map + * @param newTransitions map from {@link DateTime} to transition value {@link V} * @param allowedTransitions optional map of all possible state-to-state transitions * @param allowedTransitionMapName optional transition map description string for error messages * @param initialValue optional initial value; if present, the first transition must have this * value * @param badInitialValueErrorMessage option error message string if the initial value is wrong */ - public static > - TimedTransitionProperty make( - ImmutableSortedMap newTransitions, - Class transitionClass, - ImmutableMultimap allowedTransitions, - String allowedTransitionMapName, - V initialValue, - String badInitialValueErrorMessage) { - validateTimedTransitionMap( - newTransitions, - allowedTransitions, - allowedTransitionMapName); + public static TimedTransitionProperty make( + ImmutableSortedMap newTransitions, + ImmutableMultimap allowedTransitions, + String allowedTransitionMapName, + V initialValue, + String badInitialValueErrorMessage) { + validateTimedTransitionMap(newTransitions, allowedTransitions, allowedTransitionMapName); checkArgument( - newTransitions.firstEntry().getValue() == initialValue, - badInitialValueErrorMessage); - return fromValueMap(newTransitions, transitionClass); + newTransitions.firstEntry().getValue() == initialValue, badInitialValueErrorMessage); + return fromValueMap(newTransitions); } /** - * Validates that a transition map is not null or empty, starts at START_OF_TIME, and has + * Validates that a transition map is not null or empty, starts at {@code START_OF_TIME}, and has * transitions which move from one value to another in allowed ways. */ - public static > - void validateTimedTransitionMap( - @Nullable NavigableMap transitionMap, - ImmutableMultimap allowedTransitions, - String mapName) { + public static void validateTimedTransitionMap( + @Nullable NavigableMap transitionMap, + ImmutableMultimap allowedTransitions, + String mapName) { checkArgument( !nullToEmpty(transitionMap).isEmpty(), "%s map cannot be null or empty.", mapName); checkArgument( @@ -236,78 +124,49 @@ public class TimedTransitionProperty< } } - /** - * Returns a new mutable {@code TimedTransitionProperty} representing the given map of DateTime to - * value, with transitions constructed using the given {@code TimedTransition} subclass. - * - *

This method should only be used for initializing fields that are declared with the @Mapify - * annotation. The map for those fields must be mutable so that Objectify can load values from - * Datastore into the map, but clients should still never mutate the field's map directly. - */ - public static > - TimedTransitionProperty forMapify( - ImmutableSortedMap valueMap, Class timedTransitionSubclass) { - return new TimedTransitionProperty<>( - new TreeMap<>(makeTransitionMap(valueMap, timedTransitionSubclass))); - } + /** The backing map of {@link DateTime} to the value {@link V} that transitions over time. */ + private final ImmutableSortedMap backingMap; - /** - * Returns a new mutable {@code TimedTransitionProperty} representing the given value being set at - * start of time, constructed using the given {@code TimedTransition} subclass. - * - *

This method should only be used for initializing fields that are declared with the @Mapify - * annotation. The map for those fields must be mutable so that Objectify can load values from - * Datastore into the map, but clients should still never mutate the field's map directly. - */ - public static > - TimedTransitionProperty forMapify( - V valueAtStartOfTime, Class timedTransitionSubclass) { - return forMapify( - ImmutableSortedMap.of(START_OF_TIME, valueAtStartOfTime), timedTransitionSubclass); - } - - /** The backing map of DateTime to TimedTransition subclass used to store the transitions. */ - private final NavigableMap backingMap; - - /** Returns a new {@code TimedTransitionProperty} backed by the provided map instance. */ - private TimedTransitionProperty(NavigableMap backingMap) { - checkArgument(backingMap.get(START_OF_TIME) != null, + /** Returns a new {@link TimedTransitionProperty} backed by the provided map instance. */ + private TimedTransitionProperty(NavigableMap backingMap) { + checkArgument( + backingMap.get(START_OF_TIME) != null, "Must provide transition entry for the start of time (Unix Epoch)"); - this.backingMap = backingMap; + this.backingMap = ImmutableSortedMap.copyOfSorted(backingMap); } /** - * Checks whether this TimedTransitionProperty is in a valid state, i.e. whether it has a - * transition entry for START_OF_TIME, and throws IllegalStateException if not. + * Checks whether this {@link TimedTransitionProperty} is in a valid state, i.e. whether it has a + * transition entry for {@code START_OF_TIME}, and throws {@link IllegalStateException} if not. */ public void checkValidity() { - checkState(backingMap.get(START_OF_TIME) != null, + checkState( + backingMap.get(START_OF_TIME) != null, "Timed transition values missing required entry for the start of time (Unix Epoch)"); } @Override - protected NavigableMap delegate() { + protected ImmutableSortedMap delegate() { return backingMap; } - /** Returns the map of DateTime to value that is the "natural" representation of this property. */ + /** Exposes the underlying {@link ImmutableSortedMap}. */ public ImmutableSortedMap toValueMap() { - return ImmutableSortedMap.copyOfSorted(Maps.transformValues(backingMap, T::getValue)); + return backingMap; } /** - * Returns the value of the property that is active at the specified time. The active value for - * a time before START_OF_TIME is extrapolated to be the value that is active at START_OF_TIME. + * Returns the value of the property that is active at the specified time. The active value for a + * time before {@code START_OF_TIME} is extrapolated to be the value that is active at {@code + * START_OF_TIME}. */ public V getValueAtTime(DateTime time) { // Retrieve the current value by finding the latest transition before or at the given time, // where any given time earlier than START_OF_TIME is replaced by START_OF_TIME. - return backingMap.floorEntry(latestOf(START_OF_TIME, time)).getValue().getValue(); + return backingMap.floorEntry(latestOf(START_OF_TIME, time)).getValue(); } - /** - * Returns the time of the next transition. Returns null if there is no subsequent transition. - */ + /** Returns the time of the next transition. Returns null if there is no subsequent transition. */ @Nullable public DateTime getNextTransitionAfter(DateTime time) { return backingMap.higherKey(latestOf(START_OF_TIME, time)); diff --git a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java index 926b9ff7d..4257a2507 100644 --- a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java +++ b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java @@ -31,11 +31,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Range; import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Index; -import com.googlecode.objectify.annotation.Mapify; import com.googlecode.objectify.annotation.OnLoad; import google.registry.flows.EppException; import google.registry.flows.domain.DomainFlowUtils; @@ -45,8 +43,6 @@ import google.registry.model.CreateAutoTimestamp; import google.registry.model.annotations.ReportedOn; import google.registry.model.billing.BillingEvent.RenewalPriceBehavior; import google.registry.model.common.TimedTransitionProperty; -import google.registry.model.common.TimedTransitionProperty.TimeMapper; -import google.registry.model.common.TimedTransitionProperty.TimedTransition; import google.registry.model.reporting.HistoryEntry; import google.registry.persistence.DomainHistoryVKey; import google.registry.persistence.VKey; @@ -81,6 +77,8 @@ import org.joda.time.DateTime; }) public class AllocationToken extends BackupGroupRoot implements Buildable { + private static final long serialVersionUID = -3954475393220876903L; + // Promotions should only move forward, and ENDED / CANCELLED are terminal states. private static final ImmutableMultimap VALID_TOKEN_STATUS_TRANSITIONS = ImmutableMultimap.builder() @@ -94,7 +92,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable { UNLIMITED_USE } - /** The status of this token with regards to any potential promotion. */ + /** The status of this token with regard to any potential promotion. */ public enum TokenStatus { /** Default status for a token. Either a promotion doesn't exist or it hasn't started. */ NOT_STARTED, @@ -169,29 +167,8 @@ public class AllocationToken extends BackupGroupRoot implements Buildable { *

If the token is promotional, the status will be VALID at the start of the promotion and * ENDED at the end. If manually cancelled, we will add a CANCELLED status. */ - @Mapify(TimeMapper.class) - TimedTransitionProperty tokenStatusTransitions = - TimedTransitionProperty.forMapify(NOT_STARTED, TokenStatusTransition.class); - - /** - * A transition to a given token status at a specific time, for use in a TimedTransitionProperty. - * - *

Public because App Engine's security manager requires this for instantiation via reflection. - */ - @Embed - public static class TokenStatusTransition extends TimedTransition { - private TokenStatus tokenStatus; - - @Override - public TokenStatus getValue() { - return tokenStatus; - } - - @Override - protected void setValue(TokenStatus tokenStatus) { - this.tokenStatus = tokenStatus; - } - } + TimedTransitionProperty tokenStatusTransitions = + TimedTransitionProperty.withInitialValue(NOT_STARTED); public String getToken() { return token; @@ -240,7 +217,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable { return tokenType; } - public TimedTransitionProperty getTokenStatusTransitions() { + public TimedTransitionProperty getTokenStatusTransitions() { return tokenStatusTransitions; } @@ -364,7 +341,6 @@ public class AllocationToken extends BackupGroupRoot implements Buildable { getInstance().tokenStatusTransitions = TimedTransitionProperty.make( transitions, - TokenStatusTransition.class, VALID_TOKEN_STATUS_TRANSITIONS, "tokenStatusTransitions", NOT_STARTED, diff --git a/core/src/main/java/google/registry/model/registrar/Registrar.java b/core/src/main/java/google/registry/model/registrar/Registrar.java index 7a0680660..5d8882f6b 100644 --- a/core/src/main/java/google/registry/model/registrar/Registrar.java +++ b/core/src/main/java/google/registry/model/registrar/Registrar.java @@ -20,11 +20,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap; import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; -import static com.google.common.collect.Ordering.natural; import static com.google.common.collect.Sets.immutableEnumSet; import static com.google.common.io.BaseEncoding.base64; import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer; @@ -51,6 +48,7 @@ import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Maps; import com.google.common.collect.Range; @@ -58,15 +56,12 @@ import com.google.common.collect.Sets; import com.google.common.collect.Streams; import com.google.re2j.Pattern; import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Embed; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.Index; -import com.googlecode.objectify.annotation.Mapify; import com.googlecode.objectify.annotation.Parent; import com.googlecode.objectify.condition.IfNull; -import com.googlecode.objectify.mapper.Mapper; import google.registry.model.Buildable; import google.registry.model.CreateAutoTimestamp; import google.registry.model.ImmutableObject; @@ -77,7 +72,6 @@ import google.registry.model.UpdateAutoTimestamp; import google.registry.model.annotations.InCrossTld; import google.registry.model.annotations.ReportedOn; import google.registry.model.common.EntityGroupRoot; -import google.registry.model.registrar.Registrar.BillingAccountEntry.CurrencyMapper; import google.registry.model.tld.Registry; import google.registry.model.tld.Registry.TldType; import google.registry.persistence.VKey; @@ -394,42 +388,7 @@ public class Registrar extends ImmutableObject * accessed by {@link #getBillingAccountMap}, a sorted map is returned to guarantee deterministic * behavior when serializing the map, for display purpose for instance. */ - @Nullable - @Mapify(CurrencyMapper.class) - Map billingAccountMap; - - /** A billing account entry for this registrar, consisting of a currency and an account Id. */ - @Embed - public static class BillingAccountEntry extends ImmutableObject implements UnsafeSerializable { - - CurrencyUnit currency; - String accountId; - - BillingAccountEntry() {} - - public BillingAccountEntry(CurrencyUnit currency, String accountId) { - this.accountId = accountId; - this.currency = currency; - } - - BillingAccountEntry(Map.Entry entry) { - this.accountId = entry.getValue(); - this.currency = entry.getKey(); - } - - /** Mapper to use for {@code @Mapify}. */ - static class CurrencyMapper implements Mapper { - @Override - public CurrencyUnit getKey(BillingAccountEntry billingAccountEntry) { - return billingAccountEntry.currency; - } - } - - /** Returns the account id of this entry. */ - public String getAccountId() { - return accountId; - } - } + @Nullable Map billingAccountMap; /** URL of registrar's website. */ String url; @@ -493,12 +452,10 @@ public class Registrar extends ImmutableObject return Optional.ofNullable(poNumber); } - public ImmutableMap getBillingAccountMap() { - if (billingAccountMap == null) { - return ImmutableMap.of(); - } - return billingAccountMap.entrySet().stream() - .collect(toImmutableSortedMap(natural(), Map.Entry::getKey, v -> v.getValue().accountId)); + public ImmutableSortedMap getBillingAccountMap() { + return billingAccountMap == null + ? ImmutableSortedMap.of() + : ImmutableSortedMap.copyOf(billingAccountMap); } public DateTime getLastUpdateTime() { @@ -776,9 +733,7 @@ public class Registrar extends ImmutableObject } public Builder setBillingAccountMap(@Nullable Map billingAccountMap) { - getInstance().billingAccountMap = - nullToEmptyImmutableCopy(billingAccountMap).entrySet().stream() - .collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new)); + getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap); return this; } diff --git a/core/src/main/java/google/registry/model/tld/Registry.java b/core/src/main/java/google/registry/model/tld/Registry.java index 4b3ba0f88..e9eb75ce2 100644 --- a/core/src/main/java/google/registry/model/tld/Registry.java +++ b/core/src/main/java/google/registry/model/tld/Registry.java @@ -43,7 +43,6 @@ import google.registry.model.CreateAutoTimestamp; import google.registry.model.ImmutableObject; import google.registry.model.UnsafeSerializable; import google.registry.model.common.TimedTransitionProperty; -import google.registry.model.common.TimedTransitionProperty.TimedTransition; import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.Fee; import google.registry.model.tld.label.PremiumList; @@ -154,47 +153,6 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial PDT } - /** - * A transition to a TLD state at a specific time, for use in a TimedTransitionProperty. Public - * because App Engine's security manager requires this for instantiation via reflection. - */ - public static class TldStateTransition extends TimedTransition { - - /** The TLD state. */ - private TldState tldState; - - @Override - public TldState getValue() { - return tldState; - } - - @Override - protected void setValue(TldState tldState) { - this.tldState = tldState; - } - } - - /** - * A transition to a given billing cost at a specific time, for use in a TimedTransitionProperty. - * - *

Public because App Engine's security manager requires this for instantiation via reflection. - */ - public static class BillingCostTransition extends TimedTransition { - - /** The billing cost value. */ - private Money billingCost; - - @Override - public Money getValue() { - return billingCost; - } - - @Override - protected void setValue(Money billingCost) { - this.billingCost = billingCost; - } - } - /** Returns the registry for a given TLD, throwing if none exists. */ public static Registry get(String tld) { Registry maybeRegistry = CACHE.get(tld); @@ -343,13 +301,10 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial @Column(nullable = false) boolean invoicingEnabled; - /** - * A property that transitions to different {@link TldState}s at different times. Stored as a list - * of {@link TldStateTransition} embedded objects using the @Mapify annotation. - */ + /** A property that transitions to different {@link TldState}s at different times. */ @Column(nullable = false) - TimedTransitionProperty tldStateTransitions = - TimedTransitionProperty.forMapify(DEFAULT_TLD_STATE, TldStateTransition.class); + TimedTransitionProperty tldStateTransitions = + TimedTransitionProperty.withInitialValue(DEFAULT_TLD_STATE); /** An automatically managed creation timestamp. */ @Column(nullable = false) @@ -468,21 +423,20 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial Money registryLockOrUnlockBillingCost = DEFAULT_REGISTRY_LOCK_OR_UNLOCK_BILLING_COST; /** - * A property that transitions to different renew billing costs at different times. Stored as a - * list of BillingCostTransition embedded objects using the @Mapify annotation. + * A property that transitions to different renew billing costs at different times. * *

A given value of this property represents the per-year billing cost for renewing a domain * name. This cost is also used to compute costs for transfers, since each transfer includes a * renewal to ensure transfers have a cost. */ @Column(nullable = false) - TimedTransitionProperty renewBillingCostTransitions = - TimedTransitionProperty.forMapify(DEFAULT_RENEW_BILLING_COST, BillingCostTransition.class); + TimedTransitionProperty renewBillingCostTransitions = + TimedTransitionProperty.withInitialValue(DEFAULT_RENEW_BILLING_COST); /** A property that tracks the EAP fee schedule (if any) for the TLD. */ @Column(nullable = false) - TimedTransitionProperty eapFeeSchedule = - TimedTransitionProperty.forMapify(DEFAULT_EAP_BILLING_COST, BillingCostTransition.class); + TimedTransitionProperty eapFeeSchedule = + TimedTransitionProperty.withInitialValue(DEFAULT_EAP_BILLING_COST); /** Marksdb LORDN service username (password is stored in Keyring) */ String lordnUsername; @@ -720,8 +674,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial .filter(state -> !TldState.QUIET_PERIOD.equals(state)) .collect(Collectors.toList())), "The TLD states are chronologically out of order"); - getInstance().tldStateTransitions = - TimedTransitionProperty.fromValueMap(tldStatesMap, TldStateTransition.class); + getInstance().tldStateTransitions = TimedTransitionProperty.fromValueMap(tldStatesMap); return this; } @@ -887,7 +840,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial renewCostsMap.values().stream().allMatch(Money::isPositiveOrZero), "Renew billing cost cannot be negative"); getInstance().renewBillingCostTransitions = - TimedTransitionProperty.fromValueMap(renewCostsMap, BillingCostTransition.class); + TimedTransitionProperty.fromValueMap(renewCostsMap); return this; } @@ -897,8 +850,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial checkArgument( eapFeeSchedule.values().stream().allMatch(Money::isPositiveOrZero), "EAP fee cannot be negative"); - getInstance().eapFeeSchedule = - TimedTransitionProperty.fromValueMap(eapFeeSchedule, BillingCostTransition.class); + getInstance().eapFeeSchedule = TimedTransitionProperty.fromValueMap(eapFeeSchedule); return this; } diff --git a/core/src/main/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverter.java b/core/src/main/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverter.java index 1a4bee5a6..72739f612 100644 --- a/core/src/main/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverter.java +++ b/core/src/main/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverter.java @@ -14,36 +14,21 @@ package google.registry.persistence.converter; -import com.google.common.collect.Maps; -import google.registry.model.common.TimedTransitionProperty; import google.registry.model.domain.token.AllocationToken.TokenStatus; -import google.registry.model.domain.token.AllocationToken.TokenStatusTransition; -import java.util.Map; import javax.persistence.Converter; -import org.joda.time.DateTime; -/** - * JPA converter for storing/retrieving {@link TimedTransitionProperty} maps referring to {@link - * TokenStatus}es. - */ +/** JPA converter for storing/retrieving {@code TimedTransitionProperty} objects. */ @Converter(autoApply = true) public class AllocationTokenStatusTransitionConverter - extends TimedTransitionPropertyConverterBase { + extends TimedTransitionPropertyConverterBase { @Override - Map.Entry convertToDatabaseMapEntry( - Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name()); + protected String convertValueToString(TokenStatus value) { + return value.name(); } @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry( - DateTime.parse(entry.getKey()), TokenStatus.valueOf(entry.getValue())); - } - - @Override - Class getTimedTransitionSubclass() { - return TokenStatusTransition.class; + protected TokenStatus convertStringToValue(String string) { + return TokenStatus.valueOf(string); } } diff --git a/core/src/main/java/google/registry/persistence/converter/BillingCostTransitionConverter.java b/core/src/main/java/google/registry/persistence/converter/BillingCostTransitionConverter.java index b15dc5b97..8e3d28bcc 100644 --- a/core/src/main/java/google/registry/persistence/converter/BillingCostTransitionConverter.java +++ b/core/src/main/java/google/registry/persistence/converter/BillingCostTransitionConverter.java @@ -14,34 +14,19 @@ package google.registry.persistence.converter; -import avro.shaded.com.google.common.collect.Maps; -import google.registry.model.tld.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 {@code TimedTransitionProperty} - * objects. - */ +/** JPA converter for storing/retrieving {@code TimedTransitionProperty} objects. */ @Converter(autoApply = true) -public class BillingCostTransitionConverter - extends TimedTransitionPropertyConverterBase { - +public class BillingCostTransitionConverter extends TimedTransitionPropertyConverterBase { @Override - Map.Entry convertToDatabaseMapEntry( - Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().toString()); + protected String convertValueToString(Money value) { + return value.toString(); } @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry(DateTime.parse(entry.getKey()), Money.parse(entry.getValue())); - } - - @Override - Class getTimedTransitionSubclass() { - return BillingCostTransition.class; + protected Money convertStringToValue(String string) { + return Money.parse(string); } } diff --git a/core/src/main/java/google/registry/persistence/converter/CurrencyToBillingConverter.java b/core/src/main/java/google/registry/persistence/converter/CurrencyToBillingConverter.java index 1170c9390..620439b35 100644 --- a/core/src/main/java/google/registry/persistence/converter/CurrencyToBillingConverter.java +++ b/core/src/main/java/google/registry/persistence/converter/CurrencyToBillingConverter.java @@ -14,30 +14,37 @@ package google.registry.persistence.converter; -import static google.registry.model.registrar.Registrar.BillingAccountEntry; - -import com.google.common.collect.Maps; import java.util.Map; import javax.persistence.Converter; import org.joda.money.CurrencyUnit; -/** JPA converter for storing/retrieving {@code Map} objects. */ +/** JPA converter for storing/retrieving {@code Map} objects. */ @Converter(autoApply = true) public class CurrencyToBillingConverter - extends StringMapConverterBase { + extends StringMapConverterBase> { @Override - Map.Entry convertToDatabaseMapEntry( - Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().getCode(), entry.getValue().getAccountId()); + protected String convertKeyToString(CurrencyUnit key) { + return key.getCode(); } @Override - Map.Entry convertToEntityMapEntry( - Map.Entry entry) { - CurrencyUnit currencyUnit = CurrencyUnit.of(entry.getKey()); - BillingAccountEntry billingAccountEntry = - new BillingAccountEntry(currencyUnit, entry.getValue()); - return Maps.immutableEntry(currencyUnit, billingAccountEntry); + protected String convertValueToString(String value) { + return value; + } + + @Override + protected CurrencyUnit convertStringToKey(String string) { + return CurrencyUnit.of(string); + } + + @Override + protected String convertStringToValue(String string) { + return string; + } + + @Override + protected Map convertMapToDerivedType(Map map) { + return map; } } diff --git a/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java b/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java index 70f2b5ff6..04722089c 100644 --- a/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java +++ b/core/src/main/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverter.java @@ -14,35 +14,24 @@ package google.registry.persistence.converter; -import com.google.common.collect.Maps; import google.registry.model.annotations.DeleteAfterMigration; import google.registry.model.common.DatabaseMigrationStateSchedule; import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState; -import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationStateTransition; -import java.util.Map; import javax.persistence.Converter; -import org.joda.time.DateTime; /** JPA converter for {@link DatabaseMigrationStateSchedule} transitions. */ @DeleteAfterMigration @Converter(autoApply = true) public class DatabaseMigrationScheduleTransitionConverter - extends TimedTransitionPropertyConverterBase { + extends TimedTransitionPropertyConverterBase { @Override - Map.Entry convertToDatabaseMapEntry( - Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name()); + protected String convertValueToString(MigrationState value) { + return value.name(); } @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry( - DateTime.parse(entry.getKey()), MigrationState.valueOf(entry.getValue())); - } - - @Override - Class getTimedTransitionSubclass() { - return MigrationStateTransition.class; + protected MigrationState convertStringToValue(String string) { + return MigrationState.valueOf(string); } } diff --git a/core/src/main/java/google/registry/persistence/converter/StringMapConverterBase.java b/core/src/main/java/google/registry/persistence/converter/StringMapConverterBase.java index 3d8f1c12f..311f5ce71 100644 --- a/core/src/main/java/google/registry/persistence/converter/StringMapConverterBase.java +++ b/core/src/main/java/google/registry/persistence/converter/StringMapConverterBase.java @@ -16,6 +16,7 @@ package google.registry.persistence.converter; import static google.registry.util.CollectionUtils.entriesToImmutableMap; +import com.google.common.collect.Maps; import google.registry.persistence.converter.StringMapDescriptor.StringMap; import java.util.Map; import javax.persistence.AttributeConverter; @@ -24,15 +25,31 @@ import javax.persistence.AttributeConverter; * Base JPA converter for {@link Map} objects that are stored in a column with data type of hstore * in the database. */ -public abstract class StringMapConverterBase - implements AttributeConverter, StringMap> { +public abstract class StringMapConverterBase> + implements AttributeConverter { - abstract Map.Entry convertToDatabaseMapEntry(Map.Entry entry); + protected abstract String convertKeyToString(K key); - abstract Map.Entry convertToEntityMapEntry(Map.Entry entry); + protected abstract String convertValueToString(V value); + + protected abstract K convertStringToKey(String string); + + protected abstract V convertStringToValue(String string); + + protected abstract M convertMapToDerivedType(Map map); + + private Map.Entry convertToDatabaseMapEntry(Map.Entry entry) { + return Maps.immutableEntry( + convertKeyToString(entry.getKey()), convertValueToString(entry.getValue())); + } + + private Map.Entry convertToEntityMapEntry(Map.Entry entry) { + return Maps.immutableEntry( + convertStringToKey(entry.getKey()), convertStringToValue(entry.getValue())); + } @Override - public StringMap convertToDatabaseColumn(Map attribute) { + public StringMap convertToDatabaseColumn(M attribute) { return attribute == null ? null : StringMap.create( @@ -42,11 +59,12 @@ public abstract class StringMapConverterBase } @Override - public Map convertToEntityAttribute(StringMap dbData) { + public M convertToEntityAttribute(StringMap dbData) { return dbData == null ? null - : dbData.getMap().entrySet().stream() - .map(this::convertToEntityMapEntry) - .collect(entriesToImmutableMap()); + : convertMapToDerivedType( + dbData.getMap().entrySet().stream() + .map(this::convertToEntityMapEntry) + .collect(entriesToImmutableMap())); } } diff --git a/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java b/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java index ff69ea3d4..c9e8a2af0 100644 --- a/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java +++ b/core/src/main/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBase.java @@ -14,53 +14,31 @@ package google.registry.persistence.converter; -import static google.registry.util.CollectionUtils.entriesToImmutableMap; - -import com.google.common.collect.ImmutableMap; 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.io.Serializable; 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 extends Serializable, V extends TimedTransition> - implements AttributeConverter, StringMap> { - - abstract Map.Entry convertToDatabaseMapEntry(Map.Entry entry); - - abstract Map.Entry convertToEntityMapEntry(Map.Entry entry); - - abstract Class getTimedTransitionSubclass(); +public abstract class TimedTransitionPropertyConverterBase + extends StringMapConverterBase> { @Override - public StringMap convertToDatabaseColumn(@Nullable TimedTransitionProperty attribute) { - return attribute == null - ? null - : StringMap.create( - attribute.entrySet().stream() - .map(this::convertToDatabaseMapEntry) - .collect(entriesToImmutableMap())); + protected String convertKeyToString(DateTime key) { + return key.toString(); } @Override - public TimedTransitionProperty convertToEntityAttribute(@Nullable StringMap dbData) { - if (dbData == null) { - return null; - } - ImmutableMap map = - dbData.getMap().entrySet().stream() - .map(this::convertToEntityMapEntry) - .collect(entriesToImmutableMap()); - return TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.copyOf(map), getTimedTransitionSubclass()); + protected DateTime convertStringToKey(String string) { + return DateTime.parse(string); + } + + @Override + protected TimedTransitionProperty convertMapToDerivedType(Map map) { + return TimedTransitionProperty.fromValueMap(ImmutableSortedMap.copyOf(map)); } } diff --git a/core/src/main/java/google/registry/persistence/converter/TldStateTransitionConverter.java b/core/src/main/java/google/registry/persistence/converter/TldStateTransitionConverter.java index 076faebec..5c6fa7735 100644 --- a/core/src/main/java/google/registry/persistence/converter/TldStateTransitionConverter.java +++ b/core/src/main/java/google/registry/persistence/converter/TldStateTransitionConverter.java @@ -14,34 +14,19 @@ package google.registry.persistence.converter; -import com.google.common.collect.Maps; import google.registry.model.tld.Registry.TldState; -import google.registry.model.tld.Registry.TldStateTransition; -import java.util.Map; import javax.persistence.Converter; -import org.joda.time.DateTime; -/** - * JPA converter for storing/retrieving {@code TimedTransitionProperty} objects. - */ +/** JPA converter for storing/retrieving {@code TimedTransitionProperty} objects. */ @Converter(autoApply = true) -public class TldStateTransitionConverter - extends TimedTransitionPropertyConverterBase { - +public class TldStateTransitionConverter extends TimedTransitionPropertyConverterBase { @Override - Map.Entry convertToDatabaseMapEntry( - Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name()); + protected String convertValueToString(TldState value) { + return value.name(); } @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry(DateTime.parse(entry.getKey()), TldState.valueOf(entry.getValue())); - } - - @Override - Class getTimedTransitionSubclass() { - return TldStateTransition.class; + protected TldState convertStringToValue(String string) { + return TldState.valueOf(string); } } diff --git a/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java b/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java index 8a4c680f2..6571dc28d 100644 --- a/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java +++ b/core/src/main/java/google/registry/tools/GetDatabaseMigrationStateCommand.java @@ -18,7 +18,6 @@ import com.beust.jcommander.Parameters; import google.registry.model.annotations.DeleteAfterMigration; import google.registry.model.common.DatabaseMigrationStateSchedule; import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState; -import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationStateTransition; import google.registry.model.common.TimedTransitionProperty; /** A command to check the current Registry 3.0 migration state of the database. */ @@ -28,9 +27,8 @@ public class GetDatabaseMigrationStateCommand implements CommandWithRemoteApi { @Override public void run() throws Exception { - TimedTransitionProperty migrationSchedule = + TimedTransitionProperty migrationSchedule = DatabaseMigrationStateSchedule.get(); - System.out.println( - String.format("Current migration schedule: %s", migrationSchedule.toValueMap())); + System.out.printf("Current migration schedule: %s%n", migrationSchedule.toValueMap()); } } diff --git a/core/src/test/java/google/registry/model/common/TimedTransitionPropertyTest.java b/core/src/test/java/google/registry/model/common/TimedTransitionPropertyTest.java index c812003fc..1a5812e68 100644 --- a/core/src/test/java/google/registry/model/common/TimedTransitionPropertyTest.java +++ b/core/src/test/java/google/registry/model/common/TimedTransitionPropertyTest.java @@ -15,16 +15,12 @@ package google.registry.model.common; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.common.TimedTransitionProperty.forMapify; import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.joda.time.DateTimeZone.UTC; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSortedMap; -import java.util.Map; -import java.util.Set; import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,35 +33,17 @@ class TimedTransitionPropertyTest { private static final DateTime DATE_2 = DateTime.parse("2002-01-01T00:00:00.0Z"); private static final DateTime DATE_3 = DateTime.parse("2003-01-01T00:00:00.0Z"); - // Simple implementation of TimedTransition for testing. Public so it can be instantiated via - // reflection. - @VisibleForTesting - public static class StringTimedTransition - extends TimedTransitionProperty.TimedTransition { - private String value; - - @Override - public String getValue() { - return value; - } - - @Override - public void setValue(String value) { - this.value = value; - } - } - private static final ImmutableSortedMap values = ImmutableSortedMap.of( START_OF_TIME, "0", DATE_1, "1", DATE_2, "2", DATE_3, "3"); - private TimedTransitionProperty timedString; + private TimedTransitionProperty timedString; @BeforeEach void init() { - timedString = TimedTransitionProperty.fromValueMap(values, StringTimedTransition.class); + timedString = TimedTransitionProperty.fromValueMap(values); } @Test @@ -73,8 +51,7 @@ class TimedTransitionPropertyTest { assertThat(timedString.toValueMap()).isEqualTo(values); } - private static void testGetValueAtTime( - TimedTransitionProperty timedString) { + private static void testGetValueAtTime(TimedTransitionProperty timedString) { assertThat(timedString.getValueAtTime(A_LONG_TIME_AGO)).isEqualTo("0"); assertThat(timedString.getValueAtTime(START_OF_TIME.minusMillis(1))).isEqualTo("0"); assertThat(timedString.getValueAtTime(START_OF_TIME)).isEqualTo("0"); @@ -106,74 +83,35 @@ class TimedTransitionPropertyTest { assertThat(timedString.getNextTransitionAfter(DATE_3)).isNull(); } - @Test - void testSuccess_simulatedLoad() { - // Just for testing, don't extract transitions from a TimedTransitionProperty in real code. - Set> transitions = timedString.entrySet(); - timedString = forMapify("0", StringTimedTransition.class); - // Simulate a load from Datastore by clearing and then re-inserting the original transitions. - timedString.clear(); - for (Map.Entry transition : transitions) { - timedString.put(transition.getKey(), transition.getValue()); - } - timedString.checkValidity(); - testGetValueAtTime(timedString); - } - @Test void testFailure_valueMapNotChronologicallyOrdered() { assertThrows( IllegalArgumentException.class, () -> TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.reverseOrder().put(START_OF_TIME, "0").build(), - StringTimedTransition.class)); + ImmutableSortedMap.reverseOrder() + .put(START_OF_TIME, "0") + .build())); } @Test void testFailure_transitionTimeBeforeStartOfTime() { assertThrows( IllegalArgumentException.class, - () -> - TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.of(A_LONG_TIME_AGO, "?"), StringTimedTransition.class)); + () -> TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of(A_LONG_TIME_AGO, "?"))); } @Test void testFailure_noValues() { assertThrows( IllegalArgumentException.class, - () -> - TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.of(), StringTimedTransition.class)); + () -> TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of())); } @Test void testFailure_noValueAtStartOfTime() { assertThrows( IllegalArgumentException.class, - () -> - TimedTransitionProperty.fromValueMap( - ImmutableSortedMap.of(DATE_1, "1"), StringTimedTransition.class)); - } - - @Test - void testFailure_noValuesAfterSimulatedEmptyLoad() { - timedString = forMapify("0", StringTimedTransition.class); - // Simulate a load from Datastore by clearing, but don't insert any transitions. - timedString.clear(); - assertThrows(IllegalStateException.class, timedString::checkValidity); - } - - @Test - void testFailure_noValueAtStartOfTimeAfterSimulatedLoad() { - // Just for testing, don't extract transitions from a TimedTransitionProperty in real code. - StringTimedTransition transition1 = timedString.get(DATE_1); - timedString = forMapify("0", StringTimedTransition.class); - // Simulate a load from Datastore by clearing and inserting transitions, but deliberately - // omit a transition corresponding to START_OF_TIME. - timedString.clear(); - timedString.put(DATE_1, transition1); - assertThrows(IllegalStateException.class, timedString::checkValidity); + () -> TimedTransitionProperty.fromValueMap(ImmutableSortedMap.of(DATE_1, "1"))); } } diff --git a/core/src/test/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverterTest.java b/core/src/test/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverterTest.java index 7a51cebe3..3f0dcbc4e 100644 --- a/core/src/test/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverterTest.java +++ b/core/src/test/java/google/registry/persistence/converter/AllocationTokenStatusTransitionConverterTest.java @@ -26,7 +26,6 @@ import com.google.common.collect.ImmutableSortedMap; import google.registry.model.ImmutableObject; import google.registry.model.common.TimedTransitionProperty; import google.registry.model.domain.token.AllocationToken.TokenStatus; -import google.registry.model.domain.token.AllocationToken.TokenStatusTransition; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; import javax.persistence.Entity; @@ -55,8 +54,8 @@ public class AllocationTokenStatusTransitionConverterTest { @Test void roundTripConversion_returnsSameTimedTransitionProperty() { - TimedTransitionProperty timedTransitionProperty = - TimedTransitionProperty.fromValueMap(values, TokenStatusTransition.class); + TimedTransitionProperty timedTransitionProperty = + TimedTransitionProperty.fromValueMap(values); AllocationTokenStatusTransitionConverterTestEntity testEntity = new AllocationTokenStatusTransitionConverterTestEntity(timedTransitionProperty); insertInDb(testEntity); @@ -75,12 +74,12 @@ public class AllocationTokenStatusTransitionConverterTest { @Id String name = "id"; - TimedTransitionProperty timedTransitionProperty; + TimedTransitionProperty timedTransitionProperty; private AllocationTokenStatusTransitionConverterTestEntity() {} private AllocationTokenStatusTransitionConverterTestEntity( - TimedTransitionProperty timedTransitionProperty) { + TimedTransitionProperty timedTransitionProperty) { this.timedTransitionProperty = timedTransitionProperty; } } diff --git a/core/src/test/java/google/registry/persistence/converter/BillingCostTransitionConverterTest.java b/core/src/test/java/google/registry/persistence/converter/BillingCostTransitionConverterTest.java index e8e1a626a..0861acc76 100644 --- a/core/src/test/java/google/registry/persistence/converter/BillingCostTransitionConverterTest.java +++ b/core/src/test/java/google/registry/persistence/converter/BillingCostTransitionConverterTest.java @@ -23,7 +23,6 @@ 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.tld.Registry.BillingCostTransition; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; import javax.persistence.Entity; @@ -49,8 +48,8 @@ public class BillingCostTransitionConverterTest { @Test void roundTripConversion_returnsSameTimedTransitionProperty() { - TimedTransitionProperty timedTransitionProperty = - TimedTransitionProperty.fromValueMap(values, BillingCostTransition.class); + TimedTransitionProperty timedTransitionProperty = + TimedTransitionProperty.fromValueMap(values); TestEntity testEntity = new TestEntity(timedTransitionProperty); insertInDb(testEntity); TestEntity persisted = @@ -63,12 +62,11 @@ public class BillingCostTransitionConverterTest { @Id String name = "id"; - TimedTransitionProperty timedTransitionProperty; + TimedTransitionProperty timedTransitionProperty; private TestEntity() {} - private TestEntity( - TimedTransitionProperty timedTransitionProperty) { + private TestEntity(TimedTransitionProperty timedTransitionProperty) { this.timedTransitionProperty = timedTransitionProperty; } } diff --git a/core/src/test/java/google/registry/persistence/converter/CurrencyToBillingConverterTest.java b/core/src/test/java/google/registry/persistence/converter/CurrencyToBillingConverterTest.java index cd5593563..b315c1f66 100644 --- a/core/src/test/java/google/registry/persistence/converter/CurrencyToBillingConverterTest.java +++ b/core/src/test/java/google/registry/persistence/converter/CurrencyToBillingConverterTest.java @@ -20,7 +20,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb; import com.google.common.collect.ImmutableMap; import google.registry.model.ImmutableObject; -import google.registry.model.registrar.Registrar.BillingAccountEntry; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; import java.util.Map; @@ -39,12 +38,10 @@ public class CurrencyToBillingConverterTest { @Test void roundTripConversion_returnsSameCurrencyToBillingMap() { - ImmutableMap currencyToBilling = + ImmutableMap currencyToBilling = ImmutableMap.of( - CurrencyUnit.of("USD"), - new BillingAccountEntry(CurrencyUnit.of("USD"), "accountId1"), - CurrencyUnit.of("CNY"), - new BillingAccountEntry(CurrencyUnit.of("CNY"), "accountId2")); + CurrencyUnit.of("USD"), "accountId1", + CurrencyUnit.of("CNY"), "accountId2"); TestEntity testEntity = new TestEntity(currencyToBilling); insertInDb(testEntity); TestEntity persisted = @@ -57,11 +54,11 @@ public class CurrencyToBillingConverterTest { @Id String name = "id"; - Map currencyToBilling; + Map currencyToBilling; private TestEntity() {} - private TestEntity(Map currencyToBilling) { + private TestEntity(Map currencyToBilling) { this.currencyToBilling = currencyToBilling; } } diff --git a/core/src/test/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverterTest.java b/core/src/test/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverterTest.java index 85f268ad0..09612fc5b 100644 --- a/core/src/test/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverterTest.java +++ b/core/src/test/java/google/registry/persistence/converter/DatabaseMigrationScheduleTransitionConverterTest.java @@ -22,7 +22,6 @@ 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.DatabaseMigrationStateSchedule.MigrationState; -import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationStateTransition; import google.registry.model.common.TimedTransitionProperty; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; @@ -58,8 +57,8 @@ public class DatabaseMigrationScheduleTransitionConverterTest { @Test void roundTripConversion_returnsSameTimedTransitionProperty() { - TimedTransitionProperty timedTransitionProperty = - TimedTransitionProperty.fromValueMap(values, MigrationStateTransition.class); + TimedTransitionProperty timedTransitionProperty = + TimedTransitionProperty.fromValueMap(values); DatabaseMigrationScheduleTransitionConverterTestEntity testEntity = new DatabaseMigrationScheduleTransitionConverterTestEntity(timedTransitionProperty); insertInDb(testEntity); @@ -79,12 +78,12 @@ public class DatabaseMigrationScheduleTransitionConverterTest { @Id String name = "id"; - TimedTransitionProperty timedTransitionProperty; + TimedTransitionProperty timedTransitionProperty; private DatabaseMigrationScheduleTransitionConverterTestEntity() {} private DatabaseMigrationScheduleTransitionConverterTestEntity( - TimedTransitionProperty timedTransitionProperty) { + TimedTransitionProperty timedTransitionProperty) { this.timedTransitionProperty = timedTransitionProperty; } } diff --git a/core/src/test/java/google/registry/persistence/converter/StringMapConverterBaseTest.java b/core/src/test/java/google/registry/persistence/converter/StringMapConverterBaseTest.java index 1b8811f87..3a442e19c 100644 --- a/core/src/test/java/google/registry/persistence/converter/StringMapConverterBaseTest.java +++ b/core/src/test/java/google/registry/persistence/converter/StringMapConverterBaseTest.java @@ -20,7 +20,6 @@ import static google.registry.testing.DatabaseHelper.insertInDb; import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; import google.registry.model.ImmutableObject; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; @@ -128,7 +127,7 @@ public class StringMapConverterBaseTest { } private static class Key extends ImmutableObject { - private String key; + private final String key; private Key(String key) { this.key = key; @@ -136,7 +135,7 @@ public class StringMapConverterBaseTest { } private static class Value extends ImmutableObject { - private String value; + private final String value; private Value(String value) { this.value = value; @@ -144,16 +143,32 @@ public class StringMapConverterBaseTest { } @Converter(autoApply = true) - private static class TestStringMapConverter extends StringMapConverterBase { + private static class TestStringMapConverter + extends StringMapConverterBase> { @Override - Map.Entry convertToDatabaseMapEntry(Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().key, entry.getValue().value); + protected String convertKeyToString(Key key) { + return key.key; } @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry(new Key(entry.getKey()), new Value(entry.getValue())); + protected String convertValueToString(Value value) { + return value.value; + } + + @Override + protected Key convertStringToKey(String string) { + return new Key(string); + } + + @Override + protected Value convertStringToValue(String string) { + return new Value(string); + } + + @Override + protected Map convertMapToDerivedType(Map map) { + return map; } } diff --git a/core/src/test/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBaseTest.java b/core/src/test/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBaseTest.java index e944f26cf..6cc3af72a 100644 --- a/core/src/test/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBaseTest.java +++ b/core/src/test/java/google/registry/persistence/converter/TimedTransitionPropertyConverterBaseTest.java @@ -21,13 +21,10 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME; import static org.junit.jupiter.api.Assertions.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.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; -import java.util.Map; import javax.persistence.Converter; import javax.persistence.Entity; import javax.persistence.Id; @@ -54,8 +51,8 @@ class TimedTransitionPropertyConverterBaseTest { DATE_1, "val2", DATE_2, "val3"); - private static final TimedTransitionProperty TIMED_TRANSITION_PROPERTY = - TimedTransitionProperty.fromValueMap(VALUES, TestTransition.class); + private static final TimedTransitionProperty TIMED_TRANSITION_PROPERTY = + TimedTransitionProperty.fromValueMap(VALUES); @Test void roundTripConversion_returnsSameTimedTransitionProperty() { @@ -74,11 +71,11 @@ class TimedTransitionPropertyConverterBaseTest { jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id")); assertThat(persisted.property).containsExactlyEntriesIn(TIMED_TRANSITION_PROPERTY); ImmutableSortedMap newValues = ImmutableSortedMap.of(START_OF_TIME, "val4"); - persisted.property = TimedTransitionProperty.fromValueMap(newValues, TestTransition.class); + persisted.property = TimedTransitionProperty.fromValueMap(newValues); jpaTm().transact(() -> jpaTm().getEntityManager().merge(persisted)); TestEntity updated = jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id")); - assertThat(updated.property.toValueMap()).isEqualTo(newValues); + assertThat(updated.property).isEqualTo(newValues); } @Test @@ -131,37 +128,18 @@ class TimedTransitionPropertyConverterBaseTest { jpaTm().transact(() -> jpaTm().getEntityManager().createNativeQuery(sql).executeUpdate()); } - public static class TestTransition extends TimedTransition { - 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 { + extends TimedTransitionPropertyConverterBase { @Override - Map.Entry convertToEntityMapEntry(Map.Entry entry) { - return Maps.immutableEntry(DateTime.parse(entry.getKey()), entry.getValue()); + protected String convertValueToString(String value) { + return value; } @Override - Class getTimedTransitionSubclass() { - return TestTransition.class; - } - - @Override - Map.Entry convertToDatabaseMapEntry(Map.Entry entry) { - return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue()); + protected String convertStringToValue(String string) { + return string; } } @@ -170,12 +148,12 @@ class TimedTransitionPropertyConverterBaseTest { @Id String name = "id"; - TimedTransitionProperty property; + TimedTransitionProperty property; private TestEntity() {} - private TestEntity(TimedTransitionProperty timedTransitionProperty) { - this.property = timedTransitionProperty; + private TestEntity(TimedTransitionProperty timedTransitionProperty) { + property = timedTransitionProperty; } } } diff --git a/core/src/test/java/google/registry/persistence/converter/TldStateTransitionConverterTest.java b/core/src/test/java/google/registry/persistence/converter/TldStateTransitionConverterTest.java index f7ee70329..a4e746cf1 100644 --- a/core/src/test/java/google/registry/persistence/converter/TldStateTransitionConverterTest.java +++ b/core/src/test/java/google/registry/persistence/converter/TldStateTransitionConverterTest.java @@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSortedMap; import google.registry.model.ImmutableObject; import google.registry.model.common.TimedTransitionProperty; import google.registry.model.tld.Registry.TldState; -import google.registry.model.tld.Registry.TldStateTransition; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; import javax.persistence.Entity; @@ -52,8 +51,8 @@ class TldStateTransitionConverterTest { @Test void roundTripConversion_returnsSameTimedTransitionProperty() { - TimedTransitionProperty timedTransitionProperty = - TimedTransitionProperty.fromValueMap(values, TldStateTransition.class); + TimedTransitionProperty timedTransitionProperty = + TimedTransitionProperty.fromValueMap(values); TestEntity testEntity = new TestEntity(timedTransitionProperty); insertInDb(testEntity); TestEntity persisted = @@ -66,12 +65,11 @@ class TldStateTransitionConverterTest { @Id String name = "id"; - TimedTransitionProperty timedTransitionProperty; + TimedTransitionProperty timedTransitionProperty; private TestEntity() {} - private TestEntity( - TimedTransitionProperty timedTransitionProperty) { + private TestEntity(TimedTransitionProperty timedTransitionProperty) { this.timedTransitionProperty = timedTransitionProperty; } } diff --git a/core/src/test/resources/google/registry/model/schema.txt b/core/src/test/resources/google/registry/model/schema.txt index 127d1281d..cf95b593f 100644 --- a/core/src/test/resources/google/registry/model/schema.txt +++ b/core/src/test/resources/google/registry/model/schema.txt @@ -337,7 +337,7 @@ class google.registry.model.domain.token.AllocationToken { google.registry.model.CreateAutoTimestamp creationTime; google.registry.model.UpdateAutoTimestamp updateTimestamp; google.registry.model.billing.BillingEvent$RenewalPriceBehavior renewalPriceBehavior; - google.registry.model.common.TimedTransitionProperty tokenStatusTransitions; + google.registry.model.common.TimedTransitionProperty tokenStatusTransitions; google.registry.model.domain.token.AllocationToken$TokenType tokenType; google.registry.persistence.DomainHistoryVKey redemptionHistoryEntry; int discountYears; @@ -351,10 +351,6 @@ enum google.registry.model.domain.token.AllocationToken$TokenStatus { NOT_STARTED; VALID; } -class google.registry.model.domain.token.AllocationToken$TokenStatusTransition { - google.registry.model.domain.token.AllocationToken$TokenStatus tokenStatus; - org.joda.time.DateTime transitionTime; -} enum google.registry.model.domain.token.AllocationToken$TokenType { SINGLE_USE; UNLIMITED_USE; @@ -520,17 +516,13 @@ class google.registry.model.registrar.Registrar { java.lang.String url; java.lang.String whoisServer; java.util.List ipAddressWhitelist; - java.util.Map billingAccountMap; + java.util.Map billingAccountMap; java.util.Set allowedTlds; java.util.Set rdapBaseUrls; org.joda.time.DateTime lastCertificateUpdateTime; org.joda.time.DateTime lastExpiringCertNotificationSentDate; org.joda.time.DateTime lastExpiringFailoverCertNotificationSentDate; } -class google.registry.model.registrar.Registrar$BillingAccountEntry { - java.lang.String accountId; - org.joda.money.CurrencyUnit currency; -} enum google.registry.model.registrar.Registrar$State { ACTIVE; DISABLED;