Add a SQL schema to AllocationToken (#763)

* Add a SQL schema to AllocationToken

* Respond to CR

- rename field in tests
- rename allowed_registrar_ids field
- remove unnecessary db load in GATC

* Add TODO for HistoryEntry vkeys

* Run autoformat

* V48 -> V49
This commit is contained in:
gbrodman 2020-08-20 20:18:34 -04:00 committed by GitHub
parent 8ac30a42ed
commit 876d65e232
23 changed files with 352 additions and 45 deletions

View file

@ -370,7 +370,8 @@ public class DomainCreateFlow implements TransactionalFlow {
if (allocationToken.isPresent() if (allocationToken.isPresent()
&& TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) { && TokenType.SINGLE_USE.equals(allocationToken.get().getTokenType())) {
entitiesToSave.add( entitiesToSave.add(
allocationTokenFlowUtils.redeemToken(allocationToken.get(), Key.create(historyEntry))); allocationTokenFlowUtils.redeemToken(
allocationToken.get(), HistoryEntry.createVKey(Key.create(historyEntry))));
} }
enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice); enqueueTasks(newDomain, hasSignedMarks, hasClaimsNotice);

View file

@ -33,6 +33,7 @@ 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.model.reporting.HistoryEntry;
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;
@ -107,7 +108,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, Key<HistoryEntry> redemptionHistoryEntry) { AllocationToken token, VKey<HistoryEntry> 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");
@ -124,7 +125,8 @@ public class AllocationTokenFlowUtils {
private void validateToken( private void validateToken(
InternetDomainName domainName, AllocationToken token, String clientId, DateTime now) InternetDomainName domainName, AllocationToken token, String clientId, DateTime now)
throws EppException { throws EppException {
if (!token.getAllowedClientIds().isEmpty() && !token.getAllowedClientIds().contains(clientId)) { if (!token.getAllowedRegistrarIds().isEmpty()
&& !token.getAllowedRegistrarIds().contains(clientId)) {
throw new AllocationTokenNotValidForRegistrarException(); throw new AllocationTokenNotValidForRegistrarException();
} }
if (!token.getAllowedTlds().isEmpty() if (!token.getAllowedTlds().isEmpty()

View file

@ -49,16 +49,32 @@ import google.registry.model.common.TimedTransitionProperty.TimedTransition;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
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 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.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** An entity representing an allocation token. */ /** An entity representing an allocation token. */
@ReportedOn @ReportedOn
@Entity @Entity
@WithStringVKey @WithStringVKey
public class AllocationToken extends BackupGroupRoot implements Buildable { @javax.persistence.Entity
@Table(
indexes = {
@javax.persistence.Index(
columnList = "token",
name = "allocation_token_token_idx",
unique = true),
@javax.persistence.Index(
columnList = "domainName",
name = "allocation_token_domain_name_idx"),
})
public class AllocationToken extends BackupGroupRoot implements Buildable, DatastoreAndSqlEntity {
// Promotions should only move forward, and ENDED / CANCELLED are terminal states. // Promotions should only move forward, and ENDED / CANCELLED are terminal states.
private static final ImmutableMultimap<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS = private static final ImmutableMultimap<TokenStatus, TokenStatus> VALID_TOKEN_STATUS_TRANSITIONS =
@ -86,19 +102,22 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
} }
/** The allocation token string. */ /** The allocation token string. */
@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. */
@Nullable @Index Key<HistoryEntry> redemptionHistoryEntry; @Nullable @Index VKey<HistoryEntry> 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;
/** When this token was created. */ /** When this token was created. */
@Column(nullable = false)
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */ /** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Nullable Set<String> allowedClientIds; @Column(name = "allowedRegistrarIds")
@Nullable
Set<String> allowedClientIds;
/** Allowed TLDs for this token, or null if all TLDs are allowed. */ /** Allowed TLDs for this token, or null if all TLDs are allowed. */
@Nullable Set<String> allowedTlds; @Nullable Set<String> allowedTlds;
@ -117,6 +136,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
int discountYears = 1; int discountYears = 1;
/** The type of the token, either single-use or unlimited-use. */ /** The type of the token, either single-use or unlimited-use. */
@Enumerated(EnumType.STRING)
TokenType tokenType; TokenType tokenType;
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1. // TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
@ -161,7 +181,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return token; return token;
} }
public Optional<Key<HistoryEntry>> getRedemptionHistoryEntry() { public Optional<VKey<HistoryEntry>> getRedemptionHistoryEntry() {
return Optional.ofNullable(redemptionHistoryEntry); return Optional.ofNullable(redemptionHistoryEntry);
} }
@ -177,7 +197,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return Optional.ofNullable(creationTime.getTimestamp()); return Optional.ofNullable(creationTime.getTimestamp());
} }
public ImmutableSet<String> getAllowedClientIds() { public ImmutableSet<String> getAllowedRegistrarIds() {
return nullToEmptyImmutableCopy(allowedClientIds); return nullToEmptyImmutableCopy(allowedClientIds);
} }
@ -260,7 +280,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return this; return this;
} }
public Builder setRedemptionHistoryEntry(Key<HistoryEntry> redemptionHistoryEntry) { public Builder setRedemptionHistoryEntry(VKey<HistoryEntry> 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;
@ -279,8 +299,8 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
return this; return this;
} }
public Builder setAllowedClientIds(Set<String> allowedClientIds) { public Builder setAllowedRegistrarIds(Set<String> allowedRegistrarIds) {
getInstance().allowedClientIds = forceEmptyToNull(allowedClientIds); getInstance().allowedClientIds = forceEmptyToNull(allowedRegistrarIds);
return this; return this;
} }

View file

@ -31,6 +31,7 @@ import google.registry.model.annotations.ReportedOn;
import google.registry.model.domain.Period; import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.WithStringVKey;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverride;
@ -49,6 +50,7 @@ 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
public class HistoryEntry extends ImmutableObject implements Buildable { public class HistoryEntry extends ImmutableObject implements Buildable {
/** Represents the type of history entry. */ /** Represents the type of history entry. */

View file

@ -0,0 +1,49 @@
// 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.converter;
import com.google.common.collect.Maps;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenStatusTransition;
import java.util.Map;
import javax.persistence.Converter;
import org.joda.time.DateTime;
/**
* JPA converter for storing/retrieving {@link TimedTransitionProperty} maps referring to {@link
* TokenStatus}es.
*/
@Converter(autoApply = true)
public class AllocationTokenStatusTransitionConverter
extends TimedTransitionPropertyConverterBase<TokenStatus, TokenStatusTransition> {
@Override
Map.Entry<String, String> convertToDatabaseMapEntry(
Map.Entry<DateTime, TokenStatusTransition> entry) {
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue().getValue().name());
}
@Override
Map.Entry<DateTime, TokenStatus> convertToEntityMapEntry(Map.Entry<String, String> entry) {
return Maps.immutableEntry(
DateTime.parse(entry.getKey()), TokenStatus.valueOf(entry.getValue()));
}
@Override
Class<TokenStatusTransition> getTimedTransitionSubclass() {
return TokenStatusTransition.class;
}
}

View file

@ -182,7 +182,8 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken(t) .setToken(t)
.setTokenType(tokenType == null ? SINGLE_USE : tokenType) .setTokenType(tokenType == null ? SINGLE_USE : tokenType)
.setAllowedClientIds(ImmutableSet.copyOf(nullToEmpty(allowedClientIds))) .setAllowedRegistrarIds(
ImmutableSet.copyOf(nullToEmpty(allowedClientIds)))
.setAllowedTlds(ImmutableSet.copyOf(nullToEmpty(allowedTlds))); .setAllowedTlds(ImmutableSet.copyOf(nullToEmpty(allowedTlds)));
Optional.ofNullable(discountFraction).ifPresent(token::setDiscountFraction); Optional.ofNullable(discountFraction).ifPresent(token::setDiscountFraction);
Optional.ofNullable(discountPremiums).ifPresent(token::setDiscountPremiums); Optional.ofNullable(discountPremiums).ifPresent(token::setDiscountPremiums);

View file

@ -16,6 +16,7 @@ package google.registry.tools;
import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
@ -59,7 +60,7 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
System.out.printf("Token %s was not redeemed.\n", token); System.out.printf("Token %s was not redeemed.\n", token);
} else { } else {
DomainBase domain = DomainBase domain =
domains.get(loadedToken.getRedemptionHistoryEntry().get().<DomainBase>getParent()); domains.get(loadedToken.getRedemptionHistoryEntry().get().getOfyKey().getParent());
if (domain == null) { if (domain == null) {
System.out.printf("ERROR: Token %s was redeemed but domain can't be loaded.\n", token); System.out.printf("ERROR: Token %s was redeemed but domain can't be loaded.\n", token);
} else { } else {
@ -82,7 +83,8 @@ final class GetAllocationTokenCommand implements CommandWithRemoteApi {
.map(AllocationToken::getRedemptionHistoryEntry) .map(AllocationToken::getRedemptionHistoryEntry)
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.map(Key::<DomainBase>getParent) .map(key -> tm().load(key))
.map(he -> (Key<DomainBase>) he.getParent())
.collect(toImmutableList()); .collect(toImmutableList());
ImmutableMap.Builder<Key<DomainBase>, DomainBase> domainsBuilder = new ImmutableMap.Builder<>(); ImmutableMap.Builder<Key<DomainBase>, DomainBase> domainsBuilder = new ImmutableMap.Builder<>();
for (List<Key<DomainBase>> keys : Lists.partition(domainKeys, BATCH_SIZE)) { for (List<Key<DomainBase>> keys : Lists.partition(domainKeys, BATCH_SIZE)) {

View file

@ -131,7 +131,7 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
private AllocationToken updateToken(AllocationToken original) { private AllocationToken updateToken(AllocationToken original) {
AllocationToken.Builder builder = original.asBuilder(); AllocationToken.Builder builder = original.asBuilder();
Optional.ofNullable(allowedClientIds) Optional.ofNullable(allowedClientIds)
.ifPresent(clientIds -> builder.setAllowedClientIds(ImmutableSet.copyOf(clientIds))); .ifPresent(clientIds -> builder.setAllowedRegistrarIds(ImmutableSet.copyOf(clientIds)));
Optional.ofNullable(allowedTlds) Optional.ofNullable(allowedTlds)
.ifPresent(tlds -> builder.setAllowedTlds(ImmutableSet.copyOf(tlds))); .ifPresent(tlds -> builder.setAllowedTlds(ImmutableSet.copyOf(tlds)));
Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction); Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction);

View file

@ -26,6 +26,7 @@
<class>google.registry.model.contact.ContactResource</class> <class>google.registry.model.contact.ContactResource</class>
<class>google.registry.model.domain.DomainBase</class> <class>google.registry.model.domain.DomainBase</class>
<class>google.registry.model.domain.DomainHistory</class> <class>google.registry.model.domain.DomainHistory</class>
<class>google.registry.model.domain.token.AllocationToken</class>
<class>google.registry.model.host.HostHistory</class> <class>google.registry.model.host.HostHistory</class>
<class>google.registry.model.host.HostResource</class> <class>google.registry.model.host.HostResource</class>
<class>google.registry.model.registrar.Registrar</class> <class>google.registry.model.registrar.Registrar</class>
@ -46,6 +47,7 @@
<class>google.registry.model.registry.label.ReservedList</class> <class>google.registry.model.registry.label.ReservedList</class>
<!-- Customized type converters --> <!-- Customized type converters -->
<class>google.registry.persistence.converter.AllocationTokenStatusTransitionConverter</class>
<class>google.registry.persistence.converter.BillingCostTransitionConverter</class> <class>google.registry.persistence.converter.BillingCostTransitionConverter</class>
<class>google.registry.persistence.converter.BillingEventFlagSetConverter</class> <class>google.registry.persistence.converter.BillingEventFlagSetConverter</class>
<class>google.registry.persistence.converter.BloomFilterConverter</class> <class>google.registry.persistence.converter.BloomFilterConverter</class>
@ -76,6 +78,7 @@
<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,6 +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 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;
@ -162,12 +163,13 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
@Test @Test
void testSuccess_oneExists_allocationTokenIsRedeemed() throws Exception { void testSuccess_oneExists_allocationTokenIsRedeemed() throws Exception {
setEppInput("domain_check_allocationtoken.xml"); setEppInput("domain_check_allocationtoken.xml");
persistActiveDomain("example1.tld"); DomainBase domain = persistActiveDomain("example1.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 1L)) .setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L, historyEntryKey))
.build()); .build());
doCheckTest( doCheckTest(
create(false, "example1.tld", "In use"), create(false, "example1.tld", "In use"),
@ -417,7 +419,7 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
.setToken("abc123") .setToken("abc123")
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setAllowedClientIds(ImmutableSet.of("someOtherClient")) .setAllowedRegistrarIds(ImmutableSet.of("someOtherClient"))
.setTokenStatusTransitions( .setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder() ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()
.put(START_OF_TIME, TokenStatus.NOT_STARTED) .put(START_OF_TIME, TokenStatus.NOT_STARTED)

View file

@ -162,6 +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.testing.TaskQueueHelper.TaskMatcher; import google.registry.testing.TaskQueueHelper.TaskMatcher;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Map; import java.util.Map;
@ -492,11 +493,13 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
"domain_create_allocationtoken.xml", "domain_create_allocationtoken.xml",
ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2")); ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "2"));
persistContactsAndHosts(); persistContactsAndHosts();
DomainBase domain = persistActiveDomain("foo.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 505L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 505L)) .setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 505L, historyEntryKey))
.build()); .build());
clock.advanceOneMilli(); clock.advanceOneMilli();
EppException thrown = EppException thrown =
@ -519,7 +522,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(Key.create(historyEntry)); .hasValue(HistoryEntry.createVKey(Key.create(historyEntry)));
} }
@Test @Test
@ -1263,7 +1266,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
ofy().load().key(Key.create(AllocationToken.class, token)).now(); ofy().load().key(Key.create(AllocationToken.class, token)).now();
assertThat(reloadedToken.isRedeemed()).isTrue(); assertThat(reloadedToken.isRedeemed()).isTrue();
assertThat(reloadedToken.getRedemptionHistoryEntry()) assertThat(reloadedToken.getRedemptionHistoryEntry())
.hasValue(Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0))); .hasValue(
HistoryEntry.createVKey(
Key.create(getHistoryEntries(reloadResourceByForeignKey()).get(0))));
} }
private void assertAllocationTokenWasNotRedeemed(String token) { private void assertAllocationTokenWasNotRedeemed(String token) {
@ -1498,7 +1503,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setAllowedClientIds(ImmutableSet.of("someClientId")) .setAllowedRegistrarIds(ImmutableSet.of("someClientId"))
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setTokenStatusTransitions( .setTokenStatusTransitions(
ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder() ImmutableSortedMap.<DateTime, TokenStatus>naturalOrder()

View file

@ -22,6 +22,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.VAL
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions; import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
@ -42,11 +43,13 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException; import google.registry.flows.domain.token.AllocationTokenFlowUtils.InvalidAllocationTokenException;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainCommand; import google.registry.model.domain.DomainCommand;
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.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.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;
@ -127,7 +130,7 @@ class AllocationTokenFlowUtilsTest {
void test_validateToken_invalidForClientId() { void test_validateToken_invalidForClientId() {
persistResource( persistResource(
createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1)) createOneMonthPromoTokenBuilder(DateTime.now(UTC).minusDays(1))
.setAllowedClientIds(ImmutableSet.of("NewRegistrar")) .setAllowedRegistrarIds(ImmutableSet.of("NewRegistrar"))
.build()); .build());
assertValidateThrowsEppException(AllocationTokenNotValidForRegistrarException.class); assertValidateThrowsEppException(AllocationTokenNotValidForRegistrarException.class);
} }
@ -189,11 +192,13 @@ class AllocationTokenFlowUtilsTest {
@Test @Test
void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() { void test_checkDomainsWithToken_showsFailureMessageForRedeemedToken() {
DomainBase domain = persistActiveDomain("example.tld");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L);
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("tokeN") .setToken("tokeN")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 101L)) .setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 101L, historyEntryKey))
.build()); .build());
assertThat( assertThat(
flowUtils flowUtils

View file

@ -16,6 +16,7 @@ package google.registry.model.domain.token;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat; import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.CANCELLED; import static google.registry.model.domain.token.AllocationToken.TokenStatus.CANCELLED;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED; import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED; import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
@ -23,7 +24,9 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.VAL
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
@ -33,15 +36,21 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase; import google.registry.model.EntityTestCase;
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.VKey;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** Unit tests for {@link AllocationToken}. */ /** Unit tests for {@link AllocationToken}. */
class AllocationTokenTest extends EntityTestCase { public class AllocationTokenTest extends EntityTestCase {
public AllocationTokenTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {
@ -53,11 +62,11 @@ class AllocationTokenTest extends EntityTestCase {
AllocationToken unlimitedUseToken = AllocationToken unlimitedUseToken =
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123Unlimited")
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z")) .setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("dev", "app")) .setAllowedTlds(ImmutableSet.of("dev", "app"))
.setAllowedClientIds(ImmutableSet.of("TheRegistrar, NewRegistrar")) .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar, NewRegistrar"))
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setDiscountPremiums(true) .setDiscountPremiums(true)
.setDiscountYears(3) .setDiscountYears(3)
@ -70,26 +79,52 @@ class AllocationTokenTest extends EntityTestCase {
.build()); .build());
assertThat(ofy().load().entity(unlimitedUseToken).now()).isEqualTo(unlimitedUseToken); assertThat(ofy().load().entity(unlimitedUseToken).now()).isEqualTo(unlimitedUseToken);
DomainBase domain = persistActiveDomain("example.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
AllocationToken singleUseToken = AllocationToken singleUseToken =
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123Single")
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 1L)) .setRedemptionHistoryEntry(HistoryEntry.createVKey(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)
.build()); .build());
assertThat(ofy().load().entity(singleUseToken).now()).isEqualTo(singleUseToken); assertThat(ofy().load().entity(singleUseToken).now()).isEqualTo(singleUseToken);
jpaTm()
.transact(
() -> {
jpaTm().saveNew(unlimitedUseToken);
jpaTm().saveNew(singleUseToken);
});
jpaTm()
.transact(
() -> {
assertAboutImmutableObjects()
.that(jpaTm().load(VKey.createSql(AllocationToken.class, "abc123Unlimited")))
.isEqualExceptFields(
unlimitedUseToken,
"creationTime",
"updateTimestamp",
"redemptionHistoryEntry");
assertAboutImmutableObjects()
.that(jpaTm().load(VKey.createSql(AllocationToken.class, "abc123Single")))
.isEqualExceptFields(
singleUseToken, "creationTime", "updateTimestamp", "redemptionHistoryEntry");
});
} }
@Test @Test
void testIndexing() throws Exception { void testIndexing() throws Exception {
DomainBase domain = persistActiveDomain("blahdomain.foo");
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1);
verifyIndexing( verifyIndexing(
persistResource( persistResource(
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("abc123") .setToken("abc123")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 1L)) .setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L, 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()),
@ -193,7 +228,7 @@ class AllocationTokenTest extends EntityTestCase {
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("foobar") .setToken("foobar")
.setTokenType(TokenType.UNLIMITED_USE) .setTokenType(TokenType.UNLIMITED_USE)
.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, "hi")); .setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1L));
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, builder::build);
assertThat(thrown) assertThat(thrown)
.hasMessageThat() .hasMessageThat()

View file

@ -0,0 +1,87 @@
// 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.converter;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.ENDED;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.ImmutableObject;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.domain.token.AllocationToken.TokenStatusTransition;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestExtension;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link AllocationTokenStatusTransitionConverter}. */
public class AllocationTokenStatusTransitionConverterTest {
@RegisterExtension
public final JpaUnitTestExtension jpa =
new JpaTestRules.Builder()
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
.withEntityClass(AllocationTokenStatusTransitionConverterTestEntity.class)
.buildUnitTestRule();
private static final ImmutableSortedMap<DateTime, TokenStatus> values =
ImmutableSortedMap.of(
START_OF_TIME,
NOT_STARTED,
DateTime.parse("2001-01-01T00:00:00.0Z"),
VALID,
DateTime.parse("2002-01-01T00:00:00.0Z"),
ENDED);
@Test
void roundTripConversion_returnsSameTimedTransitionProperty() {
TimedTransitionProperty<TokenStatus, TokenStatusTransition> timedTransitionProperty =
TimedTransitionProperty.fromValueMap(values, TokenStatusTransition.class);
AllocationTokenStatusTransitionConverterTestEntity testEntity =
new AllocationTokenStatusTransitionConverterTestEntity(timedTransitionProperty);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
AllocationTokenStatusTransitionConverterTestEntity persisted =
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.find(AllocationTokenStatusTransitionConverterTestEntity.class, "id"));
assertThat(persisted.timedTransitionProperty).containsExactlyEntriesIn(timedTransitionProperty);
}
@Entity
private static class AllocationTokenStatusTransitionConverterTestEntity extends ImmutableObject {
@Id String name = "id";
TimedTransitionProperty<TokenStatus, TokenStatusTransition> timedTransitionProperty;
private AllocationTokenStatusTransitionConverterTestEntity() {}
private AllocationTokenStatusTransitionConverterTestEntity(
TimedTransitionProperty<TokenStatus, TokenStatusTransition> timedTransitionProperty) {
this.timedTransitionProperty = timedTransitionProperty;
}
}
}

