mirror of
https://github.com/google/nomulus.git
synced 2025-07-10 05:03:24 +02:00
Remove converter for CreateAutoTimestamp (#1429)
We can handle it the same way that we handle UpdateAutoTimestamp, where we simply populate it in SQL if it doesn't exist. This has the following benefits: 1. The converter is unnecessary code 2. We get non-null column definitions for free (overridden in EppResource to allow null creation times so that legacy *History objects can contain null in that field 3. More importantly, this allows us for proper SQL->DS replay. If the field is filled out using a converter (as before this PR) then the field is only actually filled out on transaction commit (rather than when the write occurs within the transaction). This means that when we serialize the Transaction object during the transaction (the data that gets replayed to Datastore), we are crucially missing the creation time. If the creation time is written on commit, we have to start a new transaction to write the Transaction object, and it's an absolute necessity that the record of the transaction be included in the transaction itself so as to avoid situations where the transaction succeeds but the record fails. If the field is filled out in a @PrePersist method, crucially that occurs on the object write itself (before transaction commit).
This commit is contained in:
parent
839d27906a
commit
4368cc8ee0
16 changed files with 118 additions and 182 deletions
|
@ -14,8 +14,20 @@
|
||||||
|
|
||||||
package google.registry.model;
|
package google.registry.model;
|
||||||
|
|
||||||
|
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.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.Embeddable;
|
||||||
|
import javax.persistence.PostLoad;
|
||||||
|
import javax.persistence.PrePersist;
|
||||||
|
import javax.persistence.PreUpdate;
|
||||||
|
import javax.persistence.Transient;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,9 +35,37 @@ import org.joda.time.DateTime;
|
||||||
*
|
*
|
||||||
* @see CreateAutoTimestampTranslatorFactory
|
* @see CreateAutoTimestampTranslatorFactory
|
||||||
*/
|
*/
|
||||||
|
@Embeddable
|
||||||
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
|
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
|
||||||
|
|
||||||
DateTime timestamp;
|
@Transient DateTime timestamp;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
@Ignore
|
||||||
|
ZonedDateTime creationTime;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
@PreUpdate
|
||||||
|
void setTimestamp() {
|
||||||
|
if (creationTime == null) {
|
||||||
|
timestamp = 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
|
||||||
|
@ -36,6 +76,7 @@ public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerial
|
||||||
public static CreateAutoTimestamp create(@Nullable DateTime timestamp) {
|
public static CreateAutoTimestamp create(@Nullable DateTime timestamp) {
|
||||||
CreateAutoTimestamp instance = new CreateAutoTimestamp();
|
CreateAutoTimestamp instance = new CreateAutoTimestamp();
|
||||||
instance.timestamp = timestamp;
|
instance.timestamp = timestamp;
|
||||||
|
instance.creationTime = (timestamp == null) ? null : DateTimeUtils.toZonedDateTime(timestamp);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import javax.persistence.Access;
|
import javax.persistence.Access;
|
||||||
import javax.persistence.AccessType;
|
import javax.persistence.AccessType;
|
||||||
|
import javax.persistence.AttributeOverride;
|
||||||
|
import javax.persistence.AttributeOverrides;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
@ -112,7 +114,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
|
||||||
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
||||||
* resource fields.
|
* resource fields.
|
||||||
*/
|
*/
|
||||||
@Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
@AttributeOverrides({@AttributeOverride(name = "creationTime", column = @Column())})
|
||||||
|
@Index
|
||||||
|
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time when this resource was or will be deleted.
|
* The time when this resource was or will be deleted.
|
||||||
|
|
|
@ -28,6 +28,8 @@ import google.registry.util.DateTimeUtils;
|
||||||
import java.time.ZonedDateTime;
|
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.AttributeOverrides;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.FetchType;
|
import javax.persistence.FetchType;
|
||||||
|
@ -100,7 +102,11 @@ public final class RegistryLock extends ImmutableObject implements Buildable, Sq
|
||||||
private String registrarPocId;
|
private String registrarPocId;
|
||||||
|
|
||||||
/** When the lock is first requested. */
|
/** When the lock is first requested. */
|
||||||
@Column(nullable = false)
|
@AttributeOverrides({
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "creationTime",
|
||||||
|
column = @Column(name = "lockRequestTime", nullable = false))
|
||||||
|
})
|
||||||
private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
|
private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
|
||||||
|
|
||||||
/** When the unlock is first requested. */
|
/** When the unlock is first requested. */
|
||||||
|
|
|
@ -122,7 +122,6 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||||
@Nullable @Index String domainName;
|
@Nullable @Index String domainName;
|
||||||
|
|
||||||
/** When this token was created. */
|
/** When this token was created. */
|
||||||
@Column(nullable = false)
|
|
||||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
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. */
|
||||||
|
|
|
@ -27,6 +27,8 @@ import google.registry.model.replay.SqlOnlyEntity;
|
||||||
import google.registry.model.tld.label.ReservedList.ReservedListEntry;
|
import google.registry.model.tld.label.ReservedList.ReservedListEntry;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javax.persistence.AttributeOverride;
|
||||||
|
import javax.persistence.AttributeOverrides;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
|
@ -56,7 +58,11 @@ public class ClaimsList extends ImmutableObject implements SqlOnlyEntity {
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
Long revisionId;
|
Long revisionId;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@AttributeOverrides({
|
||||||
|
@AttributeOverride(
|
||||||
|
name = "creationTime",
|
||||||
|
column = @Column(name = "creationTimestamp", nullable = false))
|
||||||
|
})
|
||||||
CreateAutoTimestamp creationTimestamp = CreateAutoTimestamp.create(null);
|
CreateAutoTimestamp creationTimestamp = CreateAutoTimestamp.create(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,53 +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.base.MoreObjects.firstNonNull;
|
|
||||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
|
||||||
|
|
||||||
import google.registry.model.CreateAutoTimestamp;
|
|
||||||
import google.registry.util.DateTimeUtils;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.persistence.AttributeConverter;
|
|
||||||
import javax.persistence.Converter;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
|
|
||||||
/** JPA converter to for storing/retrieving CreateAutoTimestamp objects. */
|
|
||||||
@Converter(autoApply = true)
|
|
||||||
public class CreateAutoTimestampConverter
|
|
||||||
implements AttributeConverter<CreateAutoTimestamp, Timestamp> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public Timestamp convertToDatabaseColumn(@Nullable CreateAutoTimestamp entity) {
|
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
DateTime dateTime = firstNonNull(entity.getTimestamp(), jpaTm().getTransactionTime());
|
|
||||||
return Timestamp.from(DateTimeUtils.toZonedDateTime(dateTime).toInstant());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public CreateAutoTimestamp convertToEntityAttribute(@Nullable Timestamp columnValue) {
|
|
||||||
if (columnValue == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(columnValue.toInstant(), ZoneOffset.UTC);
|
|
||||||
return CreateAutoTimestamp.create(DateTimeUtils.toJodaDateTime(zdt));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -83,7 +83,6 @@
|
||||||
<class>google.registry.persistence.converter.BillingEventFlagSetConverter</class>
|
<class>google.registry.persistence.converter.BillingEventFlagSetConverter</class>
|
||||||
<class>google.registry.persistence.converter.BloomFilterConverter</class>
|
<class>google.registry.persistence.converter.BloomFilterConverter</class>
|
||||||
<class>google.registry.persistence.converter.CidrAddressBlockListConverter</class>
|
<class>google.registry.persistence.converter.CidrAddressBlockListConverter</class>
|
||||||
<class>google.registry.persistence.converter.CreateAutoTimestampConverter</class>
|
|
||||||
<class>google.registry.persistence.converter.CurrencyToBillingConverter</class>
|
<class>google.registry.persistence.converter.CurrencyToBillingConverter</class>
|
||||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||||
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
||||||
|
|
|
@ -657,20 +657,18 @@ public class DomainBaseSqlTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) {
|
private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) {
|
||||||
// Fix the original creation timestamp (this gets initialized on first write)
|
|
||||||
DomainBase org = domain.asBuilder().setCreationTime(thatDomain.getCreationTime()).build();
|
|
||||||
|
|
||||||
ImmutableList<String> moreExcepts =
|
ImmutableList<String> moreExcepts =
|
||||||
new ImmutableList.Builder<String>()
|
new ImmutableList.Builder<String>()
|
||||||
.addAll(Arrays.asList(excepts))
|
.addAll(Arrays.asList(excepts))
|
||||||
|
.add("creationTime")
|
||||||
.add("updateTimestamp")
|
.add("updateTimestamp")
|
||||||
.add("transferData")
|
.add("transferData")
|
||||||
.build();
|
.build();
|
||||||
// Note that the equality comparison forces a lazy load of all fields.
|
// Note that the equality comparison forces a lazy load of all fields.
|
||||||
assertAboutImmutableObjects().that(thatDomain).isEqualExceptFields(org, moreExcepts);
|
assertAboutImmutableObjects().that(thatDomain).isEqualExceptFields(domain, moreExcepts);
|
||||||
// Transfer data cannot be directly compared due to serverApproveEtities inequalities
|
// Transfer data cannot be directly compared due to serverApproveEtities inequalities
|
||||||
assertAboutImmutableObjects()
|
assertAboutImmutableObjects()
|
||||||
.that(domain.getTransferData())
|
.that(domain.getTransferData())
|
||||||
.isEqualExceptFields(org.getTransferData(), "serverApproveEntities");
|
.isEqualExceptFields(thatDomain.getTransferData(), "serverApproveEntities");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ import com.google.common.testing.TestLogHandler;
|
||||||
import com.google.common.truth.Truth8;
|
import com.google.common.truth.Truth8;
|
||||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||||
|
import google.registry.model.domain.token.AllocationToken;
|
||||||
|
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||||
import google.registry.model.ofy.CommitLogBucket;
|
import google.registry.model.ofy.CommitLogBucket;
|
||||||
import google.registry.model.ofy.Ofy;
|
import google.registry.model.ofy.Ofy;
|
||||||
import google.registry.model.server.Lock;
|
import google.registry.model.server.Lock;
|
||||||
|
@ -64,8 +67,8 @@ public class ReplicateToDatastoreActionTest {
|
||||||
public final AppEngineExtension appEngine =
|
public final AppEngineExtension appEngine =
|
||||||
AppEngineExtension.builder()
|
AppEngineExtension.builder()
|
||||||
.withDatastoreAndCloudSql()
|
.withDatastoreAndCloudSql()
|
||||||
.withOfyTestEntities(Lock.class, TestObject.class)
|
.withOfyTestEntities(AllocationToken.class, Lock.class, TestObject.class)
|
||||||
.withJpaUnitTestEntities(Lock.class, TestObject.class)
|
.withJpaUnitTestEntities(AllocationToken.class, Lock.class, TestObject.class)
|
||||||
.withClock(fakeClock)
|
.withClock(fakeClock)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -97,9 +100,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@RetryingTest(4)
|
@RetryingTest(4)
|
||||||
void testReplication() {
|
void testReplication() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestObject foo = TestObject.create("foo");
|
TestObject foo = TestObject.create("foo");
|
||||||
TestObject bar = TestObject.create("bar");
|
TestObject bar = TestObject.create("bar");
|
||||||
|
@ -126,9 +127,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@RetryingTest(4)
|
@RetryingTest(4)
|
||||||
void testReplayFromLastTxn() {
|
void testReplayFromLastTxn() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestObject foo = TestObject.create("foo");
|
TestObject foo = TestObject.create("foo");
|
||||||
TestObject bar = TestObject.create("bar");
|
TestObject bar = TestObject.create("bar");
|
||||||
|
@ -152,9 +151,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@RetryingTest(4)
|
@RetryingTest(4)
|
||||||
void testUnintentionalConcurrency() {
|
void testUnintentionalConcurrency() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestObject foo = TestObject.create("foo");
|
TestObject foo = TestObject.create("foo");
|
||||||
TestObject bar = TestObject.create("bar");
|
TestObject bar = TestObject.create("bar");
|
||||||
|
@ -189,11 +186,24 @@ public class ReplicateToDatastoreActionTest {
|
||||||
Level.WARNING, "Ignoring transaction 1, which appears to have already been applied.");
|
Level.WARNING, "Ignoring transaction 1, which appears to have already been applied.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RetryingTest(4)
|
||||||
|
void testCreateAutoTimestamp() {
|
||||||
|
// Verify that fields populated by the DB (e.g. CreateAutoTimestamp) correctly get populated in
|
||||||
|
// both databases.
|
||||||
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
|
|
||||||
|
AllocationToken allocationToken =
|
||||||
|
new AllocationToken.Builder().setToken("abc123").setTokenType(TokenType.SINGLE_USE).build();
|
||||||
|
insertInDb(allocationToken);
|
||||||
|
runAndVerifySuccess();
|
||||||
|
|
||||||
|
assertThat(ofyTm().transact(() -> ofyTm().loadByEntity(allocationToken)))
|
||||||
|
.isEqualTo(jpaTm().transact(() -> jpaTm().loadByEntity(allocationToken)));
|
||||||
|
}
|
||||||
|
|
||||||
@RetryingTest(4)
|
@RetryingTest(4)
|
||||||
void testMissingTransactions() {
|
void testMissingTransactions() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a transaction (should have a transaction id of 1).
|
// Write a transaction (should have a transaction id of 1).
|
||||||
TestObject foo = TestObject.create("foo");
|
TestObject foo = TestObject.create("foo");
|
||||||
|
@ -212,9 +222,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMissingTransactions_fullTask() {
|
void testMissingTransactions_fullTask() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a transaction (should have a transaction id of 1).
|
// Write a transaction (should have a transaction id of 1).
|
||||||
TestObject foo = TestObject.create("foo");
|
TestObject foo = TestObject.create("foo");
|
||||||
|
@ -234,9 +242,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBeforeDatastoreSaveCallback() {
|
void testBeforeDatastoreSaveCallback() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestObject testObject = TestObject.create("foo");
|
TestObject testObject = TestObject.create("foo");
|
||||||
insertInDb(testObject);
|
insertInDb(testObject);
|
||||||
|
@ -247,9 +253,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testNotInMigrationState_doesNothing() {
|
void testNotInMigrationState_doesNothing() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set a schedule that backtracks the current status to DATASTORE_PRIMARY
|
// set a schedule that backtracks the current status to DATASTORE_PRIMARY
|
||||||
DateTime now = fakeClock.nowUtc();
|
DateTime now = fakeClock.nowUtc();
|
||||||
|
@ -287,9 +291,7 @@ public class ReplicateToDatastoreActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_cannotAcquireLock() {
|
void testFailure_cannotAcquireLock() {
|
||||||
if (!ReplayExtension.replayTestsEnabled()) {
|
assumeTrue(ReplayExtension.replayTestsEnabled());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatusChecker requestStatusChecker = mock(RequestStatusChecker.class);
|
RequestStatusChecker requestStatusChecker = mock(RequestStatusChecker.class);
|
||||||
when(requestStatusChecker.getLogId()).thenReturn("logId");
|
when(requestStatusChecker.getLogId()).thenReturn("logId");
|
||||||
|
|
|
@ -1,82 +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.CreateAutoTimestamp;
|
|
||||||
import google.registry.model.ImmutableObject;
|
|
||||||
import google.registry.model.replay.EntityTest.EntityForTesting;
|
|
||||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
|
||||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
|
|
||||||
import google.registry.testing.FakeClock;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
|
||||||
|
|
||||||
/** Unit tests for {@link CreateAutoTimestampConverter}. */
|
|
||||||
public class CreateAutoTimestampConverterTest {
|
|
||||||
|
|
||||||
private final FakeClock fakeClock = new FakeClock();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
public final JpaUnitTestExtension jpaExtension =
|
|
||||||
new JpaTestExtensions.Builder()
|
|
||||||
.withClock(fakeClock)
|
|
||||||
.withEntityClass(TestEntity.class)
|
|
||||||
.buildUnitTestExtension();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testTypeConversion() {
|
|
||||||
CreateAutoTimestamp ts = CreateAutoTimestamp.create(DateTime.parse("2019-09-9T11:39:00Z"));
|
|
||||||
TestEntity ent = new TestEntity("myinst", ts);
|
|
||||||
|
|
||||||
insertInDb(ent);
|
|
||||||
TestEntity result =
|
|
||||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "myinst"));
|
|
||||||
assertThat(result).isEqualTo(new TestEntity("myinst", ts));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAutoInitialization() {
|
|
||||||
CreateAutoTimestamp ts = CreateAutoTimestamp.create(null);
|
|
||||||
TestEntity ent = new TestEntity("autoinit", ts);
|
|
||||||
|
|
||||||
insertInDb(ent);
|
|
||||||
|
|
||||||
TestEntity result =
|
|
||||||
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "autoinit"));
|
|
||||||
assertThat(result.cat.getTimestamp()).isEqualTo(fakeClock.nowUtc());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
|
|
||||||
@EntityForTesting
|
|
||||||
public static class TestEntity extends ImmutableObject {
|
|
||||||
|
|
||||||
@Id String name;
|
|
||||||
|
|
||||||
CreateAutoTimestamp cat;
|
|
||||||
|
|
||||||
public TestEntity() {}
|
|
||||||
|
|
||||||
TestEntity(String name, CreateAutoTimestamp cat) {
|
|
||||||
this.name = name;
|
|
||||||
this.cat = cat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -261,11 +261,11 @@ td.section {
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="property_name">generated on</td>
|
<td class="property_name">generated on</td>
|
||||||
<td class="property_value">2021-09-14 16:11:21.688911</td>
|
<td class="property_value">2021-11-19 21:30:39.304221</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="property_name">last flyway file</td>
|
<td class="property_name">last flyway file</td>
|
||||||
<td id="lastFlywayFile" class="property_value">V102__add_indexes_to_domain_history_sub_tables.sql</td>
|
<td id="lastFlywayFile" class="property_value">V103__creation_time_not_null.sql</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -284,7 +284,7 @@ td.section {
|
||||||
generated on
|
generated on
|
||||||
</text>
|
</text>
|
||||||
<text text-anchor="start" x="4055.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
<text text-anchor="start" x="4055.5" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||||
2021-09-14 16:11:21.688911
|
2021-11-19 21:30:39.304221
|
||||||
</text>
|
</text>
|
||||||
<polygon fill="none" stroke="#888888" points="3968,-4 3968,-44 4233,-44 4233,-4 3968,-4" /> <!-- allocationtoken_a08ccbef -->
|
<polygon fill="none" stroke="#888888" points="3968,-4 3968,-44 4233,-44 4233,-4 3968,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
|
|
|
@ -261,11 +261,11 @@ td.section {
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="property_name">generated on</td>
|
<td class="property_name">generated on</td>
|
||||||
<td class="property_value">2021-09-14 16:11:19.580097</td>
|
<td class="property_value">2021-11-19 21:30:37.273092</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="property_name">last flyway file</td>
|
<td class="property_name">last flyway file</td>
|
||||||
<td id="lastFlywayFile" class="property_value">V102__add_indexes_to_domain_history_sub_tables.sql</td>
|
<td id="lastFlywayFile" class="property_value">V103__creation_time_not_null.sql</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -284,7 +284,7 @@ td.section {
|
||||||
generated on
|
generated on
|
||||||
</text>
|
</text>
|
||||||
<text text-anchor="start" x="4755.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
<text text-anchor="start" x="4755.52" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||||
2021-09-14 16:11:19.580097
|
2021-11-19 21:30:37.273092
|
||||||
</text>
|
</text>
|
||||||
<polygon fill="none" stroke="#888888" points="4668.02,-4 4668.02,-44 4933.02,-44 4933.02,-4 4668.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
<polygon fill="none" stroke="#888888" points="4668.02,-4 4668.02,-44 4933.02,-44 4933.02,-4 4668.02,-4" /> <!-- allocationtoken_a08ccbef -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
|
@ -1365,7 +1365,7 @@ td.section {
|
||||||
<text text-anchor="start" x="311" y="-2137.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
<text text-anchor="start" x="311" y="-2137.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||||
</text>
|
</text>
|
||||||
<text text-anchor="start" x="319" y="-2137.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
<text text-anchor="start" x="319" y="-2137.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||||
timestamptz
|
timestamptz not null
|
||||||
</text>
|
</text>
|
||||||
<text text-anchor="start" x="11" y="-2118.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
<text text-anchor="start" x="11" y="-2118.3" font-family="Helvetica,sans-Serif" font-size="14.00">
|
||||||
drive_folder_id
|
drive_folder_id
|
||||||
|
@ -12184,7 +12184,7 @@ td.section {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="spacer"></td>
|
<td class="spacer"></td>
|
||||||
<td class="minwidth">creation_time</td>
|
<td class="minwidth">creation_time</td>
|
||||||
<td class="minwidth">timestamptz</td>
|
<td class="minwidth">timestamptz not null</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="spacer"></td>
|
<td class="spacer"></td>
|
||||||
|
|
|
@ -100,3 +100,4 @@ V99__drop_kms_secret_table.sql
|
||||||
V100__database_migration_schedule.sql
|
V100__database_migration_schedule.sql
|
||||||
V101__domain_add_dns_refresh_request_time.sql
|
V101__domain_add_dns_refresh_request_time.sql
|
||||||
V102__add_indexes_to_domain_history_sub_tables.sql
|
V102__add_indexes_to_domain_history_sub_tables.sql
|
||||||
|
V103__creation_time_not_null.sql
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
-- Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||||
|
--
|
||||||
|
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
-- you may not use this file except in compliance with the License.
|
||||||
|
-- You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing, software
|
||||||
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
-- See the License for the specific language governing permissions and
|
||||||
|
-- limitations under the License.
|
||||||
|
|
||||||
|
ALTER TABLE "Registrar" ALTER COLUMN creation_time SET NOT NULL;
|
|
@ -572,7 +572,7 @@
|
||||||
client_certificate text,
|
client_certificate text,
|
||||||
client_certificate_hash text,
|
client_certificate_hash text,
|
||||||
contacts_require_syncing boolean not null,
|
contacts_require_syncing boolean not null,
|
||||||
creation_time timestamptz,
|
creation_time timestamptz not null,
|
||||||
drive_folder_id text,
|
drive_folder_id text,
|
||||||
email_address text,
|
email_address text,
|
||||||
failover_client_certificate text,
|
failover_client_certificate text,
|
||||||
|
|
|
@ -763,7 +763,7 @@ CREATE TABLE public."Registrar" (
|
||||||
client_certificate text,
|
client_certificate text,
|
||||||
client_certificate_hash text,
|
client_certificate_hash text,
|
||||||
contacts_require_syncing boolean NOT NULL,
|
contacts_require_syncing boolean NOT NULL,
|
||||||
creation_time timestamp with time zone,
|
creation_time timestamp with time zone NOT NULL,
|
||||||
drive_folder_id text,
|
drive_folder_id text,
|
||||||
email_address text,
|
email_address text,
|
||||||
failover_client_certificate text,
|
failover_client_certificate text,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue