Include Cursor in the initial SQL population (#1323)

The test for this also required a bit of a fix in the Cursor scope
initialization. If you persist a Key<?> in some object in Datastore, it
persists just the standard data you'd expect, basically the parent, the
kind, and the object's ID/name. Then, when you load it back in from
Datastore it uses the app ID of whatever environment you're loading in
(the Key contains this info even though it isn't included in the
toString() of Key)

If you persist the websafe string format of a Key (which is what we do
for Cursors), it includes the app ID so when you load it, it contains
the old app ID not the new one (if the app ID has changed).

In the pipelines, we use the standard default environment which has a
different app ID from the test environment that's set up by the
DatastoreEntityExtension.

As a result, we should check in Cursor to see if the key is *any*
cross-tld-key, rather than the exact one that exists within this app
environment.
This commit is contained in:
gbrodman 2021-09-19 09:23:02 -04:00 committed by GitHub
parent b4f6280d6f
commit 92b83a9d7a
4 changed files with 574 additions and 431 deletions

View file

@ -25,6 +25,7 @@ import google.registry.backup.VersionedEntity;
import google.registry.beam.common.RegistryJpaIO;
import google.registry.beam.initsql.Transforms.RemoveDomainBaseForeignKeys;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.Cursor;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken;
@ -62,6 +63,7 @@ import org.joda.time.DateTime;
* <ol>
* <li>{@link Registry}: Assumes that {@code PremiumList} and {@code ReservedList} have been set
* up in the SQL database.
* <li>{@link Cursor}: Logically can depend on {@code Registry}, but without foreign key.
* <li>{@link Registrar}: Logically depends on {@code Registry}, Foreign key not modeled yet.
* <li>{@link ContactResource}: references {@code Registrar}
* <li>{@link RegistrarContact}: references {@code Registrar}.
@ -101,7 +103,11 @@ public class InitSqlPipeline implements Serializable {
*/
private static final ImmutableList<Class<?>> PHASE_ONE_ORDERED =
ImmutableList.of(
Registry.class, Registrar.class, ContactResource.class, RegistrarContact.class);
Registry.class,
Cursor.class,
Registrar.class,
ContactResource.class,
RegistrarContact.class);
/**
* Datastore kinds to be written to the SQL database after the cleansed version of {@link

View file

@ -220,10 +220,10 @@ public class Cursor extends ImmutableObject implements DatastoreAndSqlEntity {
private static String getScopeFromId(String id) {
List<String> idSplit = Splitter.on('_').splitToList(id);
// The "parent" is always the crossTldKey; in order to find the scope (either Registry or
// cross-tld-key) we have to parse the part of the ID
// The key is always either the cross-tld-key or the key of a TLD (whose parent is the
// cross-tld-key).
Key<?> scopeKey = Key.valueOf(idSplit.get(0));
return scopeKey.equals(getCrossTldKey()) ? GLOBAL : scopeKey.getName();
return scopeKey.getParent() == null ? GLOBAL : scopeKey.getName();
}
private static CursorType getTypeFromId(String id) {

View file

@ -17,6 +17,8 @@ package google.registry.beam.initsql;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.model.common.Cursor.CursorType.BRDA;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.newRegistry;
@ -34,6 +36,7 @@ import google.registry.flows.domain.DomainFlowUtils;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.common.Cursor;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DomainAuthInfo;
@ -86,6 +89,7 @@ class InitSqlPipelineTest {
private static final ImmutableList<Class<?>> ALL_KINDS =
ImmutableList.of(
Registry.class,
Cursor.class,
Registrar.class,
ContactResource.class,
RegistrarContact.class,
@ -131,6 +135,9 @@ class InitSqlPipelineTest {
private transient DomainHistory historyEntry;
private transient Cursor globalCursor;
private transient Cursor tldCursor;
@BeforeEach
void beforeEach() throws Exception {
try (BackupTestStore store = new BackupTestStore(fakeClock)) {
@ -304,6 +311,8 @@ class InitSqlPipelineTest {
.setRecurringEventKey(recurringBillEvent.createVKey())
.setParent(historyEntryKey)
.build());
globalCursor = persistResource(Cursor.createGlobal(RECURRING_BILLING, fakeClock.nowUtc()));
tldCursor = persistResource(Cursor.create(BRDA, fakeClock.nowUtc(), Registry.get("com")));
exportDir = store.export(exportRootDir.getAbsolutePath(), ALL_KINDS, ImmutableSet.of());
commitLogDir = Files.createDirectory(tmpDir.resolve("commits")).toFile();
fakeClock.advanceOneMilli();
@ -332,6 +341,9 @@ class InitSqlPipelineTest {
.comparingElementsUsing(immutableObjectCorrespondence("revisions", "updateTimestamp"))
.containsExactly(contact1, contact2);
assertDomainEquals(jpaTm().transact(() -> jpaTm().loadByKey(domain.createVKey())), domain);
assertThat(jpaTm().transact(() -> jpaTm().loadAllOf(Cursor.class)))
.comparingElementsUsing(immutableObjectCorrespondence())
.containsExactly(globalCursor, tldCursor);
}
}