mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 11:16:04 +02:00
Implement DatastoreEntity/SqlEntity for many more classes (#788)
* Implement DatastoreEntity/SqlEntity for many more classes We still have many more classes to go, but this gets us closer to guaranteeing that we can convert from Datastore to SQL objects and back again. * Shift SqlEntity impl to HistoryEntry
This commit is contained in:
parent
a86fcf79f7
commit
fb7ba80b86
17 changed files with 164 additions and 81 deletions
|
@ -21,6 +21,7 @@ import static org.joda.time.DateTimeZone.UTC;
|
|||
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -38,6 +39,7 @@ public class CreateAutoTimestampTest {
|
|||
|
||||
/** Timestamped class. */
|
||||
@Entity(name = "CatTestEntity")
|
||||
@EntityForTesting
|
||||
public static class TestObject extends CrossTldSingleton {
|
||||
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.google.common.collect.Iterables;
|
|||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.util.ArrayDeque;
|
||||
|
@ -279,6 +280,7 @@ public class ImmutableObjectTest {
|
|||
|
||||
/** Simple subclass of ImmutableObject. */
|
||||
@Entity
|
||||
@EntityForTesting
|
||||
public static class ValueObject extends ImmutableObject {
|
||||
@Id
|
||||
long id;
|
||||
|
|
|
@ -21,6 +21,7 @@ import static org.joda.time.DateTimeZone.UTC;
|
|||
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -38,6 +39,7 @@ public class UpdateAutoTimestampTest {
|
|||
|
||||
/** Timestamped class. */
|
||||
@Entity(name = "UatTestEntity")
|
||||
@EntityForTesting
|
||||
public static class UpdateAutoTimestampTestObject extends CrossTldSingleton {
|
||||
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import google.registry.model.contact.ContactResource;
|
|||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatastoreHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
|
@ -69,13 +70,14 @@ public class OfyTest {
|
|||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
someObject = new HistoryEntry.Builder()
|
||||
.setClientId("client id")
|
||||
.setModificationTime(START_OF_TIME)
|
||||
.setParent(persistActiveContact("parentContact"))
|
||||
.setTrid(Trid.create("client", "server"))
|
||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||
.build();
|
||||
someObject =
|
||||
new HistoryEntry.Builder()
|
||||
.setClientId("client id")
|
||||
.setModificationTime(START_OF_TIME)
|
||||
.setParent(persistActiveContact("parentContact"))
|
||||
.setTrid(Trid.create("client", "server"))
|
||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||
.build();
|
||||
// This can't be initialized earlier because namespaces need the AppEngineRule to work.
|
||||
}
|
||||
|
||||
|
@ -111,8 +113,7 @@ public class OfyTest {
|
|||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy().save().entity(someObject);
|
||||
ofy().save().entity(someObject);
|
||||
|
@ -126,8 +127,7 @@ public class OfyTest {
|
|||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy().delete().entity(someObject);
|
||||
ofy().delete().entity(someObject);
|
||||
|
@ -141,8 +141,7 @@ public class OfyTest {
|
|||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy().save().entity(someObject);
|
||||
ofy().delete().entity(someObject);
|
||||
|
@ -156,8 +155,7 @@ public class OfyTest {
|
|||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
tm()
|
||||
.transact(
|
||||
tm().transact(
|
||||
() -> {
|
||||
ofy().delete().entity(someObject);
|
||||
ofy().save().entity(someObject);
|
||||
|
@ -174,13 +172,12 @@ public class OfyTest {
|
|||
|
||||
/** Simple entity class with lifecycle callbacks. */
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
@EntityForTesting
|
||||
public static class LifecycleObject extends ImmutableObject {
|
||||
|
||||
@Parent
|
||||
Key<?> parent = getCrossTldKey();
|
||||
@Parent Key<?> parent = getCrossTldKey();
|
||||
|
||||
@Id
|
||||
long id = 1;
|
||||
@Id long id = 1;
|
||||
|
||||
boolean onLoadCalled;
|
||||
boolean onSaveCalled;
|
||||
|
@ -218,20 +215,22 @@ public class OfyTest {
|
|||
/** Avoid regressions of b/21309102 where transaction time did not change on each retry. */
|
||||
@Test
|
||||
void testTransact_getsNewTimestampOnEachTry() {
|
||||
tm().transact(new Runnable() {
|
||||
tm().transact(
|
||||
new Runnable() {
|
||||
|
||||
DateTime firstAttemptTime;
|
||||
DateTime firstAttemptTime;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (firstAttemptTime == null) {
|
||||
// Sleep a bit to ensure that the next attempt is at a new millisecond.
|
||||
firstAttemptTime = tm().getTransactionTime();
|
||||
sleepUninterruptibly(10, MILLISECONDS);
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
assertThat(tm().getTransactionTime()).isGreaterThan(firstAttemptTime);
|
||||
}});
|
||||
@Override
|
||||
public void run() {
|
||||
if (firstAttemptTime == null) {
|
||||
// Sleep a bit to ensure that the next attempt is at a new millisecond.
|
||||
firstAttemptTime = tm().getTransactionTime();
|
||||
sleepUninterruptibly(10, MILLISECONDS);
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
assertThat(tm().getTransactionTime()).isGreaterThan(firstAttemptTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -319,17 +318,19 @@ public class OfyTest {
|
|||
}
|
||||
};
|
||||
// A commit logged work that throws on the first attempt to get its result.
|
||||
CommitLoggedWork<Void> commitLoggedWork = new CommitLoggedWork<Void>(work, new SystemClock()) {
|
||||
boolean firstCallToGetResult = true;
|
||||
CommitLoggedWork<Void> commitLoggedWork =
|
||||
new CommitLoggedWork<Void>(work, new SystemClock()) {
|
||||
boolean firstCallToGetResult = true;
|
||||
|
||||
@Override
|
||||
public Void getResult() {
|
||||
if (firstCallToGetResult) {
|
||||
firstCallToGetResult = false;
|
||||
throw new DatastoreTimeoutException("");
|
||||
}
|
||||
return null;
|
||||
}};
|
||||
@Override
|
||||
public Void getResult() {
|
||||
if (firstCallToGetResult) {
|
||||
firstCallToGetResult = false;
|
||||
throw new DatastoreTimeoutException("");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
// Despite the DatastoreTimeoutException in the first call to getResult(), this should succeed
|
||||
// without retrying. If a retry is triggered, the test should fail due to the call to fail().
|
||||
ofy().transactCommitLoggedWork(commitLoggedWork);
|
||||
|
@ -381,8 +382,7 @@ public class OfyTest {
|
|||
void test_getBaseEntityClassFromEntityOrKey_subclassEntity() {
|
||||
DomainBase domain = DatastoreHelper.newDomainBase("test.tld");
|
||||
assertThat(getBaseEntityClassFromEntityOrKey(domain)).isEqualTo(DomainBase.class);
|
||||
assertThat(getBaseEntityClassFromEntityOrKey(Key.create(domain)))
|
||||
.isEqualTo(DomainBase.class);
|
||||
assertThat(getBaseEntityClassFromEntityOrKey(Key.create(domain))).isEqualTo(DomainBase.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.googlecode.objectify.annotation.Entity;
|
|||
import google.registry.model.common.CrossTldSingleton;
|
||||
import google.registry.model.ofy.CommitLogManifest;
|
||||
import google.registry.model.ofy.Ofy;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
|
@ -42,6 +43,7 @@ public class CommitLogRevisionsTranslatorFactoryTest {
|
|||
private static final DateTime START_TIME = DateTime.parse("2000-01-01TZ");
|
||||
|
||||
@Entity(name = "ClrtfTestEntity")
|
||||
@EntityForTesting
|
||||
public static class TestObject extends CrossTldSingleton {
|
||||
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
|
||||
}
|
||||
|
@ -73,11 +75,11 @@ public class CommitLogRevisionsTranslatorFactoryTest {
|
|||
|
||||
@Test
|
||||
void testSave_doesNotMutateOriginalResource() {
|
||||
TestObject object = new TestObject();
|
||||
save(object);
|
||||
assertThat(object.revisions).isEmpty();
|
||||
assertThat(reload().revisions).isNotEmpty();
|
||||
}
|
||||
TestObject object = new TestObject();
|
||||
save(object);
|
||||
assertThat(object.revisions).isEmpty();
|
||||
assertThat(reload().revisions).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_translatorAddsKeyToCommitLogToField() {
|
||||
|
@ -149,8 +151,10 @@ public class CommitLogRevisionsTranslatorFactoryTest {
|
|||
com.google.appengine.api.datastore.Entity entity =
|
||||
tm().transactNewReadOnly(() -> ofy().save().toEntity(reload()));
|
||||
assertThat(entity.getProperties().keySet()).containsExactly("revisions.key", "revisions.value");
|
||||
assertThat(entity.getProperties()).containsEntry(
|
||||
"revisions.key", ImmutableList.of(START_TIME.toDate(), START_TIME.plusDays(1).toDate()));
|
||||
assertThat(entity.getProperties())
|
||||
.containsEntry(
|
||||
"revisions.key",
|
||||
ImmutableList.of(START_TIME.toDate(), START_TIME.plusDays(1).toDate()));
|
||||
assertThat(entity.getProperty("revisions.value")).isInstanceOf(List.class);
|
||||
assertThat(((List<Object>) entity.getProperty("revisions.value")).get(0))
|
||||
.isInstanceOf(com.google.appengine.api.datastore.Key.class);
|
||||
|
|
|
@ -41,24 +41,32 @@ public class EntityTest {
|
|||
new ClassGraph().enableAnnotationInfo().whitelistPackages("google.registry").scan()) {
|
||||
// All javax.persistence entities must implement SqlEntity and vice versa
|
||||
ImmutableSet<String> javaxPersistenceClasses =
|
||||
getClassNames(
|
||||
scanResult.getClassesWithAnnotation(javax.persistence.Entity.class.getName()));
|
||||
getAllClassesWithAnnotation(scanResult, javax.persistence.Entity.class.getName());
|
||||
ImmutableSet<String> sqlEntityClasses =
|
||||
getClassNames(scanResult.getClassesImplementing(SqlEntity.class.getName()));
|
||||
assertThat(javaxPersistenceClasses).isEqualTo(sqlEntityClasses);
|
||||
assertThat(sqlEntityClasses).containsExactlyElementsIn(javaxPersistenceClasses);
|
||||
|
||||
// All com.googlecode.objectify.annotation.Entity classes must implement DatastoreEntity and
|
||||
// vice versa
|
||||
// All com.googlecode.objectify entities must implement DatastoreEntity and vice versa
|
||||
ImmutableSet<String> objectifyClasses =
|
||||
getClassNames(
|
||||
scanResult.getClassesWithAnnotation(
|
||||
com.googlecode.objectify.annotation.Entity.class.getName()));
|
||||
getAllClassesWithAnnotation(
|
||||
scanResult, com.googlecode.objectify.annotation.Entity.class.getName());
|
||||
ImmutableSet<String> datastoreEntityClasses =
|
||||
getClassNames(scanResult.getClassesImplementing(DatastoreEntity.class.getName()));
|
||||
assertThat(objectifyClasses).isEqualTo(datastoreEntityClasses);
|
||||
assertThat(datastoreEntityClasses).containsExactlyElementsIn(objectifyClasses);
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableSet<String> getAllClassesWithAnnotation(
|
||||
ScanResult scanResult, String annotation) {
|
||||
ImmutableSet.Builder<String> result = new ImmutableSet.Builder<>();
|
||||
ClassInfoList classesWithAnnotation = scanResult.getClassesWithAnnotation(annotation);
|
||||
result.addAll(getClassNames(classesWithAnnotation));
|
||||
classesWithAnnotation.stream()
|
||||
.map(ClassInfo::getSubclasses)
|
||||
.forEach(classInfoList -> result.addAll(getClassNames(classInfoList)));
|
||||
return result.build();
|
||||
}
|
||||
|
||||
private ImmutableSet<String> getClassNames(ClassInfoList classInfoList) {
|
||||
return classInfoList.stream()
|
||||
.filter(ClassInfo::isStandardClass)
|
||||
|
|
|
@ -23,11 +23,11 @@ import com.googlecode.objectify.annotation.Parent;
|
|||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.VirtualEntity;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.schema.replay.EntityTest.EntityForTesting;
|
||||
|
||||
/**
|
||||
* A test model object that can be persisted in any entity group.
|
||||
*/
|
||||
/** A test model object that can be persisted in any entity group. */
|
||||
@Entity
|
||||
@EntityForTesting
|
||||
public class TestObject extends ImmutableObject {
|
||||
|
||||
@Parent
|
||||
|
@ -65,6 +65,7 @@ public class TestObject extends ImmutableObject {
|
|||
/** A test @VirtualEntity model object, which should not be persisted. */
|
||||
@Entity
|
||||
@VirtualEntity
|
||||
@EntityForTesting
|
||||
public static class TestVirtualObject extends ImmutableObject {
|
||||
|
||||
@Id
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue