From 1d91a8e647235c780a2c575436d8d8ddae9d935f Mon Sep 17 00:00:00 2001 From: Shicong Huang Date: Tue, 17 Nov 2020 16:32:32 -0500 Subject: [PATCH] Use DomainHistoryVKey to restore symmetric VKey (#874) * Use DomainHistoryVKey to restore symmetric VKey * Rebase on HEAD --- .../flows/domain/DomainCreateFlow.java | 3 +- .../token/AllocationTokenFlowUtils.java | 5 +- .../registry/model/domain/DomainHistory.java | 4 +- .../model/domain/token/AllocationToken.java | 17 +- .../registry/model/ofy/ObjectifyService.java | 2 + .../model/reporting/HistoryEntry.java | 16 - .../DomainHistoryVKeyTranslatorFactory.java | 48 + .../translators/VKeyTranslatorFactory.java | 1 - .../persistence/DomainHistoryVKey.java | 62 + .../google/registry/persistence/VKey.java | 29 +- .../main/resources/META-INF/persistence.xml | 1 - .../flows/domain/DomainCheckFlowTest.java | 4 +- .../flows/domain/DomainCreateFlowTest.java | 8 +- .../token/AllocationTokenFlowUtilsTest.java | 4 +- .../domain/token/AllocationTokenTest.java | 7 +- .../VKeyTranslatorFactoryTest.java | 17 +- .../persistence/DomainHistoryVKeyTest.java | 97 ++ .../DeleteAllocationTokensCommandTest.java | 4 +- .../GenerateAllocationTokensCommandTest.java | 5 +- .../tools/GetAllocationTokenCommandTest.java | 4 +- .../google/registry/model/schema.txt | 9 +- .../sql/er_diagram/brief_er_diagram.html | 6 +- .../sql/er_diagram/full_er_diagram.html | 1111 +++++++++-------- db/src/main/resources/sql/flyway.txt | 1 + ...istory_id_for_redemption_history_entry.sql | 20 + .../sql/schema/db-schema.sql.generated | 2 + .../resources/sql/schema/nomulus.golden.sql | 5 +- 27 files changed, 871 insertions(+), 621 deletions(-) create mode 100644 core/src/main/java/google/registry/model/translators/DomainHistoryVKeyTranslatorFactory.java create mode 100644 core/src/main/java/google/registry/persistence/DomainHistoryVKey.java create mode 100644 core/src/test/java/google/registry/persistence/DomainHistoryVKeyTest.java create mode 100644 db/src/main/resources/sql/flyway/V78__add_history_id_for_redemption_history_entry.sql diff --git a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java index b514c2e57..6d2121021 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainCreateFlow.java @@ -110,6 +110,7 @@ import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; +import google.registry.persistence.DomainHistoryVKey; import google.registry.persistence.VKey; import google.registry.tmch.LordnTaskUtils; import java.util.Optional; @@ -371,7 +372,7 @@ public class DomainCreateFlow implements TransactionalFlow { && TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) { entitiesToSave.add( allocationTokenFlowUtils.redeemToken( - allocationToken.get(), HistoryEntry.createVKey(Key.create(historyEntry)))); + allocationToken.get(), DomainHistoryVKey.create(Key.create(historyEntry)))); } enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice); diff --git a/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java b/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java index 0111a2a91..a041c88a4 100644 --- a/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java @@ -32,8 +32,7 @@ import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.registry.Registry; -import google.registry.model.reporting.HistoryEntry; -import google.registry.persistence.VKey; +import google.registry.persistence.DomainHistoryVKey; import java.util.List; import java.util.Optional; import javax.inject.Inject; @@ -108,7 +107,7 @@ public class AllocationTokenFlowUtils { /** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */ public AllocationToken redeemToken( - AllocationToken token, VKey redemptionHistoryEntry) { + AllocationToken token, DomainHistoryVKey redemptionHistoryEntry) { checkArgument( TokenType.SINGLE_USE.equals(token.getTokenType()), "Only SINGLE_USE tokens can be marked as redeemed"); diff --git a/core/src/main/java/google/registry/model/domain/DomainHistory.java b/core/src/main/java/google/registry/model/domain/DomainHistory.java index cb389be0c..bf9c09d69 100644 --- a/core/src/main/java/google/registry/model/domain/DomainHistory.java +++ b/core/src/main/java/google/registry/model/domain/DomainHistory.java @@ -257,7 +257,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { } /** Class to represent the composite primary key of {@link DomainHistory} entity. */ - static class DomainHistoryId extends ImmutableObject implements Serializable { + public static class DomainHistoryId extends ImmutableObject implements Serializable { private String domainRepoId; @@ -266,7 +266,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { /** Hibernate requires this default constructor. */ private DomainHistoryId() {} - DomainHistoryId(String domainRepoId, long id) { + public DomainHistoryId(String domainRepoId, long id) { this.domainRepoId = domainRepoId; this.id = id; } diff --git a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java index 38394e3a6..50c07f68a 100644 --- a/core/src/main/java/google/registry/model/domain/token/AllocationToken.java +++ b/core/src/main/java/google/registry/model/domain/token/AllocationToken.java @@ -47,17 +47,19 @@ import google.registry.model.common.TimedTransitionProperty; import google.registry.model.common.TimedTransitionProperty.TimeMapper; import google.registry.model.common.TimedTransitionProperty.TimedTransition; import google.registry.model.reporting.HistoryEntry; +import google.registry.persistence.DomainHistoryVKey; import google.registry.persistence.VKey; import google.registry.persistence.WithStringVKey; import google.registry.schema.replay.DatastoreAndSqlEntity; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; import javax.persistence.Column; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Table; -import javax.persistence.Transient; import org.joda.time.DateTime; /** An entity representing an allocation token. */ @@ -106,8 +108,15 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas @javax.persistence.Id @Id String token; /** The key of the history entry for which the token was used. Null if not yet used. */ - // TODO(b/172848495): Remove the "Transient" when we can finally persist and restore this. - @Transient @Nullable @Index VKey redemptionHistoryEntry; + @Nullable + @Index + @AttributeOverrides({ + @AttributeOverride(name = "domainRepoId", column = @Column(name = "redemption_domain_repo_id")), + @AttributeOverride( + name = "domainHistoryId", + column = @Column(name = "redemption_domain_history_id")) + }) + DomainHistoryVKey redemptionHistoryEntry; /** The fully-qualified domain name that this token is limited to, if any. */ @Nullable @Index String domainName; @@ -282,7 +291,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas return this; } - public Builder setRedemptionHistoryEntry(VKey redemptionHistoryEntry) { + public Builder setRedemptionHistoryEntry(DomainHistoryVKey redemptionHistoryEntry) { getInstance().redemptionHistoryEntry = checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null"); return this; diff --git a/core/src/main/java/google/registry/model/ofy/ObjectifyService.java b/core/src/main/java/google/registry/model/ofy/ObjectifyService.java index f8e1b1db6..3f9fab7d9 100644 --- a/core/src/main/java/google/registry/model/ofy/ObjectifyService.java +++ b/core/src/main/java/google/registry/model/ofy/ObjectifyService.java @@ -42,6 +42,7 @@ import google.registry.model.translators.CidrAddressBlockTranslatorFactory; import google.registry.model.translators.CommitLogRevisionsTranslatorFactory; import google.registry.model.translators.CreateAutoTimestampTranslatorFactory; import google.registry.model.translators.CurrencyUnitTranslatorFactory; +import google.registry.model.translators.DomainHistoryVKeyTranslatorFactory; import google.registry.model.translators.DurationTranslatorFactory; import google.registry.model.translators.InetAddressTranslatorFactory; import google.registry.model.translators.ReadableInstantUtcTranslatorFactory; @@ -127,6 +128,7 @@ public class ObjectifyService { new CreateAutoTimestampTranslatorFactory(), new CurrencyUnitTranslatorFactory(), new DurationTranslatorFactory(), + new DomainHistoryVKeyTranslatorFactory(), new InetAddressTranslatorFactory(), new MoneyStringTranslatorFactory(), new ReadableInstantUtcTranslatorFactory(), diff --git a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java index 07861ab26..b612c4c2d 100644 --- a/core/src/main/java/google/registry/model/reporting/HistoryEntry.java +++ b/core/src/main/java/google/registry/model/reporting/HistoryEntry.java @@ -39,8 +39,6 @@ import google.registry.model.domain.Period; import google.registry.model.eppcommon.Trid; import google.registry.model.host.HostHistory; import google.registry.model.host.HostResource; -import google.registry.persistence.VKey; -import google.registry.persistence.WithStringVKey; import google.registry.schema.replay.DatastoreEntity; import google.registry.schema.replay.SqlEntity; import java.util.Set; @@ -60,7 +58,6 @@ import org.joda.time.DateTime; @ReportedOn @Entity @MappedSuperclass -@WithStringVKey // TODO(b/162229294): This should be resolved during the course of that bug @Access(AccessType.FIELD) public class HistoryEntry extends ImmutableObject implements Buildable, DatastoreEntity { @@ -281,19 +278,6 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords); } - public static VKey createVKey(Key key) { - // TODO(b/159207551): This will likely need some revision. As it stands, this method was - // introduced purely to facilitate testing of VKey specialization in VKeyTranslatorFactory. - // This class will likely require that functionality, though perhaps not this implementation of - // it. - // For now, just assume that the primary key of a history entry is comprised of the parent - // type, key and the object identifer. - return VKey.create( - HistoryEntry.class, - key.getParent().getKind() + "/" + key.getParent().getName() + "/" + key.getId(), - key); - } - @Override public Builder asBuilder() { return new Builder(clone(this)); diff --git a/core/src/main/java/google/registry/model/translators/DomainHistoryVKeyTranslatorFactory.java b/core/src/main/java/google/registry/model/translators/DomainHistoryVKeyTranslatorFactory.java new file mode 100644 index 000000000..3f1e4ef49 --- /dev/null +++ b/core/src/main/java/google/registry/model/translators/DomainHistoryVKeyTranslatorFactory.java @@ -0,0 +1,48 @@ +// Copyright 2020 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 com.google.appengine.api.datastore.Key; +import google.registry.persistence.DomainHistoryVKey; +import javax.annotation.Nullable; + +/** Translator factory for {@link DomainHistoryVKey}. */ +public class DomainHistoryVKeyTranslatorFactory + extends AbstractSimpleTranslatorFactory { + + public DomainHistoryVKeyTranslatorFactory() { + super(DomainHistoryVKey.class); + } + + @Override + SimpleTranslator createTranslator() { + return new SimpleTranslator() { + + @Nullable + @Override + public DomainHistoryVKey loadValue(@Nullable Key datastoreValue) { + return datastoreValue == null + ? null + : DomainHistoryVKey.create(com.googlecode.objectify.Key.create(datastoreValue)); + } + + @Nullable + @Override + public Key saveValue(@Nullable DomainHistoryVKey pojoValue) { + return pojoValue == null ? null : pojoValue.getOfyKey().getRaw(); + } + }; + } +} diff --git a/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java b/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java index 98bf13ee5..fd529f2b8 100644 --- a/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java +++ b/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java @@ -43,7 +43,6 @@ public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory !clazz.isAnnotationPresent(EntitySubclass.class)) .collect(toImmutableMap(com.googlecode.objectify.Key::getKind, identity())); - ; public VKeyTranslatorFactory() { super(VKey.class); diff --git a/core/src/main/java/google/registry/persistence/DomainHistoryVKey.java b/core/src/main/java/google/registry/persistence/DomainHistoryVKey.java new file mode 100644 index 000000000..32b5d7718 --- /dev/null +++ b/core/src/main/java/google/registry/persistence/DomainHistoryVKey.java @@ -0,0 +1,62 @@ +// Copyright 2020 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; + +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; + +import com.googlecode.objectify.Key; +import google.registry.model.domain.DomainBase; +import google.registry.model.domain.DomainHistory.DomainHistoryId; +import google.registry.model.reporting.HistoryEntry; +import javax.persistence.Embeddable; +import javax.persistence.PostLoad; + +/** {@link VKey} for {@link HistoryEntry} which parent is {@link DomainBase}. */ +@Embeddable +public class DomainHistoryVKey extends VKey { + + private String domainRepoId; + + private Long domainHistoryId; + + // Hibernate requires a default constructor + private DomainHistoryVKey() {} + + private DomainHistoryVKey(String domainRepoId, long domainHistoryId) { + initWith(domainRepoId, domainHistoryId); + } + + @PostLoad + void postLoad() { + initWith(domainRepoId, domainHistoryId); + } + + private void initWith(String domainRepoId, long domainHistoryId) { + this.kind = HistoryEntry.class; + this.ofyKey = + Key.create(Key.create(DomainBase.class, domainRepoId), HistoryEntry.class, domainHistoryId); + this.sqlKey = new DomainHistoryId(domainRepoId, domainHistoryId); + this.domainRepoId = domainRepoId; + this.domainHistoryId = domainHistoryId; + } + + /** Creates {@link DomainHistoryVKey} from the given {@link Key} instance. */ + public static DomainHistoryVKey create(Key ofyKey) { + checkArgumentNotNull(ofyKey, "ofyKey must be specified"); + String domainRepoId = ofyKey.getParent().getName(); + long domainHistoryId = ofyKey.getId(); + return new DomainHistoryVKey(domainRepoId, domainHistoryId); + } +} diff --git a/core/src/main/java/google/registry/persistence/VKey.java b/core/src/main/java/google/registry/persistence/VKey.java index edce5a70b..24056a9fe 100644 --- a/core/src/main/java/google/registry/persistence/VKey.java +++ b/core/src/main/java/google/registry/persistence/VKey.java @@ -36,18 +36,20 @@ public class VKey extends ImmutableObject implements Serializable { private static final long serialVersionUID = -5291472863840231240L; - // The primary key for the referenced entity. - private final Object primaryKey; + // The SQL key for the referenced entity. + Object sqlKey; // The objectify key for the referenced entity. - private final com.googlecode.objectify.Key ofyKey; + Key ofyKey; - private final Class kind; + Class kind; - private VKey(Class kind, com.googlecode.objectify.Key ofyKey, Object primaryKey) { + VKey() {} + + VKey(Class kind, Key ofyKey, Object sqlKey) { this.kind = kind; this.ofyKey = ofyKey; - this.primaryKey = primaryKey; + this.sqlKey = sqlKey; } /** @@ -62,15 +64,14 @@ public class VKey extends ImmutableObject implements Serializable { } /** Creates a {@link VKey} which only contains the ofy primary key. */ - public static VKey createOfy(Class kind, com.googlecode.objectify.Key ofyKey) { + public static VKey createOfy(Class kind, Key ofyKey) { checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(ofyKey, "ofyKey must not be null"); return new VKey(kind, ofyKey, null); } /** Creates a {@link VKey} which only contains both sql and ofy primary key. */ - public static VKey create( - Class kind, Object sqlKey, com.googlecode.objectify.Key ofyKey) { + public static VKey create(Class kind, Object sqlKey, Key ofyKey) { checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(sqlKey, "sqlKey must not be null"); checkArgumentNotNull(ofyKey, "ofyKey must not be null"); @@ -197,23 +198,23 @@ public class VKey extends ImmutableObject implements Serializable { /** Returns the SQL primary key. */ public Object getSqlKey() { - checkState(primaryKey != null, "Attempting obtain a null SQL key."); - return this.primaryKey; + checkState(sqlKey != null, "Attempting obtain a null SQL key."); + return this.sqlKey; } /** Returns the SQL primary key if it exists. */ public Optional maybeGetSqlKey() { - return Optional.ofNullable(this.primaryKey); + return Optional.ofNullable(this.sqlKey); } /** Returns the objectify key. */ - public com.googlecode.objectify.Key getOfyKey() { + public Key getOfyKey() { checkState(ofyKey != null, "Attempting obtain a null Objectify key."); return this.ofyKey; } /** Returns the objectify key if it exists. */ - public Optional> maybeGetOfyKey() { + public Optional> maybeGetOfyKey() { return Optional.ofNullable(this.ofyKey); } diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index 5ee4ec96f..0cb05102b 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -112,7 +112,6 @@ google.registry.model.host.VKeyConverter_HostResource google.registry.model.poll.VKeyConverter_Autorenew google.registry.model.poll.VKeyConverter_OneTime - google.registry.model.reporting.VKeyConverter_HistoryEntry NONE diff --git a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java index 547fe5be0..30b3cc7c5 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java @@ -71,7 +71,7 @@ import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservedList; import google.registry.model.reporting.HistoryEntry; -import google.registry.persistence.VKey; +import google.registry.persistence.DomainHistoryVKey; import org.joda.money.CurrencyUnit; import org.joda.money.Money; import org.joda.time.DateTime; @@ -169,7 +169,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase key = - Key.create(Key.create(DomainBase.class, "ROID-1"), HistoryEntry.class, 101); - VKey vkey = VKeyTranslatorFactory.createVKey(key); - assertThat(vkey.getKind()).isEqualTo(HistoryEntry.class); - assertThat(vkey.getOfyKey()).isEqualTo(key); - assertThat(vkey.getSqlKey()).isEqualTo("DomainBase/ROID-1/101"); + Key domainKey = Key.create(DomainBase.class, "ROID-1"); + Key historyEntryKey = Key.create(domainKey, HistoryEntry.class, 10L); + Key oneTimeKey = + Key.create(historyEntryKey, BillingEvent.OneTime.class, 200L); + + VKey vkey = VKeyTranslatorFactory.createVKey(oneTimeKey); + + assertThat(vkey.getKind()).isEqualTo(BillingEvent.OneTime.class); + assertThat(vkey.getOfyKey()).isEqualTo(oneTimeKey); + assertThat(vkey.getSqlKey()).isEqualTo(200L); } @Test diff --git a/core/src/test/java/google/registry/persistence/DomainHistoryVKeyTest.java b/core/src/test/java/google/registry/persistence/DomainHistoryVKeyTest.java new file mode 100644 index 000000000..8c7b26fdf --- /dev/null +++ b/core/src/test/java/google/registry/persistence/DomainHistoryVKeyTest.java @@ -0,0 +1,97 @@ +// Copyright 2020 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; + +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.DomainBase; +import google.registry.model.domain.DomainHistory.DomainHistoryId; +import google.registry.model.reporting.HistoryEntry; +import google.registry.schema.replay.EntityTest.EntityForTesting; +import google.registry.testing.AppEngineExtension; +import google.registry.testing.DualDatabaseTest; +import google.registry.testing.TestOfyAndSql; +import javax.persistence.Transient; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** Unit test for {@link DomainHistoryVKey}. */ +@DualDatabaseTest +class DomainHistoryVKeyTest { + + @RegisterExtension + final AppEngineExtension appEngine = + AppEngineExtension.builder() + .withDatastoreAndCloudSql() + .withOfyTestEntities(TestEntity.class) + .withJpaUnitTestEntities(TestEntity.class) + .build(); + + @TestOfyAndSql + void testRestoreSymmetricVKey() { + Key ofyKey = + Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L); + DomainHistoryVKey domainHistoryVKey = DomainHistoryVKey.create(ofyKey); + TestEntity original = new TestEntity(domainHistoryVKey); + tm().transact(() -> tm().insert(original)); + TestEntity persisted = tm().transact(() -> tm().load(original.createVKey())); + assertThat(persisted).isEqualTo(original); + // Double check that the persisted.domainHistoryVKey is a symmetric VKey + assertThat(persisted.domainHistoryVKey.getKind()).isEqualTo(HistoryEntry.class); + assertThat(persisted.domainHistoryVKey.getOfyKey()) + .isEqualTo( + Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L)); + assertThat(persisted.domainHistoryVKey.getSqlKey()) + .isEqualTo(new DomainHistoryId("domainRepoId", 10L)); + } + + @TestOfyAndSql + void testCreateSymmetricVKeyFromOfyKey() { + Key ofyKey = + Key.create(Key.create(DomainBase.class, "domainRepoId"), HistoryEntry.class, 10L); + DomainHistoryVKey domainHistoryVKey = DomainHistoryVKey.create(ofyKey); + assertThat(domainHistoryVKey.getKind()).isEqualTo(HistoryEntry.class); + assertThat(domainHistoryVKey.getOfyKey()).isEqualTo(ofyKey); + assertThat(domainHistoryVKey.getSqlKey()).isEqualTo(new DomainHistoryId("domainRepoId", 10L)); + } + + @EntityForTesting + @Entity + @javax.persistence.Entity(name = "TestEntity") + private static class TestEntity extends ImmutableObject { + @Transient @Parent Key parent = getCrossTldKey(); + + @Id @javax.persistence.Id String id = "id"; + + DomainHistoryVKey domainHistoryVKey; + + TestEntity() {} + + TestEntity(DomainHistoryVKey domainHistoryVKey) { + this.domainHistoryVKey = domainHistoryVKey; + } + + VKey createVKey() { + return VKey.create(TestEntity.class, id, Key.create(parent, TestEntity.class, id)); + } + } +} diff --git a/core/src/test/java/google/registry/tools/DeleteAllocationTokensCommandTest.java b/core/src/test/java/google/registry/tools/DeleteAllocationTokensCommandTest.java index f8553f2f7..19dee3a35 100644 --- a/core/src/test/java/google/registry/tools/DeleteAllocationTokensCommandTest.java +++ b/core/src/test/java/google/registry/tools/DeleteAllocationTokensCommandTest.java @@ -27,7 +27,7 @@ import google.registry.model.domain.DomainBase; import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.reporting.HistoryEntry; -import google.registry.persistence.VKey; +import google.registry.persistence.DomainHistoryVKey; import java.util.Collection; import javax.annotation.Nullable; import org.junit.jupiter.api.BeforeEach; @@ -173,7 +173,7 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L); - builder.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1051L, historyEntryKey)); + builder.setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey)); } return persistResource(builder.build()); } diff --git a/core/src/test/java/google/registry/tools/GenerateAllocationTokensCommandTest.java b/core/src/test/java/google/registry/tools/GenerateAllocationTokensCommandTest.java index b08697d45..178024f6b 100644 --- a/core/src/test/java/google/registry/tools/GenerateAllocationTokensCommandTest.java +++ b/core/src/test/java/google/registry/tools/GenerateAllocationTokensCommandTest.java @@ -39,8 +39,7 @@ import com.google.common.collect.Iterables; import com.google.common.io.Files; import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenStatus; -import google.registry.model.reporting.HistoryEntry; -import google.registry.persistence.VKey; +import google.registry.persistence.DomainHistoryVKey; import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.DeterministicStringGenerator.Rule; import google.registry.testing.FakeClock; @@ -314,7 +313,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase redemptionHistoryEntry, + @Nullable DomainHistoryVKey redemptionHistoryEntry, @Nullable String domainName) { AllocationToken.Builder builder = new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE); diff --git a/core/src/test/java/google/registry/tools/GetAllocationTokenCommandTest.java b/core/src/test/java/google/registry/tools/GetAllocationTokenCommandTest.java index 8838b0f65..285d58f0c 100644 --- a/core/src/test/java/google/registry/tools/GetAllocationTokenCommandTest.java +++ b/core/src/test/java/google/registry/tools/GetAllocationTokenCommandTest.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableList; import com.googlecode.objectify.Key; import google.registry.model.domain.DomainBase; import google.registry.model.domain.token.AllocationToken; -import google.registry.model.reporting.HistoryEntry; +import google.registry.persistence.DomainHistoryVKey; import org.joda.time.DateTime; import org.junit.jupiter.api.Test; @@ -85,7 +85,7 @@ class GetAllocationTokenCommandTest extends CommandTestCase tokenStatusTransitions; google.registry.model.domain.token.AllocationToken$TokenType tokenType; - google.registry.persistence.VKey redemptionHistoryEntry; + google.registry.persistence.DomainHistoryVKey redemptionHistoryEntry; int discountYears; java.lang.String domainName; java.util.Set allowedClientIds; @@ -908,3 +908,10 @@ enum google.registry.model.transfer.TransferStatus { SERVER_APPROVED; SERVER_CANCELLED; } +class google.registry.persistence.DomainHistoryVKey { + com.googlecode.objectify.Key ofyKey; + java.lang.Class kind; + java.lang.Long domainHistoryId; + java.lang.Object sqlKey; + java.lang.String domainRepoId; +} diff --git a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html index 5ed89b338..4cff74278 100644 --- a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html @@ -261,11 +261,11 @@ td.section { generated on - 2020-11-16 16:45:08.581361 + 2020-11-17 20:31:03.895145 last flyway file - V77__fixes_for_replay.sql + V78__add_history_id_for_redemption_history_entry.sql @@ -284,7 +284,7 @@ td.section { generated on - 2020-11-16 16:45:08.581361 + 2020-11-17 20:31:03.895145 diff --git a/db/src/main/resources/sql/er_diagram/full_er_diagram.html b/db/src/main/resources/sql/er_diagram/full_er_diagram.html index eeacb66c1..43dd83b90 100644 --- a/db/src/main/resources/sql/er_diagram/full_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/full_er_diagram.html @@ -261,19 +261,19 @@ td.section { generated on - 2020-11-16 16:45:06.707088 + 2020-11-17 20:31:01.853374 last flyway file - V77__fixes_for_replay.sql + V78__add_history_id_for_redemption_history_entry.sql

 

 

- + SchemaCrawler_Diagram - + generated by @@ -284,259 +284,267 @@ td.section { generated on - 2020-11-16 16:45:06.707088 + 2020-11-17 20:31:01.853374 allocationtoken_a08ccbef - - + + public.AllocationToken - - + + [table] - + token - + - + text not null - + update_timestamp - + - + timestamptz - + allowed_registrar_ids - + - + _text - + allowed_tlds - + - + _text - + creation_time - + - + timestamptz not null - + discount_fraction - + - + float8(17, 17) not null - + discount_premiums - + - + bool not null - + discount_years - + - + int4 not null - + domain_name - + - + text - - redemption_history_entry + + redemption_domain_repo_id - + - + text - + token_status_transitions - + - + "hstore" - + token_type - + - + text - + + redemption_domain_history_id + + + + + int8 + + billingevent_a57d1815 - - + + public.BillingEvent - - + + [table] - + billing_event_id - + - + int8 not null - + registrar_id - + - + text not null - + domain_history_revision_id - + - + int8 not null - + domain_repo_id - + - + text not null - + event_time - + - + timestamptz not null - + flags - + - + _text - + reason - + - + text not null - + domain_name - + - + text not null - + allocation_token - + - + text - + billing_time - + - + timestamptz - + cancellation_matching_billing_recurrence_id - + - + int8 - + cost_amount - + - + numeric(19, 2) - + cost_currency - + - + text - + period_years - + - + int4 - + synthetic_creation_time - + - + timestamptz - + billingevent_a57d1815:w->allocationtoken_a08ccbef:e - - - - - - - - + + + + + + + + fk_billing_event_allocation_token @@ -634,14 +642,14 @@ td.section { billingevent_a57d1815:w->billingrecurrence_5fa2cb01:e - - - + + + - + fk_billing_event_cancellation_matching_billing_recurrence_id @@ -1179,9 +1187,9 @@ td.section { billingevent_a57d1815:w->domainhistory_a54cc226:e - - - + + + @@ -1192,9 +1200,9 @@ td.section { billingevent_a57d1815:w->domainhistory_a54cc226:e - - - + + + @@ -1577,9 +1585,9 @@ td.section { billingevent_a57d1815:w->registrar_6e1503e3:e - - - + + + @@ -1690,26 +1698,26 @@ td.section { billingcancellation_6eedf614:w->billingevent_a57d1815:e - + - - - - + + + + fk_billing_cancellation_billing_event_id billingcancellation_6eedf614:w->billingrecurrence_5fa2cb01:e - + - - - - + + + + fk_billing_cancellation_billing_recurrence_id @@ -1742,14 +1750,14 @@ td.section { billingcancellation_6eedf614:w->registrar_6e1503e3:e - - - + + + - + fk_billing_cancellation_registrar_id @@ -2183,14 +2191,14 @@ td.section {
domain_6c51cffa:w->billingevent_a57d1815:e - + - - - - - + + + + + fk_domain_transfer_billing_event_id @@ -2998,7 +3006,7 @@ td.section { domain_6c51cffa:w->registrar_6e1503e3:e - + @@ -3011,14 +3019,14 @@ td.section { domain_6c51cffa:w->registrar_6e1503e3:e - - + + - + fkjc0r9r5y1lfbt4gpbqw4wsuvq @@ -3379,106 +3387,106 @@ td.section { graceperiod_cd3b2e8f - - + + public.GracePeriod - - + + [table] - + grace_period_id - + - + int8 not null - + billing_event_id - + - + int8 - + billing_recurrence_id - + - + int8 - + registrar_id - + - + text not null - + domain_repo_id - + - + text not null - + expiration_time - + - + timestamptz not null - + type - + - + text not null - + billing_event_history_id - + - + int8 - + billing_recurrence_history_id - + - + int8 - + graceperiod_cd3b2e8f:w->billingevent_a57d1815:e - - - - - - - - + + + + + + + + fk_grace_period_billing_event_id graceperiod_cd3b2e8f:w->domain_6c51cffa:e - - - + + + @@ -3489,22 +3497,22 @@ td.section { graceperiod_cd3b2e8f:w->billingrecurrence_5fa2cb01:e - - - + + + - + fk_grace_period_billing_recurrence_id graceperiod_cd3b2e8f:w->registrar_6e1503e3:e - - - + + + @@ -3528,9 +3536,9 @@ td.section { billingrecurrence_5fa2cb01:w->domainhistory_a54cc226:e - - - + + + @@ -3554,91 +3562,91 @@ td.section { claimsentry_105da9f1 - - + + public.ClaimsEntry - - + + [table] - + revision_id - + - + int8 not null - + claim_key - + - + text not null - + domain_label - + - + text not null - + claimslist_3d49bc2b - - + + public.ClaimsList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz not null - + tmdb_generation_time - + - + timestamptz not null - + claimsentry_105da9f1:w->claimslist_3d49bc2b:e - - - - - - - - + + + + + + + + fk6sc6at5hedffc0nhdcab6ivuq @@ -3670,7 +3678,7 @@ td.section { contact_8de8cb16:w->registrar_6e1503e3:e - + @@ -4358,14 +4366,14 @@ td.section { pollmessage_614a523e:w->domainhistory_a54cc226:e - + - + fk_poll_message_domain_history @@ -4777,47 +4785,47 @@ td.section { cursor_6af40e8c - - + + public."Cursor" - - + + [table] - + "scope" - + - + text not null - + type - + - + text not null - + cursor_time - + - + timestamptz not null - + last_update_time - + - + timestamptz not null - + delegationsignerdata_e542a872 @@ -4899,7 +4907,7 @@ td.section { domainhistory_a54cc226:w->registrar_6e1503e3:e - + @@ -4953,27 +4961,27 @@ td.section { domainhost_1ea127c2:w->host_f21b78de:e - + - - - - - + + + + + fk_domainhost_host_valid host_f21b78de:w->domain_6c51cffa:e - + - + fk_host_superordinate_domain @@ -5550,265 +5558,265 @@ td.section { kmssecret_f3b28857 - - + + public.KmsSecret - - + + [table] - + revision_id - + - + int8 not null - + creation_time - + - + timestamptz not null - + encrypted_value - + - + text not null - + crypto_key_version_name - + - + text not null - + secret_name - + - + text not null - + lock_f21d4861 - - + + public.Lock - - + + [table] - + resource_name - + - + text not null - + tld - + - + text not null - + acquired_time - + - + timestamptz not null - + expiration_time - + - + timestamptz not null - + request_log_id - + - + text not null - + premiumentry_b0060b91 - - + + public.PremiumEntry - - + + [table] - + revision_id - + - + int8 not null - + price - + - + numeric(19, 2) not null - + domain_label - + - + text not null - + premiumlist_7c3ea68b - - + + public.PremiumList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz - + name - + - + text not null - + bloom_filter - + - + bytea not null - + currency - + - + text not null - + premiumentry_b0060b91:w->premiumlist_7c3ea68b:e - - - - - - - - + + + + + + + + fko0gw90lpo1tuee56l0nb6y6g5 rderevision_83396864 - - + + public.RdeRevision - - + + [table] - + tld - + - + text not null - + mode - + - + text not null - + "date" - + - + date not null - + update_timestamp - + - + timestamptz - + revision - + - + int4 not null - + registrarpoc_ab47054d @@ -5949,450 +5957,450 @@ td.section { registrylock_ac88663e - - + + public.RegistryLock - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + lock_completion_timestamp - + - + timestamptz - + lock_request_timestamp - + - + timestamptz not null - + domain_name - + - + text not null - + is_superuser - + - + bool not null - + registrar_id - + - + text not null - + registrar_poc_id - + - + text - + repo_id - + - + text not null - + verification_code - + - + text not null - + unlock_request_timestamp - + - + timestamptz - + unlock_completion_timestamp - + - + timestamptz - + last_update_timestamp - + - + timestamptz - + relock_revision_id - + - + int8 - + relock_duration - + - + interval - + registrylock_ac88663e:w->registrylock_ac88663e:e - - - - - - - - + + + + + + + + fk2lhcwpxlnqijr96irylrh1707 reservedentry_1a7b8520 - - + + public.ReservedEntry - - + + [table] - + revision_id - + - + int8 not null - + comment - + - + text - + reservation_type - + - + int4 not null - + domain_label - + - + text not null - + reservedlist_b97c3f1c - - + + public.ReservedList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_timestamp - + - + timestamptz not null - + name - + - + text not null - + should_publish - + - + bool not null - + reservedentry_1a7b8520:w->reservedlist_b97c3f1c:e - - - - - - - - + + + + + + + + fkgq03rk0bt1hb915dnyvd3vnfc serversecret_6cc90f09 - - + + public.ServerSecret - - + + [table] - + secret - + - + uuid not null - + signedmarkrevocationentry_99c39721 - - + + public.SignedMarkRevocationEntry - - + + [table] - + revision_id - + - + int8 not null - + revocation_time - + - + timestamptz not null - + smd_id - + - + text not null - + signedmarkrevocationlist_c5d968fb - - + + public.SignedMarkRevocationList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_time - + - + timestamptz - + signedmarkrevocationentry_99c39721:w->signedmarkrevocationlist_c5d968fb:e - - - - - - - - + + + + + + + + fk5ivlhvs3121yx2li5tqh54u4 sqlreplaycheckpoint_342081b3 - - + + public.SqlReplayCheckpoint - - + + [table] - + revision_id - + - + int8 not null - + last_replay_time - + - + timestamptz not null - + tmchcrl_d282355 - - + + public.TmchCrl - - + + [table] - + certificate_revocations - + - + text not null - + update_timestamp - + - + timestamptz not null - + url - + - + text not null - + transaction_d50389d4 - - + + public.Transaction - - + + [table] - + id - + - + bigserial not null - + - + auto-incremented - + contents - + - + bytea - + @@ -6448,7 +6456,7 @@ td.section { - redemption_history_entry + redemption_domain_repo_id text @@ -6461,6 +6469,11 @@ td.section { token_type text + + + redemption_domain_history_id + int8 + diff --git a/db/src/main/resources/sql/flyway.txt b/db/src/main/resources/sql/flyway.txt index 829d26a1b..df9bbcc2e 100644 --- a/db/src/main/resources/sql/flyway.txt +++ b/db/src/main/resources/sql/flyway.txt @@ -75,3 +75,4 @@ V74__sql_replay_checkpoint.sql V75__add_grace_period_history.sql V76__change_history_nullability.sql V77__fixes_for_replay.sql +V78__add_history_id_for_redemption_history_entry.sql diff --git a/db/src/main/resources/sql/flyway/V78__add_history_id_for_redemption_history_entry.sql b/db/src/main/resources/sql/flyway/V78__add_history_id_for_redemption_history_entry.sql new file mode 100644 index 000000000..bb3573c05 --- /dev/null +++ b/db/src/main/resources/sql/flyway/V78__add_history_id_for_redemption_history_entry.sql @@ -0,0 +1,20 @@ +-- Copyright 2020 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 "AllocationToken" + rename column "redemption_history_entry" + to "redemption_domain_repo_id"; + +alter table "AllocationToken" + add column if not exists "redemption_domain_history_id" int8; diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 5828406cc..20400cbd5 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -22,6 +22,8 @@ discount_premiums boolean not null, discount_years int4 not null, domain_name text, + redemption_domain_history_id int8, + redemption_domain_repo_id text, token_status_transitions hstore, token_type text, primary key (token) diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql index 482cec6ff..45f8ed949 100644 --- a/db/src/main/resources/sql/schema/nomulus.golden.sql +++ b/db/src/main/resources/sql/schema/nomulus.golden.sql @@ -48,9 +48,10 @@ CREATE TABLE public."AllocationToken" ( discount_premiums boolean NOT NULL, discount_years integer NOT NULL, domain_name text, - redemption_history_entry text, + redemption_domain_repo_id text, token_status_transitions public.hstore, - token_type text + token_type text, + redemption_domain_history_id bigint );