Use DomainHistoryVKey to restore symmetric VKey (#874)

* Use DomainHistoryVKey to restore symmetric VKey

* Rebase on HEAD
This commit is contained in:
Shicong Huang 2020-11-17 16:32:32 -05:00 committed by GitHub
parent 385e8fb6ec
commit b43fac655c
27 changed files with 871 additions and 621 deletions

View file

@ -110,6 +110,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.tmch.LordnTaskUtils; import google.registry.tmch.LordnTaskUtils;
import java.util.Optional; import java.util.Optional;
@ -371,7 +372,7 @@ public class DomainCreateFlow implements TransactionalFlow {
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) { && TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
entitiesToSave.add( entitiesToSave.add(
allocationTokenFlowUtils.redeemToken( allocationTokenFlowUtils.redeemToken(
allocationToken.get(), HistoryEntry.createVKey(Key.create(historyEntry)))); allocationToken.get(), DomainHistoryVKey.create(Key.create(historyEntry))));
} }
enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice); enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice);

View file

@ -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.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
@ -108,7 +107,7 @@ public class AllocationTokenFlowUtils {
/** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */ /** Redeems a SINGLE_USE {@link AllocationToken}, returning the redeemed copy. */
public AllocationToken redeemToken( public AllocationToken redeemToken(
AllocationToken token, VKey<HistoryEntry> redemptionHistoryEntry) { AllocationToken token, DomainHistoryVKey redemptionHistoryEntry) {
checkArgument( checkArgument(
TokenType.SINGLE_USE.equals(token.getTokenType()), TokenType.SINGLE_USE.equals(token.getTokenType()),
"Only SINGLE_USE tokens can be marked as redeemed"); "Only SINGLE_USE tokens can be marked as redeemed");

View file

@ -257,7 +257,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
} }
/** Class to represent the composite primary key of {@link DomainHistory} entity. */ /** 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; private String domainRepoId;
@ -266,7 +266,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
/** Hibernate requires this default constructor. */ /** Hibernate requires this default constructor. */
private DomainHistoryId() {} private DomainHistoryId() {}
DomainHistoryId(String domainRepoId, long id) { public DomainHistoryId(String domainRepoId, long id) {
this.domainRepoId = domainRepoId; this.domainRepoId = domainRepoId;
this.id = id; this.id = id;
} }

View file

@ -47,17 +47,19 @@ import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.common.TimedTransitionProperty.TimeMapper; import google.registry.model.common.TimedTransitionProperty.TimeMapper;
import google.registry.model.common.TimedTransitionProperty.TimedTransition; import google.registry.model.common.TimedTransitionProperty.TimedTransition;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.WithStringVKey; import google.registry.persistence.WithStringVKey;
import google.registry.schema.replay.DatastoreAndSqlEntity; import google.registry.schema.replay.DatastoreAndSqlEntity;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
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.EnumType; import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** An entity representing an allocation token. */ /** An entity representing an allocation token. */
@ -106,8 +108,15 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
@javax.persistence.Id @Id String token; @javax.persistence.Id @Id String token;
/** The key of the history entry for which the token was used. Null if not yet used. */ /** 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. @Nullable
@Transient @Nullable @Index VKey<HistoryEntry> redemptionHistoryEntry; @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. */ /** The fully-qualified domain name that this token is limited to, if any. */
@Nullable @Index String domainName; @Nullable @Index String domainName;
@ -282,7 +291,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
return this; return this;
} }
public Builder setRedemptionHistoryEntry(VKey<HistoryEntry> redemptionHistoryEntry) { public Builder setRedemptionHistoryEntry(DomainHistoryVKey redemptionHistoryEntry) {
getInstance().redemptionHistoryEntry = getInstance().redemptionHistoryEntry =
checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null"); checkArgumentNotNull(redemptionHistoryEntry, "Redemption history entry must not be null");
return this; return this;

View file

@ -42,6 +42,7 @@ import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CommitLogRevisionsTranslatorFactory; import google.registry.model.translators.CommitLogRevisionsTranslatorFactory;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory; import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory; import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DomainHistoryVKeyTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory; import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.InetAddressTranslatorFactory; import google.registry.model.translators.InetAddressTranslatorFactory;
import google.registry.model.translators.ReadableInstantUtcTranslatorFactory; import google.registry.model.translators.ReadableInstantUtcTranslatorFactory;
@ -127,6 +128,7 @@ public class ObjectifyService {
new CreateAutoTimestampTranslatorFactory(), new CreateAutoTimestampTranslatorFactory(),
new CurrencyUnitTranslatorFactory(), new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(), new DurationTranslatorFactory(),
new DomainHistoryVKeyTranslatorFactory(),
new InetAddressTranslatorFactory(), new InetAddressTranslatorFactory(),
new MoneyStringTranslatorFactory(), new MoneyStringTranslatorFactory(),
new ReadableInstantUtcTranslatorFactory(), new ReadableInstantUtcTranslatorFactory(),

View file

@ -39,8 +39,6 @@ import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostHistory; import google.registry.model.host.HostHistory;
import google.registry.model.host.HostResource; 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.DatastoreEntity;
import google.registry.schema.replay.SqlEntity; import google.registry.schema.replay.SqlEntity;
import java.util.Set; import java.util.Set;
@ -60,7 +58,6 @@ import org.joda.time.DateTime;
@ReportedOn @ReportedOn
@Entity @Entity
@MappedSuperclass @MappedSuperclass
@WithStringVKey // TODO(b/162229294): This should be resolved during the course of that bug
@Access(AccessType.FIELD) @Access(AccessType.FIELD)
public class HistoryEntry extends ImmutableObject implements Buildable, DatastoreEntity { 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); domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords);
} }
public static VKey<HistoryEntry> createVKey(Key<HistoryEntry> 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 @Override
public Builder asBuilder() { public Builder asBuilder() {
return new Builder(clone(this)); return new Builder(clone(this));

View file

@ -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<DomainHistoryVKey, Key> {
public DomainHistoryVKeyTranslatorFactory() {
super(DomainHistoryVKey.class);
}
@Override
SimpleTranslator<DomainHistoryVKey, Key> createTranslator() {
return new SimpleTranslator<DomainHistoryVKey, Key>() {
@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();
}
};
}
}

View file

@ -43,7 +43,6 @@ public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory<VKey,
ALL_CLASSES.stream() ALL_CLASSES.stream()
.filter(clazz -> !clazz.isAnnotationPresent(EntitySubclass.class)) .filter(clazz -> !clazz.isAnnotationPresent(EntitySubclass.class))
.collect(toImmutableMap(com.googlecode.objectify.Key::getKind, identity())); .collect(toImmutableMap(com.googlecode.objectify.Key::getKind, identity()));
;
public VKeyTranslatorFactory() { public VKeyTranslatorFactory() {
super(VKey.class); super(VKey.class);

View file

@ -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<HistoryEntry> {
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<HistoryEntry> ofyKey) {
checkArgumentNotNull(ofyKey, "ofyKey must be specified");
String domainRepoId = ofyKey.getParent().getName();
long domainHistoryId = ofyKey.getId();
return new DomainHistoryVKey(domainRepoId, domainHistoryId);
}
}

View file

@ -36,18 +36,20 @@ public class VKey<T> extends ImmutableObject implements Serializable {
private static final long serialVersionUID = -5291472863840231240L; private static final long serialVersionUID = -5291472863840231240L;
// The primary key for the referenced entity. // The SQL key for the referenced entity.
private final Object primaryKey; Object sqlKey;
// The objectify key for the referenced entity. // The objectify key for the referenced entity.
private final com.googlecode.objectify.Key<T> ofyKey; Key<T> ofyKey;
private final Class<? extends T> kind; Class<? extends T> kind;
private VKey(Class<? extends T> kind, com.googlecode.objectify.Key<T> ofyKey, Object primaryKey) { VKey() {}
VKey(Class<? extends T> kind, Key<T> ofyKey, Object sqlKey) {
this.kind = kind; this.kind = kind;
this.ofyKey = ofyKey; this.ofyKey = ofyKey;
this.primaryKey = primaryKey; this.sqlKey = sqlKey;
} }
/** /**
@ -62,15 +64,14 @@ public class VKey<T> extends ImmutableObject implements Serializable {
} }
/** Creates a {@link VKey} which only contains the ofy primary key. */ /** Creates a {@link VKey} which only contains the ofy primary key. */
public static <T> VKey<T> createOfy(Class<T> kind, com.googlecode.objectify.Key<T> ofyKey) { public static <T> VKey<T> createOfy(Class<T> kind, Key<T> ofyKey) {
checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(kind, "kind must not be null");
checkArgumentNotNull(ofyKey, "ofyKey must not be null"); checkArgumentNotNull(ofyKey, "ofyKey must not be null");
return new VKey<T>(kind, ofyKey, null); return new VKey<T>(kind, ofyKey, null);
} }
/** Creates a {@link VKey} which only contains both sql and ofy primary key. */ /** Creates a {@link VKey} which only contains both sql and ofy primary key. */
public static <T> VKey<T> create( public static <T> VKey<T> create(Class<T> kind, Object sqlKey, Key<T> ofyKey) {
Class<T> kind, Object sqlKey, com.googlecode.objectify.Key<T> ofyKey) {
checkArgumentNotNull(kind, "kind must not be null"); checkArgumentNotNull(kind, "kind must not be null");
checkArgumentNotNull(sqlKey, "sqlKey must not be null"); checkArgumentNotNull(sqlKey, "sqlKey must not be null");
checkArgumentNotNull(ofyKey, "ofyKey must not be null"); checkArgumentNotNull(ofyKey, "ofyKey must not be null");
@ -197,23 +198,23 @@ public class VKey<T> extends ImmutableObject implements Serializable {
/** Returns the SQL primary key. */ /** Returns the SQL primary key. */
public Object getSqlKey() { public Object getSqlKey() {
checkState(primaryKey != null, "Attempting obtain a null SQL key."); checkState(sqlKey != null, "Attempting obtain a null SQL key.");
return this.primaryKey; return this.sqlKey;
} }
/** Returns the SQL primary key if it exists. */ /** Returns the SQL primary key if it exists. */
public Optional<Object> maybeGetSqlKey() { public Optional<Object> maybeGetSqlKey() {
return Optional.ofNullable(this.primaryKey); return Optional.ofNullable(this.sqlKey);
} }
/** Returns the objectify key. */ /** Returns the objectify key. */
public com.googlecode.objectify.Key<T> getOfyKey() { public Key<T> getOfyKey() {
checkState(ofyKey != null, "Attempting obtain a null Objectify key."); checkState(ofyKey != null, "Attempting obtain a null Objectify key.");
return this.ofyKey; return this.ofyKey;
} }
/** Returns the objectify key if it exists. */ /** Returns the objectify key if it exists. */
public Optional<com.googlecode.objectify.Key<T>> maybeGetOfyKey() { public Optional<Key<T>> maybeGetOfyKey() {
return Optional.ofNullable(this.ofyKey); return Optional.ofNullable(this.ofyKey);
} }

View file

@ -112,7 +112,6 @@
<class>google.registry.model.host.VKeyConverter_HostResource</class> <class>google.registry.model.host.VKeyConverter_HostResource</class>
<class>google.registry.model.poll.VKeyConverter_Autorenew</class> <class>google.registry.model.poll.VKeyConverter_Autorenew</class>
<class>google.registry.model.poll.VKeyConverter_OneTime</class> <class>google.registry.model.poll.VKeyConverter_OneTime</class>
<class>google.registry.model.reporting.VKeyConverter_HistoryEntry</class>
<!-- TODO(weiminyu): check out application-layer validation. --> <!-- TODO(weiminyu): check out application-layer validation. -->
<validation-mode>NONE</validation-mode> <validation-mode>NONE</validation-mode>

View file

@ -71,7 +71,7 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservedList; import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.HistoryEntry; 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.CurrencyUnit;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -169,7 +169,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L, historyEntryKey)) .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
.build()); .build());
doCheckTest( doCheckTest(
create(false, "example1.tld", "In use"), create(false, "example1.tld", "In use"),

View file

@ -162,7 +162,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.monitoring.whitebox.EppMetric; import google.registry.monitoring.whitebox.EppMetric;
import google.registry.persistence.VKey; import google.registry.persistence.DomainHistoryVKey;
import google.registry.testing.ReplayExtension; import google.registry.testing.ReplayExtension;
import google.registry.testing.TaskQueueHelper.TaskMatcher; import google.registry.testing.TaskQueueHelper.TaskMatcher;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -505,7 +505,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 505L, historyEntryKey)) .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = EppException thrown =
@ -528,7 +528,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
HistoryEntry historyEntry = HistoryEntry historyEntry =
ofy().load().type(HistoryEntry.class).ancestor(reloadResourceByForeignKey()).first().now(); ofy().load().type(HistoryEntry.class).ancestor(reloadResourceByForeignKey()).first().now();
assertThat(ofy().load().entity(token).now().getRedemptionHistoryEntry()) assertThat(ofy().load().entity(token).now().getRedemptionHistoryEntry())
.hasValue(HistoryEntry.createVKey(Key.create(historyEntry))); .hasValue(DomainHistoryVKey.create(Key.create(historyEntry)));
} }
@Test @Test
@ -1275,7 +1275,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertThat(reloadedToken.isRedeemed()).isTrue(); assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry()) assertThat(reloadedToken.getRedemptionHistoryEntry())
.hasValue( .hasValue(
HistoryEntry.createVKey( DomainHistoryVKey.create(
Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0)))); Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0))));
} }

View file

@ -49,7 +49,7 @@ import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.DomainHistoryVKey;
import google.registry.testing.AppEngineExtension; import google.registry.testing.AppEngineExtension;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -198,7 +198,7 @@ class AllocationTokenFlowUtilsTest {
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("tokeN") .setToken("tokeN")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 101L, historyEntryKey)) .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
.build()); .build());
assertThat( assertThat(
flowUtils flowUtils

View file

@ -40,6 +40,7 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -85,7 +86,7 @@ public class AllocationTokenTest extends EntityTestCase {
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123Single") .setToken("abc123Single")
.setRedemptionHistoryEntry(HistoryEntry.createVKey(historyEntryKey)) .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
.setDomainName("example.foo") .setDomainName("example.foo")
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z")) .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
@ -124,7 +125,7 @@ public class AllocationTokenTest extends EntityTestCase {
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L, historyEntryKey)) .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey))
.setDomainName("blahdomain.foo") .setDomainName("blahdomain.foo")
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z")) .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.build()), .build()),
@ -230,7 +231,7 @@ public class AllocationTokenTest extends EntityTestCase {
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("foobar") .setToken("foobar")
.setTokenType(TokenType.UNLIMITED_USE) .setTokenType(TokenType.UNLIMITED_USE)
.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L, historyEntryKey)); .setRedemptionHistoryEntry(DomainHistoryVKey.create(historyEntryKey));
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
assertThat(thrown) assertThat(thrown)
.hasMessageThat() .hasMessageThat()

View file

@ -20,6 +20,7 @@ import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.CommitLogCheckpoint; import google.registry.model.ofy.CommitLogCheckpoint;
import google.registry.model.ofy.CommitLogCheckpointRoot; import google.registry.model.ofy.CommitLogCheckpointRoot;
@ -64,12 +65,16 @@ public class VKeyTranslatorFactoryTest {
@Test @Test
void testEntityWithAncestor() { void testEntityWithAncestor() {
Key<HistoryEntry> key = Key<DomainBase> domainKey = Key.create(DomainBase.class, "ROID-1");
Key.create(Key.create(DomainBase.class, "ROID-1"), HistoryEntry.class, 101); Key<HistoryEntry> historyEntryKey = Key.create(domainKey, HistoryEntry.class, 10L);
VKey<HistoryEntry> vkey = VKeyTranslatorFactory.createVKey(key); Key<BillingEvent.OneTime> oneTimeKey =
assertThat(vkey.getKind()).isEqualTo(HistoryEntry.class); Key.create(historyEntryKey, BillingEvent.OneTime.class, 200L);
assertThat(vkey.getOfyKey()).isEqualTo(key);
assertThat(vkey.getSqlKey()).isEqualTo("DomainBase/ROID-1/101"); VKey<BillingEvent.OneTime> vkey = VKeyTranslatorFactory.createVKey(oneTimeKey);
assertThat(vkey.getKind()).isEqualTo(BillingEvent.OneTime.class);
assertThat(vkey.getOfyKey()).isEqualTo(oneTimeKey);
assertThat(vkey.getSqlKey()).isEqualTo(200L);
} }
@Test @Test

View file

@ -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<HistoryEntry> 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<HistoryEntry> 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<EntityGroupRoot> parent = getCrossTldKey();
@Id @javax.persistence.Id String id = "id";
DomainHistoryVKey domainHistoryVKey;
TestEntity() {}
TestEntity(DomainHistoryVKey domainHistoryVKey) {
this.domainHistoryVKey = domainHistoryVKey;
}
VKey<TestEntity> createVKey() {
return VKey.create(TestEntity.class, id, Key.create(parent, TestEntity.class, id));
}
}
}

View file

@ -27,7 +27,7 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey; import google.registry.persistence.DomainHistoryVKey;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -173,7 +173,7 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
String domainToPersist = domainName != null ? domainName : "example.foo"; String domainToPersist = domainName != null ? domainName : "example.foo";
DomainBase domain = persistActiveDomain(domainToPersist); DomainBase domain = persistActiveDomain(domainToPersist);
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L); Key<HistoryEntry> 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()); return persistResource(builder.build());
} }

View file

@ -39,8 +39,7 @@ import com.google.common.collect.Iterables;
import com.google.common.io.Files; import com.google.common.io.Files;
import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.reporting.HistoryEntry; import google.registry.persistence.DomainHistoryVKey;
import google.registry.persistence.VKey;
import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.DeterministicStringGenerator.Rule; import google.registry.testing.DeterministicStringGenerator.Rule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
@ -314,7 +313,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
private AllocationToken createToken( private AllocationToken createToken(
String token, String token,
@Nullable VKey<HistoryEntry> redemptionHistoryEntry, @Nullable DomainHistoryVKey redemptionHistoryEntry,
@Nullable String domainName) { @Nullable String domainName) {
AllocationToken.Builder builder = AllocationToken.Builder builder =
new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE); new AllocationToken.Builder().setToken(token).setTokenType(SINGLE_USE);

View file

@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.model.domain.token.AllocationToken; 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.joda.time.DateTime;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -85,7 +85,7 @@ class GetAllocationTokenCommandTest extends CommandTestCase<GetAllocationTokenCo
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setDomainName("fqqdn.tld") .setDomainName("fqqdn.tld")
.setRedemptionHistoryEntry( .setRedemptionHistoryEntry(
HistoryEntry.createVKey(Key.create(createHistoryEntryForEppResource(domain)))) DomainHistoryVKey.create(Key.create(createHistoryEntryForEppResource(domain))))
.build()); .build());
runCommand("foo"); runCommand("foo");
assertInStdout( assertInStdout(

View file

@ -344,7 +344,7 @@ class google.registry.model.domain.token.AllocationToken {
google.registry.model.UpdateAutoTimestamp updateTimestamp; google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus, google.registry.model.domain.token.AllocationToken$TokenStatusTransition> tokenStatusTransitions; google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus, google.registry.model.domain.token.AllocationToken$TokenStatusTransition> tokenStatusTransitions;
google.registry.model.domain.token.AllocationToken$TokenType tokenType; google.registry.model.domain.token.AllocationToken$TokenType tokenType;
google.registry.persistence.VKey<google.registry.model.reporting.HistoryEntry> redemptionHistoryEntry; google.registry.persistence.DomainHistoryVKey redemptionHistoryEntry;
int discountYears; int discountYears;
java.lang.String domainName; java.lang.String domainName;
java.util.Set<java.lang.String> allowedClientIds; java.util.Set<java.lang.String> allowedClientIds;
@ -908,3 +908,10 @@ enum google.registry.model.transfer.TransferStatus {
SERVER_APPROVED; SERVER_APPROVED;
SERVER_CANCELLED; SERVER_CANCELLED;
} }
class google.registry.persistence.DomainHistoryVKey {
com.googlecode.objectify.Key<T> ofyKey;
java.lang.Class<? extends T> kind;
java.lang.Long domainHistoryId;
java.lang.Object sqlKey;
java.lang.String domainRepoId;
}

View file

@ -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">2020-11-16 16:45:08.581361</td> <td class="property_value">2020-11-17 20:31:03.895145</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">V77__fixes_for_replay.sql</td> <td id="lastFlywayFile" class="property_value">V78__add_history_id_for_redemption_history_entry.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="5830.94" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00"> <text text-anchor="start" x="5830.94" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">
2020-11-16 16:45:08.581361 2020-11-17 20:31:03.895145
</text> </text>
<polygon fill="none" stroke="#888888" points="5743.44,-4 5743.44,-44 6008.44,-44 6008.44,-4 5743.44,-4" /> <!-- allocationtoken_a08ccbef --> <polygon fill="none" stroke="#888888" points="5743.44,-4 5743.44,-44 6008.44,-44 6008.44,-4 5743.44,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node"> <g id="node1" class="node">

File diff suppressed because it is too large Load diff

View file

@ -75,3 +75,4 @@ V74__sql_replay_checkpoint.sql
V75__add_grace_period_history.sql V75__add_grace_period_history.sql
V76__change_history_nullability.sql V76__change_history_nullability.sql
V77__fixes_for_replay.sql V77__fixes_for_replay.sql
V78__add_history_id_for_redemption_history_entry.sql

View file

@ -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;

View file

@ -22,6 +22,8 @@
discount_premiums boolean not null, discount_premiums boolean not null,
discount_years int4 not null, discount_years int4 not null,
domain_name text, domain_name text,
redemption_domain_history_id int8,
redemption_domain_repo_id text,
token_status_transitions hstore, token_status_transitions hstore,
token_type text, token_type text,
primary key (token) primary key (token)

View file

@ -48,9 +48,10 @@ CREATE TABLE public."AllocationToken" (
discount_premiums boolean NOT NULL, discount_premiums boolean NOT NULL,
discount_years integer NOT NULL, discount_years integer NOT NULL,
domain_name text, domain_name text,
redemption_history_entry text, redemption_domain_repo_id text,
token_status_transitions public.hstore, token_status_transitions public.hstore,
token_type text token_type text,
redemption_domain_history_id bigint
); );