View file

@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assert_;
import google.registry.model.billing.BillingEventTest; import google.registry.model.billing.BillingEventTest;
import google.registry.model.contact.ContactResourceTest; import google.registry.model.contact.ContactResourceTest;
import google.registry.model.domain.DomainBaseSqlTest; import google.registry.model.domain.DomainBaseSqlTest;
import google.registry.model.domain.token.AllocationTokenTest;
import google.registry.model.history.ContactHistoryTest; import google.registry.model.history.ContactHistoryTest;
import google.registry.model.history.DomainHistoryTest; import google.registry.model.history.DomainHistoryTest;
import google.registry.model.history.HostHistoryTest; import google.registry.model.history.HostHistoryTest;
@ -72,6 +73,7 @@ import org.junit.runner.RunWith;
@SelectClasses({ @SelectClasses({
// BeforeSuiteTest must be the first entry. See class javadoc for details. // BeforeSuiteTest must be the first entry. See class javadoc for details.
BeforeSuiteTest.class, BeforeSuiteTest.class,
AllocationTokenTest.class,
BillingEventTest.class, BillingEventTest.class,
ClaimsListDaoTest.class, ClaimsListDaoTest.class,
ContactHistoryTest.class, ContactHistoryTest.class,

View file

@ -18,13 +18,16 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTlds; import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
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.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 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;
@ -167,12 +170,15 @@ class DeleteAllocationTokensCommandTest extends CommandTestCase<DeleteAllocation
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setDomainName(domainName); .setDomainName(domainName);
if (redeemed) { if (redeemed) {
builder.setRedemptionHistoryEntry(Key.create(HistoryEntry.class, 1051L)); String domainToPersist = domainName != null ? domainName : "example.foo";
DomainBase domain = persistActiveDomain(domainToPersist);
Key<HistoryEntry> historyEntryKey = Key.create(Key.create(domain), HistoryEntry.class, 1051L);
builder.setRedemptionHistoryEntry(VKey.create(HistoryEntry.class, 1051L, historyEntryKey));
} }
return persistResource(builder.build()); return persistResource(builder.build());
} }
private static Collection<AllocationToken> reloadTokens(AllocationToken ... tokens) { private static Collection<AllocationToken> reloadTokens(AllocationToken... tokens) {
return ofy().load().entities(tokens).values(); return ofy().load().entities(tokens).values();
} }
} }

