mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 03:57:51 +02:00
Remove support for @Mapify (#1691)
We no longer need to support Objectify's @Mapify logic. This substantially simplified how we store maps in the database.
This commit is contained in:
parent
1536dcb078
commit
ffe5a201b3
22 changed files with 240 additions and 659 deletions
|
@ -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<MigrationState> {
|
||||
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<DatabaseMigrationStateSchedule>,
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition>>
|
||||
Class<DatabaseMigrationStateSchedule>, TimedTransitionProperty<MigrationState>>
|
||||
// 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<MigrationState, MigrationStateTransition>
|
||||
DEFAULT_TRANSITION_MAP =
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY),
|
||||
MigrationStateTransition.class);
|
||||
public static final TimedTransitionProperty<MigrationState> DEFAULT_TRANSITION_MAP =
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY));
|
||||
|
||||
@VisibleForTesting
|
||||
public TimedTransitionProperty<MigrationState, MigrationStateTransition> migrationTransitions =
|
||||
TimedTransitionProperty.forMapify(
|
||||
MigrationState.DATASTORE_ONLY, MigrationStateTransition.class);
|
||||
public TimedTransitionProperty<MigrationState> migrationTransitions =
|
||||
TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY);
|
||||
|
||||
// Required for Objectify initialization
|
||||
private DatabaseMigrationStateSchedule() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public DatabaseMigrationStateSchedule(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> migrationTransitions) {
|
||||
TimedTransitionProperty<MigrationState> migrationTransitions) {
|
||||
this.migrationTransitions = migrationTransitions;
|
||||
}
|
||||
|
||||
/** Sets and persists to SQL the provided migration transition schedule. */
|
||||
public static void set(ImmutableSortedMap<DateTime, MigrationState> migrationTransitionMap) {
|
||||
jpaTm().assertInTransaction();
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> transitions =
|
||||
TimedTransitionProperty<MigrationState> 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<MigrationState, MigrationStateTransition> get() {
|
||||
public static TimedTransitionProperty<MigrationState> 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<MigrationState, MigrationStateTransition> getUncached() {
|
||||
static TimedTransitionProperty<MigrationState> 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<MigrationState, MigrationStateTransition> newTransitions) {
|
||||
TimedTransitionProperty<MigrationState> newTransitions) {
|
||||
MigrationState currentValue = getUncached().getValueAtTime(jpaTm().getTransactionTime());
|
||||
MigrationState nextCurrentValue = newTransitions.getValueAtTime(jpaTm().getTransactionTime());
|
||||
checkArgument(
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>The type parameter {@code T} specifies a user-defined subclass of {@code TimedTransition<V>}
|
||||
* 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.
|
||||
* <p>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<V>>
|
||||
extends ForwardingMap<DateTime, T> implements UnsafeSerializable {
|
||||
public class TimedTransitionProperty<V extends Serializable> extends ForwardingMap<DateTime, V>
|
||||
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.
|
||||
*
|
||||
* <p>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<V extends Serializable> 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<DateTime, TimedTransition<?>> {
|
||||
@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<T> parameter could be eliminated by getting the class via reflection, but then
|
||||
// the callsite cannot infer T, so unless you explicitly call this as .<V, T>fromValueMap() it
|
||||
// will default to using just TimedTransition<V>, which fails at runtime.
|
||||
private static <V extends Serializable, T extends TimedTransition<V>>
|
||||
NavigableMap<DateTime, T> makeTransitionMap(
|
||||
ImmutableSortedMap<DateTime, V> valueMap, final Class<T> timedTransitionSubclass) {
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> fromValueMap(
|
||||
ImmutableSortedMap<DateTime, V> 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<V>) 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.
|
||||
*
|
||||
* <p>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 <V extends Serializable, T extends TimedTransition<V>>
|
||||
TimedTransitionProperty<V, T> fromValueMap(
|
||||
ImmutableSortedMap<DateTime, V> valueMap, final Class<T> timedTransitionSubclass) {
|
||||
return new TimedTransitionProperty<>(ImmutableSortedMap.copyOf(
|
||||
makeTransitionMap(valueMap, timedTransitionSubclass)));
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> 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<V, T> 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<V, T> copyWithAddedTransition(
|
||||
DateTime transitionTime,
|
||||
V transitionValue,
|
||||
Class<T> transitionClass,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String allowedTransitionMapName) {
|
||||
ImmutableSortedMap<DateTime, V> currentMap = toValueMap();
|
||||
checkArgument(
|
||||
transitionTime.isAfter(currentMap.lastKey()),
|
||||
"New transitions can only be appended after the last previous transition.");
|
||||
Map<DateTime, V> newInnerMap = new HashMap<>(currentMap);
|
||||
newInnerMap.put(transitionTime, transitionValue);
|
||||
ImmutableSortedMap<DateTime, V> 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 <V extends Serializable, T extends TimedTransitionProperty.TimedTransition<V>>
|
||||
TimedTransitionProperty<V, T> make(
|
||||
ImmutableSortedMap<DateTime, V> newTransitions,
|
||||
Class<T> transitionClass,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String allowedTransitionMapName,
|
||||
V initialValue,
|
||||
String badInitialValueErrorMessage) {
|
||||
validateTimedTransitionMap(
|
||||
newTransitions,
|
||||
allowedTransitions,
|
||||
allowedTransitionMapName);
|
||||
public static <V extends Serializable> TimedTransitionProperty<V> make(
|
||||
ImmutableSortedMap<DateTime, V> newTransitions,
|
||||
ImmutableMultimap<V, V> 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 <V extends Serializable, T extends TimedTransitionProperty.TimedTransition<V>>
|
||||
void validateTimedTransitionMap(
|
||||
@Nullable NavigableMap<DateTime, V> transitionMap,
|
||||
ImmutableMultimap<V, V> allowedTransitions,
|
||||
String mapName) {
|
||||
public static <V extends Serializable> void validateTimedTransitionMap(
|
||||
@Nullable NavigableMap<DateTime, V> transitionMap,
|
||||
ImmutableMultimap<V, V> 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.
|
||||
*
|
||||
* <p>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 <V extends Serializable, T extends TimedTransition<V>>
|
||||
TimedTransitionProperty<V, T> forMapify(
|
||||
ImmutableSortedMap<DateTime, V> valueMap, Class<T> 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<DateTime, V> backingMap;
|
||||
|
||||
/**
|
||||
* Returns a new mutable {@code TimedTransitionProperty} representing the given value being set at
|
||||
* start of time, constructed using the given {@code TimedTransition} subclass.
|
||||
*
|
||||
* <p>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 <V extends Serializable, T extends TimedTransition<V>>
|
||||
TimedTransitionProperty<V, T> forMapify(
|
||||
V valueAtStartOfTime, Class<T> 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<DateTime, T> backingMap;
|
||||
|
||||
/** Returns a new {@code TimedTransitionProperty} backed by the provided map instance. */
|
||||
private TimedTransitionProperty(NavigableMap<DateTime, T> backingMap) {
|
||||
checkArgument(backingMap.get(START_OF_TIME) != null,
|
||||
/** Returns a new {@link TimedTransitionProperty} backed by the provided map instance. */
|
||||
private TimedTransitionProperty(NavigableMap<DateTime, V> 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<DateTime, T> delegate() {
|
||||
protected ImmutableSortedMap<DateTime, V> 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<DateTime, V> 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));
|
||||
|
|
|
@ -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<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS =
|
||||
ImmutableMultimap.<TokenStatus, TokenStatus>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 {
|
|||
* <p>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<TokenStatus, TokenStatusTransition> tokenStatusTransitions =
|
||||
TimedTransitionProperty.forMapify(NOT_STARTED, TokenStatusTransition.class);
|
||||
|
||||
/**
|
||||
* A transition to a given token status at a specific time, for use in a TimedTransitionProperty.
|
||||
*
|
||||
* <p>Public because App Engine's security manager requires this for instantiation via reflection.
|
||||
*/
|
||||
@Embed
|
||||
public static class TokenStatusTransition extends TimedTransition<TokenStatus> {
|
||||
private TokenStatus tokenStatus;
|
||||
|
||||
@Override
|
||||
public TokenStatus getValue() {
|
||||
return tokenStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValue(TokenStatus tokenStatus) {
|
||||
this.tokenStatus = tokenStatus;
|
||||
}
|
||||
}
|
||||
TimedTransitionProperty<TokenStatus> 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<TokenStatus, TokenStatusTransition> getTokenStatusTransitions() {
|
||||
public TimedTransitionProperty<TokenStatus> 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,
|
||||
|
|
|
@ -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<CurrencyUnit, BillingAccountEntry> 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<CurrencyUnit, String> entry) {
|
||||
this.accountId = entry.getValue();
|
||||
this.currency = entry.getKey();
|
||||
}
|
||||
|
||||
/** Mapper to use for {@code @Mapify}. */
|
||||
static class CurrencyMapper implements Mapper<CurrencyUnit, BillingAccountEntry> {
|
||||
@Override
|
||||
public CurrencyUnit getKey(BillingAccountEntry billingAccountEntry) {
|
||||
return billingAccountEntry.currency;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the account id of this entry. */
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
}
|
||||
@Nullable Map<CurrencyUnit, String> billingAccountMap;
|
||||
|
||||
/** URL of registrar's website. */
|
||||
String url;
|
||||
|
@ -493,12 +452,10 @@ public class Registrar extends ImmutableObject
|
|||
return Optional.ofNullable(poNumber);
|
||||
}
|
||||
|
||||
public ImmutableMap<CurrencyUnit, String> getBillingAccountMap() {
|
||||
if (billingAccountMap == null) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
return billingAccountMap.entrySet().stream()
|
||||
.collect(toImmutableSortedMap(natural(), Map.Entry::getKey, v -> v.getValue().accountId));
|
||||
public ImmutableSortedMap<CurrencyUnit, String> 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<CurrencyUnit, String> billingAccountMap) {
|
||||
getInstance().billingAccountMap =
|
||||
nullToEmptyImmutableCopy(billingAccountMap).entrySet().stream()
|
||||
.collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
|
||||
getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TldState> {
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* <p>Public because App Engine's security manager requires this for instantiation via reflection.
|
||||
*/
|
||||
public static class BillingCostTransition extends TimedTransition<Money> {
|
||||
|
||||
/** 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<TldState, TldStateTransition> tldStateTransitions =
|
||||
TimedTransitionProperty.forMapify(DEFAULT_TLD_STATE, TldStateTransition.class);
|
||||
TimedTransitionProperty<TldState> 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.
|
||||
*
|
||||
* <p>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<Money, BillingCostTransition> renewBillingCostTransitions =
|
||||
TimedTransitionProperty.forMapify(DEFAULT_RENEW_BILLING_COST, BillingCostTransition.class);
|
||||
TimedTransitionProperty<Money> renewBillingCostTransitions =
|
||||
TimedTransitionProperty.withInitialValue(DEFAULT_RENEW_BILLING_COST);
|
||||
|
||||
/** A property that tracks the EAP fee schedule (if any) for the TLD. */
|
||||
@Column(nullable = false)
|
||||
TimedTransitionProperty<Money, BillingCostTransition> eapFeeSchedule =
|
||||
TimedTransitionProperty.forMapify(DEFAULT_EAP_BILLING_COST, BillingCostTransition.class);
|
||||
TimedTransitionProperty<Money> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TokenStatus>} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class AllocationTokenStatusTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<TokenStatus, TokenStatusTransition> {
|
||||
extends TimedTransitionPropertyConverterBase<TokenStatus> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, TokenStatusTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
|
||||
protected String convertValueToString(TokenStatus value) {
|
||||
return value.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, TokenStatus> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(
|
||||
DateTime.parse(entry.getKey()), TokenStatus.valueOf(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<TokenStatusTransition> getTimedTransitionSubclass() {
|
||||
return TokenStatusTransition.class;
|
||||
protected TokenStatus convertStringToValue(String string) {
|
||||
return TokenStatus.valueOf(string);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Money,BillingCostTransition>}
|
||||
* objects.
|
||||
*/
|
||||
/** JPA converter for storing/retrieving {@code TimedTransitionProperty<Money>} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class BillingCostTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<Money, BillingCostTransition> {
|
||||
|
||||
public class BillingCostTransitionConverter extends TimedTransitionPropertyConverterBase<Money> {
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, BillingCostTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().toString());
|
||||
protected String convertValueToString(Money value) {
|
||||
return value.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;
|
||||
protected Money convertStringToValue(String string) {
|
||||
return Money.parse(string);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CurrencyUnit, BillingAccountEntry>} objects. */
|
||||
/** JPA converter for storing/retrieving {@code Map<CurrencyUnit, String>} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class CurrencyToBillingConverter
|
||||
extends StringMapConverterBase<CurrencyUnit, BillingAccountEntry> {
|
||||
extends StringMapConverterBase<CurrencyUnit, String, Map<CurrencyUnit, String>> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<CurrencyUnit, BillingAccountEntry> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().getCode(), entry.getValue().getAccountId());
|
||||
protected String convertKeyToString(CurrencyUnit key) {
|
||||
return key.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<CurrencyUnit, BillingAccountEntry> convertToEntityMapEntry(
|
||||
Map.Entry<String, String> 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<CurrencyUnit, String> convertMapToDerivedType(Map<CurrencyUnit, String> map) {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MigrationState, MigrationStateTransition> {
|
||||
extends TimedTransitionPropertyConverterBase<MigrationState> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, MigrationStateTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
|
||||
protected String convertValueToString(MigrationState value) {
|
||||
return value.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, MigrationState> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(
|
||||
DateTime.parse(entry.getKey()), MigrationState.valueOf(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<MigrationStateTransition> getTimedTransitionSubclass() {
|
||||
return MigrationStateTransition.class;
|
||||
protected MigrationState convertStringToValue(String string) {
|
||||
return MigrationState.valueOf(string);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<K, V>
|
||||
implements AttributeConverter<Map<K, V>, StringMap> {
|
||||
public abstract class StringMapConverterBase<K, V, M extends Map<K, V>>
|
||||
implements AttributeConverter<M, StringMap> {
|
||||
|
||||
abstract Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<K, V> entry);
|
||||
protected abstract String convertKeyToString(K key);
|
||||
|
||||
abstract Map.Entry<K, V> convertToEntityMapEntry(Map.Entry<String, String> entry);
|
||||
protected abstract String convertValueToString(V value);
|
||||
|
||||
protected abstract K convertStringToKey(String string);
|
||||
|
||||
protected abstract V convertStringToValue(String string);
|
||||
|
||||
protected abstract M convertMapToDerivedType(Map<K, V> map);
|
||||
|
||||
private Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<K, V> entry) {
|
||||
return Maps.immutableEntry(
|
||||
convertKeyToString(entry.getKey()), convertValueToString(entry.getValue()));
|
||||
}
|
||||
|
||||
private Map.Entry<K, V> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(
|
||||
convertStringToKey(entry.getKey()), convertStringToValue(entry.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringMap convertToDatabaseColumn(Map<K, V> attribute) {
|
||||
public StringMap convertToDatabaseColumn(M attribute) {
|
||||
return attribute == null
|
||||
? null
|
||||
: StringMap.create(
|
||||
|
@ -42,11 +59,12 @@ public abstract class StringMapConverterBase<K, V>
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> 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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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();
|
||||
public abstract class TimedTransitionPropertyConverterBase<V extends Serializable>
|
||||
extends StringMapConverterBase<DateTime, V, TimedTransitionProperty<V>> {
|
||||
|
||||
@Override
|
||||
public StringMap convertToDatabaseColumn(@Nullable TimedTransitionProperty<K, V> 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<K, V> convertToEntityAttribute(@Nullable StringMap dbData) {
|
||||
if (dbData == null) {
|
||||
return null;
|
||||
}
|
||||
ImmutableMap<DateTime, K> 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<V> convertMapToDerivedType(Map<DateTime, V> map) {
|
||||
return TimedTransitionProperty.fromValueMap(ImmutableSortedMap.copyOf(map));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TldState,
|
||||
* TldStateTransition>} objects.
|
||||
*/
|
||||
/** JPA converter for storing/retrieving {@code TimedTransitionProperty<TldState>} objects. */
|
||||
@Converter(autoApply = true)
|
||||
public class TldStateTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<TldState, TldStateTransition> {
|
||||
|
||||
public class TldStateTransitionConverter extends TimedTransitionPropertyConverterBase<TldState> {
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(
|
||||
Map.Entry<DateTime, TldStateTransition> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
|
||||
protected String convertValueToString(TldState value) {
|
||||
return value.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;
|
||||
protected TldState convertStringToValue(String string) {
|
||||
return TldState.valueOf(string);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MigrationState, MigrationStateTransition> migrationSchedule =
|
||||
TimedTransitionProperty<MigrationState> migrationSchedule =
|
||||
DatabaseMigrationStateSchedule.get();
|
||||
System.out.println(
|
||||
String.format("Current migration schedule: %s", migrationSchedule.toValueMap()));
|
||||
System.out.printf("Current migration schedule: %s%n", migrationSchedule.toValueMap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> {
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ImmutableSortedMap<DateTime, String> values = ImmutableSortedMap.of(
|
||||
START_OF_TIME, "0",
|
||||
DATE_1, "1",
|
||||
DATE_2, "2",
|
||||
DATE_3, "3");
|
||||
|
||||
private TimedTransitionProperty<String, StringTimedTransition> timedString;
|
||||
private TimedTransitionProperty<String> 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<String, StringTimedTransition> timedString) {
|
||||
private static void testGetValueAtTime(TimedTransitionProperty<String> 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<Map.Entry<DateTime, StringTimedTransition>> 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<DateTime, StringTimedTransition> transition : transitions) {
|
||||
timedString.put(transition.getKey(), transition.getValue());
|
||||
}
|
||||
timedString.checkValidity();
|
||||
testGetValueAtTime(timedString);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_valueMapNotChronologicallyOrdered() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.<DateTime, String>reverseOrder().put(START_OF_TIME, "0").build(),
|
||||
StringTimedTransition.class));
|
||||
ImmutableSortedMap.<DateTime, String>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")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TokenStatus, TokenStatusTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, TokenStatusTransition.class);
|
||||
TimedTransitionProperty<TokenStatus> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
AllocationTokenStatusTransitionConverterTestEntity testEntity =
|
||||
new AllocationTokenStatusTransitionConverterTestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
|
@ -75,12 +74,12 @@ public class AllocationTokenStatusTransitionConverterTest {
|
|||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<TokenStatus, TokenStatusTransition> timedTransitionProperty;
|
||||
TimedTransitionProperty<TokenStatus> timedTransitionProperty;
|
||||
|
||||
private AllocationTokenStatusTransitionConverterTestEntity() {}
|
||||
|
||||
private AllocationTokenStatusTransitionConverterTestEntity(
|
||||
TimedTransitionProperty<TokenStatus, TokenStatusTransition> timedTransitionProperty) {
|
||||
TimedTransitionProperty<TokenStatus> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Money, BillingCostTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, BillingCostTransition.class);
|
||||
TimedTransitionProperty<Money> 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<Money, BillingCostTransition> timedTransitionProperty;
|
||||
TimedTransitionProperty<Money> timedTransitionProperty;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(
|
||||
TimedTransitionProperty<Money, BillingCostTransition> timedTransitionProperty) {
|
||||
private TestEntity(TimedTransitionProperty<Money> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CurrencyUnit, BillingAccountEntry> currencyToBilling =
|
||||
ImmutableMap<CurrencyUnit, String> 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<CurrencyUnit, BillingAccountEntry> currencyToBilling;
|
||||
Map<CurrencyUnit, String> currencyToBilling;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(Map<CurrencyUnit, BillingAccountEntry> currencyToBilling) {
|
||||
private TestEntity(Map<CurrencyUnit, String> currencyToBilling) {
|
||||
this.currencyToBilling = currencyToBilling;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MigrationState, MigrationStateTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, MigrationStateTransition.class);
|
||||
TimedTransitionProperty<MigrationState> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
DatabaseMigrationScheduleTransitionConverterTestEntity testEntity =
|
||||
new DatabaseMigrationScheduleTransitionConverterTestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
|
@ -79,12 +78,12 @@ public class DatabaseMigrationScheduleTransitionConverterTest {
|
|||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> timedTransitionProperty;
|
||||
TimedTransitionProperty<MigrationState> timedTransitionProperty;
|
||||
|
||||
private DatabaseMigrationScheduleTransitionConverterTestEntity() {}
|
||||
|
||||
private DatabaseMigrationScheduleTransitionConverterTestEntity(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> timedTransitionProperty) {
|
||||
TimedTransitionProperty<MigrationState> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Key, Value> {
|
||||
private static class TestStringMapConverter
|
||||
extends StringMapConverterBase<Key, Value, Map<Key, Value>> {
|
||||
|
||||
@Override
|
||||
Map.Entry<String, String> convertToDatabaseMapEntry(Map.Entry<Key, Value> entry) {
|
||||
return Maps.immutableEntry(entry.getKey().key, entry.getValue().value);
|
||||
protected String convertKeyToString(Key key) {
|
||||
return key.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
Map.Entry<Key, Value> convertToEntityMapEntry(Map.Entry<String, String> 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<Key, Value> convertMapToDerivedType(Map<Key, Value> map) {
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String, TestTransition> TIMED_TRANSITION_PROPERTY =
|
||||
TimedTransitionProperty.fromValueMap(VALUES, TestTransition.class);
|
||||
private static final TimedTransitionProperty<String> 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<DateTime, String> 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<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> {
|
||||
extends TimedTransitionPropertyConverterBase<String> {
|
||||
|
||||
@Override
|
||||
Map.Entry<DateTime, String> convertToEntityMapEntry(Map.Entry<String, String> entry) {
|
||||
return Maps.immutableEntry(DateTime.parse(entry.getKey()), entry.getValue());
|
||||
protected String convertValueToString(String value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@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());
|
||||
protected String convertStringToValue(String string) {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,12 +148,12 @@ class TimedTransitionPropertyConverterBaseTest {
|
|||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<String, TestTransition> property;
|
||||
TimedTransitionProperty<String> property;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(TimedTransitionProperty<String, TestTransition> timedTransitionProperty) {
|
||||
this.property = timedTransitionProperty;
|
||||
private TestEntity(TimedTransitionProperty<String> timedTransitionProperty) {
|
||||
property = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TldState, TldStateTransition> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values, TldStateTransition.class);
|
||||
TimedTransitionProperty<TldState> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
TestEntity testEntity = new TestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
TestEntity persisted =
|
||||
|
@ -66,12 +65,11 @@ class TldStateTransitionConverterTest {
|
|||
|
||||
@Id String name = "id";
|
||||
|
||||
TimedTransitionProperty<TldState, TldStateTransition> timedTransitionProperty;
|
||||
TimedTransitionProperty<TldState> timedTransitionProperty;
|
||||
|
||||
private TestEntity() {}
|
||||
|
||||
private TestEntity(
|
||||
TimedTransitionProperty<TldState, TldStateTransition> timedTransitionProperty) {
|
||||
private TestEntity(TimedTransitionProperty<TldState> timedTransitionProperty) {
|
||||
this.timedTransitionProperty = timedTransitionProperty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<google.registry.model.domain.token.AllocationToken$TokenStatus, google.registry.model.domain.token.AllocationToken$TokenStatusTransition> tokenStatusTransitions;
|
||||
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> 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<google.registry.util.CidrAddressBlock> ipAddressWhitelist;
|
||||
java.util.Map<org.joda.money.CurrencyUnit, google.registry.model.registrar.Registrar$BillingAccountEntry> billingAccountMap;
|
||||
java.util.Map<org.joda.money.CurrencyUnit, java.lang.String> billingAccountMap;
|
||||
java.util.Set<java.lang.String> allowedTlds;
|
||||
java.util.Set<java.lang.String> 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue