Remove ofy support from auto timestamp classes (#1708)

Also remove the use of ZonedDateTime, as joda DateTime can already be persisted to SQL with an existing converted.
This commit is contained in:
Lai Jiang 2022-07-26 09:51:35 -04:00 committed by GitHub
parent e0a0030837
commit badfb29ce1
22 changed files with 100 additions and 512 deletions

View file

@ -19,15 +19,13 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import java.time.ZoneId; import java.sql.Date;
import java.time.ZonedDateTime;
import java.util.TimeZone;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
/** Utilities methods and constants related to Joda {@link DateTime} objects. */ /** Utilities methods and constants related to Joda {@link DateTime} objects. */
public class DateTimeUtils { public abstract class DateTimeUtils {
/** The start of the epoch, in a convenient constant. */ /** The start of the epoch, in a convenient constant. */
public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC); public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC);
@ -91,39 +89,11 @@ public class DateTimeUtils {
return years == 0 ? now : now.minusYears(1).minusYears(years - 1); return years == 0 ? now : now.minusYears(1).minusYears(years - 1);
} }
/** public static Date toSqlDate(LocalDate localDate) {
* Converts a Joda {@link DateTime} object to an equivalent java.time {@link ZonedDateTime} return new Date(localDate.toDateTimeAtStartOfDay().getMillis());
* object.
*/
public static ZonedDateTime toZonedDateTime(DateTime dateTime) {
java.time.Instant instant = java.time.Instant.ofEpochMilli(dateTime.getMillis());
return ZonedDateTime.ofInstant(instant, ZoneId.of(dateTime.getZone().getID()).normalized());
} }
/** public static LocalDate toLocalDate(Date date) {
* Converts a Joda {@link DateTime} object to an equivalent java.time {@link ZonedDateTime}
* object.
*/
public static ZonedDateTime toZonedDateTime(DateTime dateTime, ZoneId zoneId) {
java.time.Instant instant = java.time.Instant.ofEpochMilli(dateTime.getMillis());
return ZonedDateTime.ofInstant(instant, zoneId);
}
/**
* Converts a java.time {@link ZonedDateTime} object to an equivalent Joda {@link DateTime}
* object.
*/
public static DateTime toJodaDateTime(ZonedDateTime zonedDateTime) {
return new DateTime(
zonedDateTime.toInstant().toEpochMilli(),
DateTimeZone.forTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone())));
}
public static java.sql.Date toSqlDate(LocalDate localDate) {
return new java.sql.Date(localDate.toDateTimeAtStartOfDay().getMillis());
}
public static LocalDate toLocalDate(java.sql.Date date) {
return new LocalDate(date.getTime(), DateTimeZone.UTC); return new LocalDate(date.getTime(), DateTimeZone.UTC);
} }
} }

View file

@ -28,16 +28,16 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.avro.generic.GenericRecord; import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.coders.AtomicCoder; import org.apache.beam.sdk.coders.AtomicCoder;
import org.apache.beam.sdk.coders.Coder; import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.sdk.coders.StringUtf8Coder; import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord; import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/** /**
* A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}. * A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}.
@ -51,7 +51,7 @@ import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
public abstract class BillingEvent implements Serializable { public abstract class BillingEvent implements Serializable {
private static final DateTimeFormatter DATE_TIME_FORMATTER = private static final DateTimeFormatter DATE_TIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz"); DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz");
/** The amount we multiply the price for sunrise creates. This is currently a 15% discount. */ /** The amount we multiply the price for sunrise creates. This is currently a 15% discount. */
private static final double SUNRISE_DISCOUNT_PRICE_MODIFIER = 0.85; private static final double SUNRISE_DISCOUNT_PRICE_MODIFIER = 0.85;
@ -77,10 +77,10 @@ public abstract class BillingEvent implements Serializable {
abstract long id(); abstract long id();
/** Returns the UTC DateTime this event becomes billable. */ /** Returns the UTC DateTime this event becomes billable. */
abstract ZonedDateTime billingTime(); abstract DateTime billingTime();
/** Returns the UTC DateTime this event was generated. */ /** Returns the UTC DateTime this event was generated. */
abstract ZonedDateTime eventTime(); abstract DateTime eventTime();
/** Returns the billed registrar's name. */ /** Returns the billed registrar's name. */
abstract String registrarId(); abstract String registrarId();
@ -132,10 +132,8 @@ public abstract class BillingEvent implements Serializable {
// Objects, which contain a string representation of their underlying types. // Objects, which contain a string representation of their underlying types.
Long.parseLong(extractField(record, "id")), Long.parseLong(extractField(record, "id")),
// Bigquery provides UNIX timestamps with microsecond precision. // Bigquery provides UNIX timestamps with microsecond precision.
Instant.ofEpochMilli(Long.parseLong(extractField(record, "billingTime")) / 1000) new DateTime(Long.parseLong(extractField(record, "billingTime")) / 1000, DateTimeZone.UTC),
.atZone(ZoneId.of("UTC")), new DateTime(Long.parseLong(extractField(record, "eventTime")) / 1000, DateTimeZone.UTC),
Instant.ofEpochMilli(Long.parseLong(extractField(record, "eventTime")) / 1000)
.atZone(ZoneId.of("UTC")),
extractField(record, "registrarId"), extractField(record, "registrarId"),
extractField(record, "billingId"), extractField(record, "billingId"),
extractField(record, "poNumber"), extractField(record, "poNumber"),
@ -180,8 +178,8 @@ public abstract class BillingEvent implements Serializable {
@VisibleForTesting @VisibleForTesting
static BillingEvent create( static BillingEvent create(
long id, long id,
ZonedDateTime billingTime, DateTime billingTime,
ZonedDateTime eventTime, DateTime eventTime,
String registrarId, String registrarId,
String billingId, String billingId,
String poNumber, String poNumber,
@ -231,8 +229,8 @@ public abstract class BillingEvent implements Serializable {
.join( .join(
ImmutableList.of( ImmutableList.of(
id(), id(),
DATE_TIME_FORMATTER.format(billingTime()), DATE_TIME_FORMATTER.print(billingTime()),
DATE_TIME_FORMATTER.format(eventTime()), DATE_TIME_FORMATTER.print(eventTime()),
registrarId(), registrarId(),
billingId(), billingId(),
poNumber(), poNumber(),

View file

@ -27,14 +27,12 @@ import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel; import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.reporting.billing.BillingModule; import google.registry.reporting.billing.BillingModule;
import google.registry.util.DateTimeUtils;
import google.registry.util.DomainNameUtils; import google.registry.util.DomainNameUtils;
import google.registry.util.SqlTemplate; import google.registry.util.SqlTemplate;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.YearMonth; import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -123,8 +121,8 @@ public class InvoicingPipeline implements Serializable {
return Optional.of( return Optional.of(
BillingEvent.create( BillingEvent.create(
oneTime.getId(), oneTime.getId(),
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")), oneTime.getBillingTime(),
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")), oneTime.getEventTime(),
registrar.getRegistrarId(), registrar.getRegistrarId(),
registrar.getBillingAccountMap().get(currency), registrar.getBillingAccountMap().get(currency),
registrar.getPoNumber().orElse(""), registrar.getPoNumber().orElse(""),

View file

@ -14,6 +14,7 @@
package google.registry.model; package google.registry.model;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.util.PreconditionsUtils; import google.registry.util.PreconditionsUtils;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
@ -44,6 +45,7 @@ public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeS
// require an unnecessary non-private setter method. // require an unnecessary non-private setter method.
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
@AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp")) @AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp"))
@Ignore
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null); UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null);
/** Get the {@link UpdateAutoTimestamp} for this entity. */ /** Get the {@link UpdateAutoTimestamp} for this entity. */

View file

@ -10,73 +10,43 @@
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the Licenseschema..
package google.registry.model; package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist; import javax.persistence.PrePersist;
import javax.persistence.PreUpdate; import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /** A timestamp that auto-updates when first saved to Datastore. */
* A timestamp that auto-updates when first saved to Datastore.
*
* @see CreateAutoTimestampTranslatorFactory
*/
@Embeddable @Embeddable
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable { public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@Transient DateTime timestamp;
@Column(nullable = false) @Column(nullable = false)
@Ignore DateTime creationTime;
ZonedDateTime creationTime;
@PrePersist @PrePersist
@PreUpdate @PreUpdate
void setTimestamp() { void setTimestamp() {
if (creationTime == null) { if (creationTime == null) {
timestamp = jpaTm().getTransactionTime(); creationTime = jpaTm().getTransactionTime();
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (creationTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(creationTime);
} }
} }
/** Returns the timestamp. */ /** Returns the timestamp. */
@Nullable @Nullable
public DateTime getTimestamp() { public DateTime getTimestamp() {
return timestamp; return creationTime;
} }
public static CreateAutoTimestamp create(@Nullable DateTime timestamp) { public static CreateAutoTimestamp create(@Nullable DateTime creationTime) {
CreateAutoTimestamp instance = new CreateAutoTimestamp(); CreateAutoTimestamp instance = new CreateAutoTimestamp();
instance.timestamp = timestamp; instance.creationTime = creationTime;
instance.creationTime = (timestamp == null) ? null : DateTimeUtils.toZonedDateTime(timestamp);
return instance; return instance;
} }
} }

View file

@ -32,6 +32,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig; import google.registry.config.RegistryConfig;
import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader; import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
@ -111,7 +112,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
* resource fields. * resource fields.
*/ */
@AttributeOverrides(@AttributeOverride(name = "creationTime", column = @Column)) @AttributeOverrides(@AttributeOverride(name = "creationTime", column = @Column))
@Index @Ignore
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** /**

View file

@ -59,8 +59,7 @@ public final class SchemaVersion {
* types (for classes), or else a list of all possible values (for enums). * types (for classes), or else a list of all possible values (for enums).
*/ */
public static String getSchema() { public static String getSchema() {
return getAllPersistedTypes() return getAllPersistedTypes().stream()
.stream()
.filter(or(subtypeOf(Enum.class), subtypeOf(ImmutableObject.class))) .filter(or(subtypeOf(Enum.class), subtypeOf(ImmutableObject.class)))
.map(ModelUtils::getSchema) .map(ModelUtils::getSchema)
.collect(joining("\n")); .collect(joining("\n"));

View file

@ -17,23 +17,15 @@ package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist; import javax.persistence.PrePersist;
import javax.persistence.PreUpdate; import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /** A timestamp that auto-updates on each save to Datastore/Cloud SQL. */
* A timestamp that auto-updates on each save to Datastore/Cloud SQL.
*/
@Embeddable @Embeddable
public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable { public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@ -42,11 +34,8 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
// during a replay). // during a replay).
private static final ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true); private static final ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true);
@Transient DateTime timestamp;
@Ignore
@Column(nullable = false) @Column(nullable = false)
ZonedDateTime lastUpdateTime; DateTime lastUpdateTime;
// Unfortunately, we cannot use the @UpdateTimestamp annotation on "lastUpdateTime" in this class // Unfortunately, we cannot use the @UpdateTimestamp annotation on "lastUpdateTime" in this class
// because Hibernate does not allow it to be used on @Embeddable classes, see // because Hibernate does not allow it to be used on @Embeddable classes, see
@ -55,34 +44,18 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
@PreUpdate @PreUpdate
void setTimestamp() { void setTimestamp() {
if (autoUpdateEnabled() || lastUpdateTime == null) { if (autoUpdateEnabled() || lastUpdateTime == null) {
timestamp = jpaTm().getTransactionTime(); lastUpdateTime = jpaTm().getTransactionTime();
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (lastUpdateTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(lastUpdateTime);
} }
} }
/** Returns the timestamp, or {@code START_OF_TIME} if it's null. */ /** Returns the timestamp, or {@code START_OF_TIME} if it's null. */
public DateTime getTimestamp() { public DateTime getTimestamp() {
return Optional.ofNullable(timestamp).orElse(START_OF_TIME); return Optional.ofNullable(lastUpdateTime).orElse(START_OF_TIME);
} }
public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) { public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) {
UpdateAutoTimestamp instance = new UpdateAutoTimestamp(); UpdateAutoTimestamp instance = new UpdateAutoTimestamp();
instance.timestamp = timestamp; instance.lastUpdateTime = timestamp;
instance.lastUpdateTime = timestamp == null ? null : DateTimeUtils.toZonedDateTime(timestamp);
return instance; return instance;
} }

View file

@ -16,15 +16,12 @@ package google.registry.model.domain;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable; import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp; import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.UpdateAutoTimestamp; import google.registry.model.UpdateAutoTimestamp;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverride;
@ -109,19 +106,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null); private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
/** When the unlock is first requested. */ /** When the unlock is first requested. */
private ZonedDateTime unlockRequestTime; private DateTime unlockRequestTime;
/** /**
* When the user has verified the lock. If this field is null, it means the lock has not been * When the user has verified the lock. If this field is null, it means the lock has not been
* verified yet (and thus not been put into effect). * verified yet (and thus not been put into effect).
*/ */
private ZonedDateTime lockCompletionTime; private DateTime lockCompletionTime;
/** /**
* When the user has verified the unlock of this lock. If this field is null, it means the unlock * When the user has verified the unlock of this lock. If this field is null, it means the unlock
* action has not been verified yet (and has not been put into effect). * action has not been verified yet (and has not been put into effect).
*/ */
private ZonedDateTime unlockCompletionTime; private DateTime unlockCompletionTime;
/** The user must provide the random verification code in order to complete the action. */ /** The user must provide the random verification code in order to complete the action. */
@Column(nullable = false) @Column(nullable = false)
@ -168,19 +165,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
/** Returns the unlock request timestamp or null if an unlock has not been requested yet. */ /** Returns the unlock request timestamp or null if an unlock has not been requested yet. */
public Optional<DateTime> getUnlockRequestTime() { public Optional<DateTime> getUnlockRequestTime() {
return Optional.ofNullable(unlockRequestTime).map(DateTimeUtils::toJodaDateTime); return Optional.ofNullable(unlockRequestTime);
} }
/** Returns the completion timestamp, or empty if this lock has not been completed yet. */ /** Returns the completion timestamp, or empty if this lock has not been completed yet. */
public Optional<DateTime> getLockCompletionTime() { public Optional<DateTime> getLockCompletionTime() {
return Optional.ofNullable(lockCompletionTime).map(DateTimeUtils::toJodaDateTime); return Optional.ofNullable(lockCompletionTime);
} }
/** /**
* Returns the unlock completion timestamp, or empty if this unlock has not been completed yet. * Returns the unlock completion timestamp, or empty if this unlock has not been completed yet.
*/ */
public Optional<DateTime> getUnlockCompletionTime() { public Optional<DateTime> getUnlockCompletionTime() {
return Optional.ofNullable(unlockCompletionTime).map(DateTimeUtils::toJodaDateTime); return Optional.ofNullable(unlockCompletionTime);
} }
public String getVerificationCode() { public String getVerificationCode() {
@ -278,17 +275,17 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
} }
public Builder setUnlockRequestTime(DateTime unlockRequestTime) { public Builder setUnlockRequestTime(DateTime unlockRequestTime) {
getInstance().unlockRequestTime = toZonedDateTime(unlockRequestTime); getInstance().unlockRequestTime = unlockRequestTime;
return this; return this;
} }
public Builder setLockCompletionTime(DateTime lockCompletionTime) { public Builder setLockCompletionTime(DateTime lockCompletionTime) {
getInstance().lockCompletionTime = toZonedDateTime(lockCompletionTime); getInstance().lockCompletionTime = lockCompletionTime;
return this; return this;
} }
public Builder setUnlockCompletionTime(DateTime unlockCompletionTime) { public Builder setUnlockCompletionTime(DateTime unlockCompletionTime) {
getInstance().unlockCompletionTime = toZonedDateTime(unlockCompletionTime); getInstance().unlockCompletionTime = unlockCompletionTime;
return this; return this;
} }

View file

@ -33,6 +33,7 @@ import com.google.common.collect.Range;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad; import com.googlecode.objectify.annotation.OnLoad;
import google.registry.flows.EppException; import google.registry.flows.EppException;
@ -138,7 +139,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
@Nullable @Index String domainName; @Nullable @Index String domainName;
/** When this token was created. */ /** When this token was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); @Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */ /** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds") @Column(name = "allowedRegistrarIds")

View file

@ -38,7 +38,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration; import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory; import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory; import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory; import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory; import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.EppHistoryVKeyTranslatorFactory; import google.registry.model.translators.EppHistoryVKeyTranslatorFactory;
@ -122,7 +121,6 @@ public class ObjectifyService {
ImmutableList.of( ImmutableList.of(
new BloomFilterOfStringTranslatorFactory(), new BloomFilterOfStringTranslatorFactory(),
new CidrAddressBlockTranslatorFactory(), new CidrAddressBlockTranslatorFactory(),
new CreateAutoTimestampTranslatorFactory(),
new CurrencyUnitTranslatorFactory(), new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(), new DurationTranslatorFactory(),
new EppHistoryVKeyTranslatorFactory(), new EppHistoryVKeyTranslatorFactory(),

View file

@ -58,6 +58,7 @@ import com.google.re2j.Pattern;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent; import com.googlecode.objectify.annotation.Parent;
@ -407,10 +408,10 @@ public class Registrar extends ImmutableObject
// Metadata. // Metadata.
/** The time when this registrar was created. */ /** The time when this registrar was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); @Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** An automatically managed last-saved timestamp. */ /** An automatically managed last-saved timestamp. */
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null); @Ignore UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/** The time that the certificate was last updated. */ /** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime; DateTime lastCertificateUpdateTime;

View file

@ -1,58 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.joda.time.DateTimeZone.UTC;
import google.registry.model.CreateAutoTimestamp;
import java.util.Date;
import org.joda.time.DateTime;
/** Saves {@link CreateAutoTimestamp} as either its value, or the current time if it was null. */
public class CreateAutoTimestampTranslatorFactory
extends AbstractSimpleTranslatorFactory<CreateAutoTimestamp, Date> {
public CreateAutoTimestampTranslatorFactory() {
super(CreateAutoTimestamp.class);
}
@Override
SimpleTranslator<CreateAutoTimestamp, Date> createTranslator() {
return new SimpleTranslator<CreateAutoTimestamp, Date>() {
/**
* Load an existing timestamp. It can be assumed to be non-null since if the field is null in
* Datastore then Objectify will skip this translator and directly load a null.
*/
@Override
public CreateAutoTimestamp loadValue(Date datastoreValue) {
return CreateAutoTimestamp.create(new DateTime(datastoreValue, UTC));
}
/** Save a timestamp, setting it to the current time if it did not have a previous value. */
@Override
public Date saveValue(CreateAutoTimestamp pojoValue) {
// Note that we use the current transaction manager -- we need to do this under JPA when we
// serialize the entity from a Transaction object, but we need to use the JPA transaction
// manager in that case.
return (pojoValue.getTimestamp() == null
? tm().getTransactionTime()
: pojoValue.getTimestamp())
.toDate();
}
};
}
}

View file

@ -1,48 +0,0 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.converter;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import javax.annotation.Nullable;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* JPA converter to for storing/retrieving {@link ZonedDateTime} objects.
*
* <p>Hibernate provides a default converter for {@link ZonedDateTime}, but it converts timestamp to
* a non-normalized format, e.g., 2019-09-01T01:01:01Z will be converted to
* 2019-09-01T01:01:01Z[UTC]. This converter solves that problem by explicitly calling {@link
* ZoneId#normalized()} to normalize the zone id.
*/
@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
@Nullable
public Timestamp convertToDatabaseColumn(@Nullable ZonedDateTime attribute) {
return attribute == null ? null : Timestamp.from(attribute.toInstant());
}
@Override
@Nullable
public ZonedDateTime convertToEntityAttribute(@Nullable Timestamp dbData) {
return dbData == null
? null
: ZonedDateTime.ofInstant(dbData.toInstant(), ZoneId.of("UTC").normalized());
}
}

View file

@ -96,7 +96,6 @@
<class>google.registry.persistence.converter.StringSetConverter</class> <class>google.registry.persistence.converter.StringSetConverter</class>
<class>google.registry.persistence.converter.TldStateTransitionConverter</class> <class>google.registry.persistence.converter.TldStateTransitionConverter</class>
<class>google.registry.persistence.converter.TransferServerApproveEntitySetConverter</class> <class>google.registry.persistence.converter.TransferServerApproveEntitySetConverter</class>
<class>google.registry.persistence.converter.ZonedDateTimeConverter</class>
<!-- Generated converters for VKey --> <!-- Generated converters for VKey -->
<class>google.registry.model.billing.VKeyConverter_Cancellation</class> <class>google.registry.model.billing.VKeyConverter_Cancellation</class>

View file

@ -23,12 +23,11 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.apache.avro.Schema; import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord; import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord; import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -86,10 +85,8 @@ class BillingEventTest {
void testParseBillingEventFromRecord_success() { void testParseBillingEventFromRecord_success() {
BillingEvent event = BillingEvent.parseFromRecord(schemaAndRecord); BillingEvent event = BillingEvent.parseFromRecord(schemaAndRecord);
assertThat(event.id()).isEqualTo(1); assertThat(event.id()).isEqualTo(1);
assertThat(event.billingTime()) assertThat(event.billingTime()).isEqualTo(DateTime.parse("2017-10-24T09:06:03Z"));
.isEqualTo(ZonedDateTime.of(2017, 10, 24, 9, 6, 3, 0, ZoneId.of("UTC"))); assertThat(event.eventTime()).isEqualTo(DateTime.parse("2017-01-19T23:59:43Z"));
assertThat(event.eventTime())
.isEqualTo(ZonedDateTime.of(2017, 1, 19, 23, 59, 43, 0, ZoneId.of("UTC")));
assertThat(event.registrarId()).isEqualTo("myRegistrar"); assertThat(event.registrarId()).isEqualTo("myRegistrar");
assertThat(event.billingId()).isEqualTo("12345-CRRHELLO"); assertThat(event.billingId()).isEqualTo("12345-CRRHELLO");
assertThat(event.poNumber()).isEmpty(); assertThat(event.poNumber()).isEmpty();

View file

@ -55,8 +55,6 @@ import google.registry.util.ResourceUtils;
import java.io.File; import java.io.File;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
@ -104,8 +102,8 @@ class InvoicingPipelineTest {
ImmutableList.of( ImmutableList.of(
BillingEvent.create( BillingEvent.create(
1, 1,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar", "theRegistrar",
"234", "234",
"", "",
@ -119,8 +117,8 @@ class InvoicingPipelineTest {
""), ""),
BillingEvent.create( BillingEvent.create(
2, 2,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar", "theRegistrar",
"234", "234",
"", "",
@ -134,8 +132,8 @@ class InvoicingPipelineTest {
""), ""),
BillingEvent.create( BillingEvent.create(
3, 3,
ZonedDateTime.of(2017, 10, 2, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-02T00:00:00Z"),
ZonedDateTime.of(2017, 9, 29, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-09-29T00:00:00Z"),
"theRegistrar", "theRegistrar",
"234", "234",
"", "",
@ -149,8 +147,8 @@ class InvoicingPipelineTest {
""), ""),
BillingEvent.create( BillingEvent.create(
4, 4,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"bestdomains", "bestdomains",
"456", "456",
"116688", "116688",
@ -164,8 +162,8 @@ class InvoicingPipelineTest {
""), ""),
BillingEvent.create( BillingEvent.create(
5, 5,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"anotherRegistrar", "anotherRegistrar",
"789", "789",
"", "",
@ -179,8 +177,8 @@ class InvoicingPipelineTest {
"SUNRISE ANCHOR_TENANT"), "SUNRISE ANCHOR_TENANT"),
BillingEvent.create( BillingEvent.create(
6, 6,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar", "theRegistrar",
"234", "234",
"", "",
@ -194,8 +192,8 @@ class InvoicingPipelineTest {
""), ""),
BillingEvent.create( BillingEvent.create(
7, 7,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")), DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar", "theRegistrar",
"234", "234",
"", "",

View file

@ -19,10 +19,11 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.testing.DatabaseHelper.loadByEntity; import static google.registry.testing.DatabaseHelper.loadByEntity;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton; import google.registry.model.common.CrossTldSingleton;
import google.registry.testing.AppEngineExtension; import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
@ -31,22 +32,19 @@ import org.junit.jupiter.api.extension.RegisterExtension;
public class CreateAutoTimestampTest { public class CreateAutoTimestampTest {
@RegisterExtension @RegisterExtension
public final AppEngineExtension appEngine = public final JpaUnitTestExtension jpaUnitTestExtension =
AppEngineExtension.builder() new JpaTestExtensions.Builder()
.withCloudSql() .withEntityClass(CreateAutoTimestampTestObject.class)
.withOfyTestEntities(CreateAutoTimestampTestObject.class) .buildUnitTestExtension();
.withJpaUnitTestEntities(CreateAutoTimestampTestObject.class)
.build();
/** Timestamped class. */ /** Timestamped class. */
@Entity(name = "CatTestEntity") @Entity
@javax.persistence.Entity
public static class CreateAutoTimestampTestObject extends CrossTldSingleton { public static class CreateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID; @Id long id = SINGLETON_ID;
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null); CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
} }
private CreateAutoTimestampTestObject reload() { private static CreateAutoTimestampTestObject reload() {
return loadByEntity(new CreateAutoTimestampTestObject()); return loadByEntity(new CreateAutoTimestampTestObject());
} }
@ -61,7 +59,7 @@ public class CreateAutoTimestampTest {
return tm().getTransactionTime(); return tm().getTransactionTime();
}); });
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(reload().createTime.timestamp).isEqualTo(transactionTime); assertThat(reload().createTime.getTimestamp()).isEqualTo(transactionTime);
} }
@Test @Test
@ -74,6 +72,6 @@ public class CreateAutoTimestampTest {
tm().put(object); tm().put(object);
}); });
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(reload().createTime.timestamp).isEqualTo(oldCreateTime); assertThat(reload().createTime.getTimestamp()).isEqualTo(oldCreateTime);
} }
} }

View file

@ -16,59 +16,42 @@ package google.registry.model;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton; import google.registry.model.common.CrossTldSingleton;
import google.registry.model.ofy.Ofy;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension; import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension; import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link UpdateAutoTimestamp}. */ /** Unit tests for {@link UpdateAutoTimestamp}. */
public class UpdateAutoTimestampTest { public class UpdateAutoTimestampTest {
FakeClock clock = new FakeClock(); private final FakeClock clock = new FakeClock();
@RegisterExtension @RegisterExtension
public final AppEngineExtension appEngine = public final JpaUnitTestExtension jpaUnitTestExtension =
AppEngineExtension.builder() new JpaTestExtensions.Builder()
.withCloudSql()
.withJpaUnitTestEntities(UpdateAutoTimestampTestObject.class)
.withOfyTestEntities(UpdateAutoTimestampTestObject.class)
.withClock(clock) .withClock(clock)
.build(); .withEntityClass(UpdateAutoTimestampTestObject.class)
.buildUnitTestExtension();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
}
/** Timestamped class. */ /** Timestamped class. */
@Entity(name = "UatTestEntity") @Entity
@javax.persistence.Entity
public static class UpdateAutoTimestampTestObject extends CrossTldSingleton { public static class UpdateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID; @Id long id = SINGLETON_ID;
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null); UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
} }
private UpdateAutoTimestampTestObject reload() { private static UpdateAutoTimestampTestObject reload() {
return tm().transact( return tm().transact(
() -> () -> tm().loadByKey(VKey.createSql(UpdateAutoTimestampTestObject.class, 1L)));
tm().loadByKey(
VKey.create(
UpdateAutoTimestampTestObject.class,
1L,
Key.create(new UpdateAutoTimestampTestObject()))));
} }
@Test @Test
@ -78,12 +61,12 @@ public class UpdateAutoTimestampTest {
() -> { () -> {
clock.advanceOneMilli(); clock.advanceOneMilli();
UpdateAutoTimestampTestObject object = new UpdateAutoTimestampTestObject(); UpdateAutoTimestampTestObject object = new UpdateAutoTimestampTestObject();
assertThat(object.updateTime.timestamp).isNull(); assertThat(object.updateTime.getTimestamp()).isEqualTo(START_OF_TIME);
tm().insert(object); tm().insert(object);
return tm().getTransactionTime(); return tm().getTransactionTime();
}); });
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(reload().updateTime.timestamp).isEqualTo(transactionTime); assertThat(reload().updateTime.getTimestamp()).isEqualTo(transactionTime);
} }
@Test @Test
@ -99,7 +82,7 @@ public class UpdateAutoTimestampTest {
UpdateAutoTimestampTestObject object = reload(); UpdateAutoTimestampTestObject object = reload();
clock.advanceOneMilli(); clock.advanceOneMilli();
try (UpdateAutoTimestamp.DisableAutoUpdateResource disabler = try (UpdateAutoTimestamp.DisableAutoUpdateResource ignoredDisabler =
new UpdateAutoTimestamp.DisableAutoUpdateResource()) { new UpdateAutoTimestamp.DisableAutoUpdateResource()) {
DateTime secondTransactionTime = DateTime secondTransactionTime =
tm().transact( tm().transact(
@ -109,7 +92,7 @@ public class UpdateAutoTimestampTest {
}); });
assertThat(secondTransactionTime).isGreaterThan(initialTime); assertThat(secondTransactionTime).isGreaterThan(initialTime);
} }
assertThat(reload().updateTime.timestamp).isEqualTo(initialTime); assertThat(reload().updateTime.getTimestamp()).isEqualTo(initialTime);
} }
@Test @Test
@ -124,7 +107,7 @@ public class UpdateAutoTimestampTest {
return tm().getTransactionTime(); return tm().getTransactionTime();
}); });
tm().clearSessionCache(); tm().clearSessionCache();
assertThat(reload().updateTime.timestamp).isEqualTo(transactionTime); assertThat(reload().updateTime.getTimestamp()).isEqualTo(transactionTime);
} }
@Test @Test

View file

@ -1,116 +0,0 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.converter;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.insertInDb;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZonedDateTime;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ZonedDateTimeConverter}. */
public class ZonedDateTimeConverterTest {
@RegisterExtension
public final JpaUnitTestExtension jpaExtension =
new JpaTestExtensions.Builder().withEntityClass(TestEntity.class).buildUnitTestExtension();
private final ZonedDateTimeConverter converter = new ZonedDateTimeConverter();
@Test
void convertToDatabaseColumn_returnsNullIfInputIsNull() {
assertThat(converter.convertToDatabaseColumn(null)).isNull();
}
@Test
void convertToDatabaseColumn_convertsCorrectly() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-01T01:01:01Z");
assertThat(converter.convertToDatabaseColumn(zonedDateTime).toInstant())
.isEqualTo(zonedDateTime.toInstant());
}
@Test
void convertToEntityAttribute_returnsNullIfInputIsNull() {
assertThat(converter.convertToEntityAttribute(null)).isNull();
}
@Test
void convertToEntityAttribute_convertsCorrectly() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-01T01:01:01Z");
Instant instant = zonedDateTime.toInstant();
assertThat(converter.convertToEntityAttribute(Timestamp.from(instant)))
.isEqualTo(zonedDateTime);
}
@Test
void converter_generatesTimestampWithNormalizedZone() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01Z");
TestEntity entity = new TestEntity("normalized_utc_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm()
.transact(
() -> jpaTm().getEntityManager().find(TestEntity.class, "normalized_utc_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-09-01T01:01:01Z");
}
@Test
void converter_convertsNonNormalizedZoneCorrectly() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01Z[UTC]");
TestEntity entity = new TestEntity("non_normalized_utc_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm()
.transact(
() -> jpaTm().getEntityManager().find(TestEntity.class, "non_normalized_utc_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-09-01T01:01:01Z");
}
@Test
void converter_convertsNonUtcZoneCorrectly() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01+05:00");
TestEntity entity = new TestEntity("new_york_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "new_york_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-08-31T20:01:01Z");
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name;
ZonedDateTime zdt;
public TestEntity() {}
TestEntity(String name, ZonedDateTime zdt) {
this.name = name;
this.zdt = zdt;
}
}
}

View file

@ -1,9 +1,3 @@
class google.registry.model.CreateAutoTimestamp {
org.joda.time.DateTime timestamp;
}
class google.registry.model.UpdateAutoTimestamp {
org.joda.time.DateTime timestamp;
}
class google.registry.model.billing.BillingEvent$Cancellation { class google.registry.model.billing.BillingEvent$Cancellation {
@Id java.lang.Long id; @Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<google.registry.model.domain.DomainHistory> parent; @Parent com.googlecode.objectify.Key<google.registry.model.domain.DomainHistory> parent;
@ -81,7 +75,6 @@ enum google.registry.model.billing.BillingEvent$RenewalPriceBehavior {
} }
class google.registry.model.common.EntityGroupRoot { class google.registry.model.common.EntityGroupRoot {
@Id java.lang.String id; @Id java.lang.String id;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
} }
class google.registry.model.common.GaeUserIdConverter { class google.registry.model.common.GaeUserIdConverter {
@Id long id; @Id long id;
@ -102,8 +95,6 @@ class google.registry.model.contact.ContactAuthInfo {
} }
class google.registry.model.contact.ContactBase { class google.registry.model.contact.ContactBase {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.contact.ContactAuthInfo authInfo; google.registry.model.contact.ContactAuthInfo authInfo;
google.registry.model.contact.ContactPhoneNumber fax; google.registry.model.contact.ContactPhoneNumber fax;
google.registry.model.contact.ContactPhoneNumber voice; google.registry.model.contact.ContactPhoneNumber voice;
@ -144,8 +135,6 @@ class google.registry.model.contact.ContactPhoneNumber {
} }
class google.registry.model.contact.ContactResource { class google.registry.model.contact.ContactResource {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.contact.ContactAuthInfo authInfo; google.registry.model.contact.ContactAuthInfo authInfo;
google.registry.model.contact.ContactPhoneNumber fax; google.registry.model.contact.ContactPhoneNumber fax;
google.registry.model.contact.ContactPhoneNumber voice; google.registry.model.contact.ContactPhoneNumber voice;
@ -191,8 +180,6 @@ class google.registry.model.domain.DomainAuthInfo {
} }
class google.registry.model.domain.DomainBase { class google.registry.model.domain.DomainBase {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.domain.DomainAuthInfo authInfo; google.registry.model.domain.DomainAuthInfo authInfo;
google.registry.model.domain.launch.LaunchNotice launchNotice; google.registry.model.domain.launch.LaunchNotice launchNotice;
google.registry.model.transfer.DomainTransferData transferData; google.registry.model.transfer.DomainTransferData transferData;
@ -223,8 +210,6 @@ class google.registry.model.domain.DomainBase {
} }
class google.registry.model.domain.DomainContent { class google.registry.model.domain.DomainContent {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.domain.DomainAuthInfo authInfo; google.registry.model.domain.DomainAuthInfo authInfo;
google.registry.model.domain.launch.LaunchNotice launchNotice; google.registry.model.domain.launch.LaunchNotice launchNotice;
google.registry.model.transfer.DomainTransferData transferData; google.registry.model.transfer.DomainTransferData transferData;
@ -334,8 +319,6 @@ class google.registry.model.domain.token.AllocationToken {
@Id java.lang.String token; @Id java.lang.String token;
boolean discountPremiums; boolean discountPremiums;
double discountFraction; double discountFraction;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.billing.BillingEvent$RenewalPriceBehavior renewalPriceBehavior; google.registry.model.billing.BillingEvent$RenewalPriceBehavior renewalPriceBehavior;
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> tokenStatusTransitions; google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> tokenStatusTransitions;
google.registry.model.domain.token.AllocationToken$RegistrationBehavior registrationBehavior; google.registry.model.domain.token.AllocationToken$RegistrationBehavior registrationBehavior;
@ -393,8 +376,6 @@ class google.registry.model.eppcommon.Trid {
} }
class google.registry.model.host.HostBase { class google.registry.model.host.HostBase {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain; google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain;
java.lang.String creationClientId; java.lang.String creationClientId;
java.lang.String currentSponsorClientId; java.lang.String currentSponsorClientId;
@ -425,8 +406,6 @@ class google.registry.model.host.HostHistory {
} }
class google.registry.model.host.HostResource { class google.registry.model.host.HostResource {
@Id java.lang.String repoId; @Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain; google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain;
java.lang.String creationClientId; java.lang.String creationClientId;
java.lang.String currentSponsorClientId; java.lang.String currentSponsorClientId;
@ -443,7 +422,6 @@ class google.registry.model.index.EppResourceIndex {
@Id java.lang.String id; @Id java.lang.String id;
@Parent com.googlecode.objectify.Key<google.registry.model.index.EppResourceIndexBucket> bucket; @Parent com.googlecode.objectify.Key<google.registry.model.index.EppResourceIndexBucket> bucket;
com.googlecode.objectify.Key<? extends google.registry.model.EppResource> reference; com.googlecode.objectify.Key<? extends google.registry.model.EppResource> reference;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
java.lang.String kind; java.lang.String kind;
} }
class google.registry.model.index.EppResourceIndexBucket { class google.registry.model.index.EppResourceIndexBucket {
@ -451,19 +429,16 @@ class google.registry.model.index.EppResourceIndexBucket {
} }
class google.registry.model.index.ForeignKeyIndex$ForeignKeyContactIndex { class google.registry.model.index.ForeignKeyIndex$ForeignKeyContactIndex {
@Id java.lang.String foreignKey; @Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference; google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime; org.joda.time.DateTime deletionTime;
} }
class google.registry.model.index.ForeignKeyIndex$ForeignKeyDomainIndex { class google.registry.model.index.ForeignKeyIndex$ForeignKeyDomainIndex {
@Id java.lang.String foreignKey; @Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference; google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime; org.joda.time.DateTime deletionTime;
} }
class google.registry.model.index.ForeignKeyIndex$ForeignKeyHostIndex { class google.registry.model.index.ForeignKeyIndex$ForeignKeyHostIndex {
@Id java.lang.String foreignKey; @Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference; google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime; org.joda.time.DateTime deletionTime;
} }
@ -489,7 +464,6 @@ class google.registry.model.poll.PollMessage$OneTime {
} }
class google.registry.model.rde.RdeRevision { class google.registry.model.rde.RdeRevision {
@Id java.lang.String id; @Id java.lang.String id;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
int revision; int revision;
} }
class google.registry.model.registrar.Registrar { class google.registry.model.registrar.Registrar {
@ -498,8 +472,6 @@ class google.registry.model.registrar.Registrar {
boolean blockPremiumNames; boolean blockPremiumNames;
boolean contactsRequireSyncing; boolean contactsRequireSyncing;
boolean registryLockAllowed; boolean registryLockAllowed;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
google.registry.model.registrar.Registrar$State state; google.registry.model.registrar.Registrar$State state;
google.registry.model.registrar.Registrar$Type type; google.registry.model.registrar.Registrar$Type type;
google.registry.model.registrar.RegistrarAddress internationalizedAddress; google.registry.model.registrar.RegistrarAddress internationalizedAddress;

View file

@ -23,15 +23,12 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf; import static google.registry.util.DateTimeUtils.latestOf;
import static google.registry.util.DateTimeUtils.leapSafeAddYears; import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.leapSafeSubtractYears; import static google.registry.util.DateTimeUtils.leapSafeSubtractYears;
import static google.registry.util.DateTimeUtils.toJodaDateTime;
import static google.registry.util.DateTimeUtils.toLocalDate; import static google.registry.util.DateTimeUtils.toLocalDate;
import static google.registry.util.DateTimeUtils.toSqlDate; import static google.registry.util.DateTimeUtils.toSqlDate;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.sql.Date; import java.sql.Date;
import java.time.ZonedDateTime;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -99,48 +96,6 @@ class DateTimeUtilsTest {
assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of())); assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of()));
} }
@Test
void testSuccess_toZonedDateTime_preservesTimeZone() {
DateTime dateTime = DateTime.parse("2019-09-06T10:59:36.283-07:00"); // PDT
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2019-09-06T10:59:36.283-07:00"); // still PDT
}
@Test
void testSuccess_toZonedDateTime_fromStringZulu() {
DateTime dateTime = DateTime.parse("2015-10-13T11:22:33.168Z");
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2015-10-13T11:22:33.168Z");
}
@Test
void testSuccess_toZonedDateTime_leapYear() {
DateTime dateTime = DateTime.parse("2016-02-29T11:22:33.168Z");
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2016-02-29T11:22:33.168Z");
}
@Test
void testSuccess_toJodaDateTime_preservesTimeZone() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-06T10:59:36.283-07:00"); // PDT
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2019-09-06T10:59:36.283-07:00"); // still PDT
}
@Test
void testSuccess_toJodaDateTime_fromStringZulu() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2015-10-13T11:22:33.168Z");
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2015-10-13T11:22:33.168Z");
}
@Test
void testSuccess_toJodaDateTime_leapYear() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2016-02-29T11:22:33.168Z");
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2016-02-29T11:22:33.168Z");
}
@Test @Test
void testSuccess_toSqlDate() { void testSuccess_toSqlDate() {
LocalDate localDate = LocalDate.parse("2020-02-29"); LocalDate localDate = LocalDate.parse("2020-02-29");