View file

@ -37,10 +37,10 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.googlecode.objectify.Key;
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.model.reporting.HistoryEntry;
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;
@ -168,7 +168,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
new AllocationToken.Builder() new AllocationToken.Builder()
.setToken("promo123456789ABCDEFG") .setToken("promo123456789ABCDEFG")
.setTokenType(UNLIMITED_USE) .setTokenType(UNLIMITED_USE)
.setAllowedClientIds(ImmutableSet.of("TheRegistrar", "NewRegistrar")) .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar", "NewRegistrar"))
.setAllowedTlds(ImmutableSet.of("tld", "example")) .setAllowedTlds(ImmutableSet.of("tld", "example"))
.setDiscountFraction(0.5) .setDiscountFraction(0.5)
.setDiscountPremiums(true) .setDiscountPremiums(true)
@ -314,7 +314,7 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
private AllocationToken createToken( private AllocationToken createToken(
String token, String token,
@Nullable Key<HistoryEntry> redemptionHistoryEntry, @Nullable VKey<HistoryEntry> 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,6 +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 org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -83,7 +84,8 @@ class GetAllocationTokenCommandTest extends CommandTestCase<GetAllocationTokenCo
.setToken("foo") .setToken("foo")
.setTokenType(SINGLE_USE) .setTokenType(SINGLE_USE)
.setDomainName("fqqdn.tld") .setDomainName("fqqdn.tld")
.setRedemptionHistoryEntry(Key.create(createHistoryEntryForEppResource(domain))) .setRedemptionHistoryEntry(
HistoryEntry.createVKey(Key.create(createHistoryEntryForEppResource(domain))))
.build()); .build());
runCommand("foo"); runCommand("foo");
assertInStdout( assertInStdout(

View file

@ -55,9 +55,9 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
void testUpdateClientIds_setClientIds() throws Exception { void testUpdateClientIds_setClientIds() throws Exception {
AllocationToken token = AllocationToken token =
persistResource( persistResource(
builderWithPromo().setAllowedClientIds(ImmutableSet.of("toRemove")).build()); builderWithPromo().setAllowedRegistrarIds(ImmutableSet.of("toRemove")).build());
runCommandForced("--prefix", "token", "--allowed_client_ids", "clientone,clienttwo"); runCommandForced("--prefix", "token", "--allowed_client_ids", "clientone,clienttwo");
assertThat(reloadResource(token).getAllowedClientIds()) assertThat(reloadResource(token).getAllowedRegistrarIds())
.containsExactly("clientone", "clienttwo"); .containsExactly("clientone", "clienttwo");
} }
@ -65,9 +65,9 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
void testUpdateClientIds_clearClientIds() throws Exception { void testUpdateClientIds_clearClientIds() throws Exception {
AllocationToken token = AllocationToken token =
persistResource( persistResource(
builderWithPromo().setAllowedClientIds(ImmutableSet.of("toRemove")).build()); builderWithPromo().setAllowedRegistrarIds(ImmutableSet.of("toRemove")).build());
runCommandForced("--prefix", "token", "--allowed_client_ids", ""); runCommandForced("--prefix", "token", "--allowed_client_ids", "");
assertThat(reloadResource(token).getAllowedClientIds()).isEmpty(); assertThat(reloadResource(token).getAllowedRegistrarIds()).isEmpty();
} }
@Test @Test
@ -175,14 +175,14 @@ class UpdateAllocationTokensCommandTest extends CommandTestCase<UpdateAllocation
AllocationToken token = AllocationToken token =
persistResource( persistResource(
builderWithPromo() builderWithPromo()
.setAllowedClientIds(ImmutableSet.of("clientid")) .setAllowedRegistrarIds(ImmutableSet.of("clientid"))
.setAllowedTlds(ImmutableSet.of("tld")) .setAllowedTlds(ImmutableSet.of("tld"))
.setDiscountFraction(0.15) .setDiscountFraction(0.15)
.build()); .build());
runCommandForced("--prefix", "token"); runCommandForced("--prefix", "token");
AllocationToken reloaded = reloadResource(token); AllocationToken reloaded = reloadResource(token);
assertThat(reloaded.getAllowedTlds()).isEqualTo(token.getAllowedTlds()); assertThat(reloaded.getAllowedTlds()).isEqualTo(token.getAllowedTlds());
assertThat(reloaded.getAllowedClientIds()).isEqualTo(token.getAllowedClientIds()); assertThat(reloaded.getAllowedRegistrarIds()).isEqualTo(token.getAllowedRegistrarIds());
assertThat(reloaded.getDiscountFraction()).isEqualTo(token.getDiscountFraction()); assertThat(reloaded.getDiscountFraction()).isEqualTo(token.getDiscountFraction());
} }

