mirror of
https://github.com/google/nomulus
synced 2026-02-08 14:00:32 +00:00
Delete EntityGroupRoot (#1776)
This commit is contained in:
@@ -51,7 +51,6 @@ public class ClassPathManagerTest {
|
||||
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
|
||||
assertThat(ClassPathManager.getClass("EppResourceIndexBucket"))
|
||||
.isEqualTo(EppResourceIndexBucket.class);
|
||||
assertThat(ClassPathManager.getClass("EntityGroupRoot")).isEqualTo(EntityGroupRoot.class);
|
||||
assertThat(ClassPathManager.getClass("Domain")).isEqualTo(Domain.class);
|
||||
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
|
||||
assertThat(ClassPathManager.getClass("ForeignKeyHostIndex"))
|
||||
@@ -100,7 +99,6 @@ public class ClassPathManagerTest {
|
||||
.isEqualTo("GaeUserIdConverter");
|
||||
assertThat(ClassPathManager.getClassName(EppResourceIndexBucket.class))
|
||||
.isEqualTo("EppResourceIndexBucket");
|
||||
assertThat(ClassPathManager.getClassName(EntityGroupRoot.class)).isEqualTo("EntityGroupRoot");
|
||||
assertThat(ClassPathManager.getClassName(Domain.class)).isEqualTo("Domain");
|
||||
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
|
||||
assertThat(ClassPathManager.getClassName(ForeignKeyHostIndex.class))
|
||||
|
||||
@@ -1,348 +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.ofy;
|
||||
|
||||
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static google.registry.model.ofy.Ofy.getBaseEntityClassFromEntityOrKey;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import com.google.appengine.api.datastore.DatastoreFailureException;
|
||||
import com.google.appengine.api.datastore.DatastoreTimeoutException;
|
||||
import com.google.appengine.api.datastore.Entity;
|
||||
import com.google.appengine.api.taskqueue.TransientFailureException;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.annotation.OnSave;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.function.Supplier;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for our wrapper around Objectify. */
|
||||
@Disabled
|
||||
public class OfyTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2000-01-01TZ"));
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withCloudSql().withClock(fakeClock).build();
|
||||
|
||||
/** An entity to use in save and delete tests. */
|
||||
private HistoryEntry someObject;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
someObject =
|
||||
new ContactHistory.Builder()
|
||||
.setRegistrarId("clientid")
|
||||
.setModificationTime(START_OF_TIME)
|
||||
.setType(HistoryEntry.Type.CONTACT_CREATE)
|
||||
.setContact(newContact("parentContact"))
|
||||
.setTrid(Trid.create("client", "server"))
|
||||
.setXmlBytes("<xml></xml>".getBytes(UTF_8))
|
||||
.build();
|
||||
// This can't be initialized earlier because namespaces need the AppEngineExtension to work.
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSavingKeyTwiceInOneCall() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> ofy().transact(() -> auditedOfy().save().entities(someObject, someObject)));
|
||||
}
|
||||
|
||||
/** Simple entity class with lifecycle callbacks. */
|
||||
@com.googlecode.objectify.annotation.Entity
|
||||
public static class LifecycleObject extends ImmutableObject {
|
||||
|
||||
@Parent Key<?> parent = getCrossTldKey();
|
||||
|
||||
@Id long id = 1;
|
||||
|
||||
boolean onLoadCalled;
|
||||
boolean onSaveCalled;
|
||||
|
||||
@OnLoad
|
||||
public void load() {
|
||||
onLoadCalled = true;
|
||||
}
|
||||
|
||||
@OnSave
|
||||
public void save() {
|
||||
onSaveCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLifecycleCallbacks_loadFromEntity() {
|
||||
auditedOfy().factory().register(LifecycleObject.class);
|
||||
LifecycleObject object = new LifecycleObject();
|
||||
Entity entity = auditedOfy().save().toEntity(object);
|
||||
assertThat(object.onSaveCalled).isTrue();
|
||||
assertThat(auditedOfy().load().<LifecycleObject>fromEntity(entity).onLoadCalled).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLifecycleCallbacks_loadFromDatastore() {
|
||||
auditedOfy().factory().register(LifecycleObject.class);
|
||||
final LifecycleObject object = new LifecycleObject();
|
||||
ofy().transact(() -> auditedOfy().save().entity(object).now());
|
||||
assertThat(object.onSaveCalled).isTrue();
|
||||
auditedOfy().clearSessionCache();
|
||||
assertThat(auditedOfy().load().entity(object).now().onLoadCalled).isTrue();
|
||||
}
|
||||
|
||||
/** Avoid regressions of b/21309102 where transaction time did not change on each retry. */
|
||||
@Test
|
||||
void testTransact_getsNewTimestampOnEachTry() {
|
||||
ofy()
|
||||
.transact(
|
||||
new Runnable() {
|
||||
|
||||
DateTime firstAttemptTime;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (firstAttemptTime == null) {
|
||||
// Sleep a bit to ensure that the next attempt is at a new millisecond.
|
||||
firstAttemptTime = ofy().getTransactionTime();
|
||||
sleepUninterruptibly(java.time.Duration.ofMillis(10));
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
assertThat(ofy().getTransactionTime()).isGreaterThan(firstAttemptTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransact_transientFailureException_retries() {
|
||||
assertThat(
|
||||
ofy()
|
||||
.transact(
|
||||
new Supplier<Integer>() {
|
||||
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
count++;
|
||||
if (count == 3) {
|
||||
return count;
|
||||
}
|
||||
throw new TransientFailureException("");
|
||||
}
|
||||
}))
|
||||
.isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransact_datastoreTimeoutException_noManifest_retries() {
|
||||
assertThat(
|
||||
ofy()
|
||||
.transact(
|
||||
new Supplier<Integer>() {
|
||||
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
// We don't write anything in this transaction, so there is no commit log
|
||||
// manifest.
|
||||
// Therefore it's always safe to retry since nothing got written.
|
||||
count++;
|
||||
if (count == 3) {
|
||||
return count;
|
||||
}
|
||||
throw new DatastoreTimeoutException("");
|
||||
}
|
||||
}))
|
||||
.isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransact_datastoreTimeoutException_manifestNotWrittenToDatastore_retries() {
|
||||
assertThat(
|
||||
ofy()
|
||||
.transact(
|
||||
new Supplier<Integer>() {
|
||||
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
// There will be something in the manifest now, but it won't be committed if
|
||||
// we throw.
|
||||
auditedOfy().save().entity(someObject.asHistoryEntry());
|
||||
count++;
|
||||
if (count == 3) {
|
||||
return count;
|
||||
}
|
||||
throw new DatastoreTimeoutException("");
|
||||
}
|
||||
}))
|
||||
.isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransact_datastoreTimeoutException_manifestWrittenToDatastore_returnsSuccess() {
|
||||
// A work unit that throws if it is ever retried.
|
||||
Supplier work =
|
||||
new Supplier<Void>() {
|
||||
boolean firstCallToVrun = true;
|
||||
|
||||
@Override
|
||||
public Void get() {
|
||||
if (firstCallToVrun) {
|
||||
firstCallToVrun = false;
|
||||
auditedOfy().save().entity(someObject.asHistoryEntry());
|
||||
return null;
|
||||
}
|
||||
fail("Shouldn't have retried.");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
// 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;
|
||||
|
||||
@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().
|
||||
auditedOfy().transactCommitLoggedWork(commitLoggedWork);
|
||||
}
|
||||
|
||||
void doReadOnlyRetryTest(final RuntimeException e) {
|
||||
assertThat(
|
||||
ofy()
|
||||
.transactNewReadOnly(
|
||||
new Supplier<Integer>() {
|
||||
|
||||
int count = 0;
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
count++;
|
||||
if (count == 3) {
|
||||
return count;
|
||||
}
|
||||
throw new TransientFailureException("");
|
||||
}
|
||||
}))
|
||||
.isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactNewReadOnly_transientFailureException_retries() {
|
||||
doReadOnlyRetryTest(new TransientFailureException(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactNewReadOnly_datastoreTimeoutException_retries() {
|
||||
doReadOnlyRetryTest(new DatastoreTimeoutException(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactNewReadOnly_datastoreFailureException_retries() {
|
||||
doReadOnlyRetryTest(new DatastoreFailureException(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBaseEntityClassFromEntityOrKey_regularEntity() {
|
||||
Contact contact = newContact("testcontact");
|
||||
assertThat(getBaseEntityClassFromEntityOrKey(contact)).isEqualTo(Contact.class);
|
||||
assertThat(getBaseEntityClassFromEntityOrKey(Key.create(contact))).isEqualTo(Contact.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBaseEntityClassFromEntityOrKey_unregisteredEntity() {
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> getBaseEntityClassFromEntityOrKey(new SystemClock()));
|
||||
assertThat(thrown).hasMessageThat().contains("SystemClock");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_getBaseEntityClassFromEntityOrKey_unregisteredEntityKey() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
getBaseEntityClassFromEntityOrKey(
|
||||
Key.create(
|
||||
com.google.appengine.api.datastore.KeyFactory.createKey(
|
||||
"UnknownKind", 1))));
|
||||
assertThat(thrown).hasMessageThat().contains("UnknownKind");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_doWithFreshSessionCache() {
|
||||
auditedOfy().saveWithoutBackup().entity(someObject.asHistoryEntry()).now();
|
||||
final HistoryEntry modifiedObject =
|
||||
someObject.asBuilder().setModificationTime(END_OF_TIME).build();
|
||||
// Mutate the saved objected, bypassing the Objectify session cache.
|
||||
getDatastoreService()
|
||||
.put(auditedOfy().saveWithoutBackup().toEntity(modifiedObject.asHistoryEntry()));
|
||||
// Normal loading should come from the session cache and shouldn't reflect the mutation.
|
||||
assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry());
|
||||
// Loading inside doWithFreshSessionCache() should reflect the mutation.
|
||||
boolean ran =
|
||||
auditedOfy()
|
||||
.doWithFreshSessionCache(
|
||||
() -> {
|
||||
assertAboutImmutableObjects()
|
||||
.that(auditedOfy().load().entity(someObject).now())
|
||||
.isEqualExceptFields(modifiedObject, "contactBase");
|
||||
return true;
|
||||
});
|
||||
assertThat(ran).isTrue();
|
||||
// Test the normal loading again to verify that we've restored the original session unchanged.
|
||||
assertThat(auditedOfy().load().entity(someObject).now()).isEqualTo(someObject.asHistoryEntry());
|
||||
}
|
||||
}
|
||||
@@ -15,20 +15,16 @@
|
||||
package google.registry.persistence;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory.DomainHistoryId;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import javax.persistence.Transient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
@@ -74,7 +70,6 @@ class DomainHistoryVKeyTest {
|
||||
@Entity
|
||||
@javax.persistence.Entity(name = "TestEntity")
|
||||
private static class TestEntity extends ImmutableObject {
|
||||
@Transient @Parent Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||
|
||||
@Id @javax.persistence.Id String id = "id";
|
||||
|
||||
|
||||
@@ -162,18 +162,14 @@ class VKeyTest {
|
||||
void testStringify_sqlAndOfyVKey() {
|
||||
assertThat(
|
||||
VKey.create(TestObject.class, "foo", Key.create(TestObject.create("foo"))).stringify())
|
||||
.isEqualTo(
|
||||
"kind:TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0cjELEg9FbnRpdH"
|
||||
+ "lHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M");
|
||||
.isEqualTo("kind:TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringify_asymmetricVKey() {
|
||||
assertThat(
|
||||
VKey.create(TestObject.class, "test", Key.create(TestObject.create("foo"))).stringify())
|
||||
.isEqualTo(
|
||||
"kind:TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0cjELEg9FbnRpd"
|
||||
+ "HlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M");
|
||||
.isEqualTo("kind:TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M");
|
||||
}
|
||||
|
||||
/** Test create() via different vkey string representations. */
|
||||
|
||||
@@ -14,25 +14,18 @@
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
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.persistence.VKey;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
/** A test model object that can be persisted in any entity group. */
|
||||
@Entity
|
||||
@javax.persistence.Entity
|
||||
public class TestObject extends ImmutableObject {
|
||||
|
||||
@Parent @Transient Key<EntityGroupRoot> parent;
|
||||
|
||||
@Id @javax.persistence.Id String id;
|
||||
|
||||
String field;
|
||||
@@ -58,14 +51,9 @@ public class TestObject extends ImmutableObject {
|
||||
}
|
||||
|
||||
public static TestObject create(String id, String field) {
|
||||
return create(id, field, getCrossTldKey());
|
||||
}
|
||||
|
||||
public static TestObject create(String id, String field, Key<EntityGroupRoot> parent) {
|
||||
TestObject instance = new TestObject();
|
||||
instance.id = id;
|
||||
instance.field = field;
|
||||
instance.parent = parent;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
class google.registry.model.common.EntityGroupRoot {
|
||||
@Id java.lang.String id;
|
||||
}
|
||||
class google.registry.model.common.GaeUserIdConverter {
|
||||
@Id long id;
|
||||
com.google.appengine.api.users.User user;
|
||||
@@ -391,7 +388,6 @@ enum google.registry.model.reporting.HistoryEntry$Type {
|
||||
}
|
||||
class google.registry.model.server.ServerSecret {
|
||||
@Id long id;
|
||||
@Parent com.googlecode.objectify.Key<google.registry.model.common.EntityGroupRoot> parent;
|
||||
long leastSignificant;
|
||||
long mostSignificant;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user