View file

@ -233,12 +233,12 @@ class google.registry.model.domain.secdns.DelegationSignerData {
class google.registry.model.domain.token.AllocationToken { class google.registry.model.domain.token.AllocationToken {
@Id java.lang.String token; @Id java.lang.String token;
boolean discountPremiums; boolean discountPremiums;
com.googlecode.objectify.Key<google.registry.model.reporting.HistoryEntry> redemptionHistoryEntry;
double discountFraction; double discountFraction;
google.registry.model.CreateAutoTimestamp creationTime; google.registry.model.CreateAutoTimestamp creationTime;
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;
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;

View file

@ -0,0 +1,31 @@
-- 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.
CREATE TABLE "AllocationToken" (
token text NOT NULL,
update_timestamp timestamptz,
allowed_registrar_ids text[],
allowed_tlds text[],
creation_time timestamptz NOT NULL,
discount_fraction float8 NOT NULL,
discount_premiums boolean NOT NULL,
discount_years int4 NOT NULL,
domain_name text,
redemption_history_entry text,
token_status_transitions hstore,
token_type text,
PRIMARY KEY (token)
);
CREATE INDEX allocation_token_domain_name_idx ON "AllocationToken" (domain_name);

View file

@ -13,6 +13,22 @@
-- limitations under the License. -- limitations under the License.
create sequence history_id_sequence start 1 increment 1; create sequence history_id_sequence start 1 increment 1;
create table "AllocationToken" (
token text not null,
update_timestamp timestamptz,
allowed_registrar_ids text[],
allowed_tlds text[],
creation_time timestamptz not null,
discount_fraction float8 not null,
discount_premiums boolean not null,
discount_years int4 not null,
domain_name text,
redemption_history_entry text,
token_status_transitions hstore,
token_type text,
primary key (token)
);
create table "BillingCancellation" ( create table "BillingCancellation" (
billing_cancellation_id bigserial not null, billing_cancellation_id bigserial not null,
registrar_id text not null, registrar_id text not null,
@ -567,6 +583,7 @@ create sequence history_id_sequence start 1 increment 1;
contents bytea, contents bytea,
primary key (id) primary key (id)
); );
create index allocation_token_domain_name_idx on "AllocationToken" (domain_name);
create index IDXih4b2tea127p5rb61gje6e1y2 on "BillingCancellation" (registrar_id); create index IDXih4b2tea127p5rb61gje6e1y2 on "BillingCancellation" (registrar_id);
create index IDX2exdfbx6oiiwnhr8j6gjpqt2j on "BillingCancellation" (event_time); create index IDX2exdfbx6oiiwnhr8j6gjpqt2j on "BillingCancellation" (event_time);
create index IDXqa3g92jc17e8dtiaviy4fet4x on "BillingCancellation" (billing_time); create index IDXqa3g92jc17e8dtiaviy4fet4x on "BillingCancellation" (billing_time);

View file

@ -34,6 +34,26 @@ SET default_tablespace = '';
SET default_with_oids = false; SET default_with_oids = false;
--
-- Name: AllocationToken; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public."AllocationToken" (
token text NOT NULL,
update_timestamp timestamp with time zone,
allowed_registrar_ids text[],
allowed_tlds text[],
creation_time timestamp with time zone NOT NULL,
discount_fraction double precision NOT NULL,
discount_premiums boolean NOT NULL,
discount_years integer NOT NULL,
domain_name text,
redemption_history_entry text,
token_status_transitions public.hstore,
token_type text
);
-- --
-- Name: BillingCancellation; Type: TABLE; Schema: public; Owner: - -- Name: BillingCancellation; Type: TABLE; Schema: public; Owner: -
-- --
@ -985,6 +1005,14 @@ ALTER TABLE ONLY public."Spec11ThreatMatch" ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY public."Transaction" ALTER COLUMN id SET DEFAULT nextval('public."Transaction_id_seq"'::regclass); ALTER TABLE ONLY public."Transaction" ALTER COLUMN id SET DEFAULT nextval('public."Transaction_id_seq"'::regclass);
--
-- Name: AllocationToken AllocationToken_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."AllocationToken"
ADD CONSTRAINT "AllocationToken_pkey" PRIMARY KEY (token);
-- --
-- Name: BillingCancellation BillingCancellation_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- Name: BillingCancellation BillingCancellation_pkey; Type: CONSTRAINT; Schema: public; Owner: -
-- --
@ -1185,6 +1213,13 @@ ALTER TABLE ONLY public."RegistryLock"
ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id); ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id);
--
-- Name: allocation_token_domain_name_idx; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX allocation_token_domain_name_idx ON public."AllocationToken" USING btree (domain_name);
-- --
-- Name: idx1iy7njgb7wjmj9piml4l2g0qi; Type: INDEX; Schema: public; Owner: - -- Name: idx1iy7njgb7wjmj9piml4l2g0qi; Type: INDEX; Schema: public; Owner: -
-- --