mirror of
https://github.com/google/nomulus.git
synced 2025-04-29 19:47:51 +02:00
Stop writing PremiumList to Datastore (#1160)
* Stop writing PremiumList to Datastore * Fix formatting * Format fix * Rename the DAO * Fix merge conflicts and add comment
This commit is contained in:
parent
b9027047c4
commit
8293e1e807
34 changed files with 142 additions and 1295 deletions
|
@ -32,12 +32,12 @@ import com.google.common.net.MediaType;
|
|||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.storage.drive.DriveConnection;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
@ -139,9 +139,10 @@ public class ExportPremiumTermsAction implements Runnable {
|
|||
private String getFormattedPremiumTerms(Registry registry) {
|
||||
String premiumListName = registry.getPremiumList().getName();
|
||||
checkState(
|
||||
PremiumListDualDao.exists(premiumListName), "Could not load premium list for " + tld);
|
||||
PremiumListDao.getLatestRevision(premiumListName).isPresent(),
|
||||
"Could not load premium list for " + tld);
|
||||
SortedSet<String> premiumTerms =
|
||||
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
|
||||
Streams.stream(PremiumListDao.loadAllPremiumListEntries(premiumListName))
|
||||
.map(PremiumListEntry::toString)
|
||||
.collect(ImmutableSortedSet.toImmutableSortedSet(String::compareTo));
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ import google.registry.model.registrar.RegistrarContact;
|
|||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
@ -295,7 +295,7 @@ public final class OteAccountBuilder {
|
|||
boolean isEarlyAccess,
|
||||
int roidSuffix) {
|
||||
String tldNameAlphaNumerical = tldName.replaceAll("[^a-z0-9]", "");
|
||||
Optional<PremiumList> premiumList = PremiumListDualDao.getLatestRevision(DEFAULT_PREMIUM_LIST);
|
||||
Optional<PremiumList> premiumList = PremiumListDao.getLatestRevision(DEFAULT_PREMIUM_LIST);
|
||||
checkState(premiumList.isPresent(), "Couldn't find premium list %s.", DEFAULT_PREMIUM_LIST);
|
||||
Registry.Builder builder =
|
||||
new Registry.Builder()
|
||||
|
|
|
@ -19,7 +19,7 @@ import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
|||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.Money;
|
||||
|
@ -38,7 +38,8 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
|
|||
String tld = getTldFromDomainName(fullyQualifiedDomainName);
|
||||
String label = InternetDomainName.from(fullyQualifiedDomainName).parts().get(0);
|
||||
Registry registry = Registry.get(checkNotNull(tld, "tld"));
|
||||
Optional<Money> premiumPrice = PremiumListDualDao.getPremiumPrice(label, registry);
|
||||
Optional<Money> premiumPrice =
|
||||
PremiumListDao.getPremiumPrice(registry.getPremiumList().getName(), label);
|
||||
return DomainPrices.create(
|
||||
premiumPrice.isPresent(),
|
||||
premiumPrice.orElse(registry.getStandardCreateCost()),
|
||||
|
|
|
@ -36,7 +36,7 @@ import google.registry.model.annotations.ReportedOn;
|
|||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.replay.DatastoreOnlyEntity;
|
||||
import google.registry.schema.replay.NonReplicatedEntity;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
|
@ -173,7 +173,7 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
|||
* <p>Note that this is lazily loaded and thus will throw a {@link LazyInitializationException} if
|
||||
* used outside the transaction in which the given entity was loaded. You generally should not be
|
||||
* using this anyway as it's inefficient to load all of the PremiumEntry rows if you don't need
|
||||
* them. To check prices, use {@link PremiumListSqlDao#getPremiumPrice} instead.
|
||||
* them. To check prices, use {@link PremiumListDao#getPremiumPrice} instead.
|
||||
*/
|
||||
@Nullable
|
||||
public ImmutableMap<String, BigDecimal> getLabelsToPrices() {
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
// Copyright 2021 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.registry.label;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Iterables.partition;
|
||||
import static google.registry.config.RegistryConfig.getDomainLabelListCacheDuration;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCachePersistDuration;
|
||||
import static google.registry.config.RegistryConfig.getStaticPremiumListMaxCachedEntries;
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.BLOOM_FILTER_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_POSITIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.UNCACHED_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.UNCACHED_POSITIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListRevision;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* DAO for {@link PremiumList} objects stored in Datastore.
|
||||
*
|
||||
* <p>This class handles both the mapping from string to Datastore-level PremiumList objects as well
|
||||
* as the mapping from PremiumList objects to the contents of those premium lists in the Datastore
|
||||
* world. Specifically, this deals with retrieving the most recent revision for a given list and
|
||||
* retrieving (or writing/deleting) all entries associated with that particular revision. The {@link
|
||||
* PremiumList} object itself, in the Datastore world, does not store the premium pricing data.
|
||||
*/
|
||||
public class PremiumListDatastoreDao {
|
||||
|
||||
/** The number of premium list entry entities that are created and deleted per batch. */
|
||||
private static final int TRANSACTION_BATCH_SIZE = 200;
|
||||
|
||||
/**
|
||||
* In-memory cache for premium lists.
|
||||
*
|
||||
* <p>This is cached for a shorter duration because we need to periodically reload this entity to
|
||||
* check if a new revision has been published, and if so, then use that.
|
||||
*
|
||||
* <p>We also cache the absence of premium lists with a given name to avoid unnecessary pointless
|
||||
* lookups. Note that this cache is only applicable to PremiumList objects stored in Datastore.
|
||||
*/
|
||||
@NonFinalForTesting
|
||||
static LoadingCache<String, Optional<PremiumList>> premiumListCache =
|
||||
createPremiumListCache(getDomainLabelListCacheDuration());
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setPremiumListCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getSingletonCachePersistDuration());
|
||||
premiumListCache = createPremiumListCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static LoadingCache<String, Optional<PremiumList>> createPremiumListCache(
|
||||
Duration cachePersistDuration) {
|
||||
return CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
|
||||
.build(
|
||||
new CacheLoader<String, Optional<PremiumList>>() {
|
||||
@Override
|
||||
public Optional<PremiumList> load(final String name) {
|
||||
return tm().doTransactionless(() -> getLatestRevisionUncached(name));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* In-memory cache for {@link PremiumListRevision}s, used for retrieving Bloom filters quickly.
|
||||
*
|
||||
* <p>This is cached for a long duration (essentially indefinitely) because a given {@link
|
||||
* PremiumListRevision} is immutable and cannot ever be changed once created, so its cache need
|
||||
* not ever expire.
|
||||
*/
|
||||
static final LoadingCache<Key<PremiumListRevision>, PremiumListRevision>
|
||||
premiumListRevisionsCache =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(
|
||||
java.time.Duration.ofMillis(getSingletonCachePersistDuration().getMillis()))
|
||||
.build(
|
||||
new CacheLoader<Key<PremiumListRevision>, PremiumListRevision>() {
|
||||
@Override
|
||||
public PremiumListRevision load(final Key<PremiumListRevision> revisionKey) {
|
||||
return ofyTm()
|
||||
.doTransactionless(() -> auditedOfy().load().key(revisionKey).now());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* In-memory cache for {@link PremiumListEntry}s for a given label and {@link PremiumListRevision}
|
||||
*
|
||||
* <p>Because the PremiumList itself makes up part of the PremiumListRevision's key, this is
|
||||
* specific to a given premium list. Premium list entries might not be present, as indicated by
|
||||
* the Optional wrapper, and we want to cache that as well.
|
||||
*
|
||||
* <p>This is cached for a long duration (essentially indefinitely) because a given {@link
|
||||
* PremiumListRevision} and its child {@link PremiumListEntry}s are immutable and cannot ever be
|
||||
* changed once created, so the cache need not ever expire.
|
||||
*
|
||||
* <p>A maximum size is set here on the cache because it can potentially grow too big to fit in
|
||||
* memory if there are a large number of distinct premium list entries being queried (both those
|
||||
* that exist, as well as those that might exist according to the Bloom filter, must be cached).
|
||||
* The entries judged least likely to be accessed again will be evicted first.
|
||||
*/
|
||||
@NonFinalForTesting
|
||||
static LoadingCache<Key<PremiumListEntry>, Optional<PremiumListEntry>> premiumListEntriesCache =
|
||||
createPremiumListEntriesCache(getSingletonCachePersistDuration());
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setPremiumListEntriesCacheForTest(Optional<Duration> expiry) {
|
||||
Duration effectiveExpiry = expiry.orElse(getSingletonCachePersistDuration());
|
||||
premiumListEntriesCache = createPremiumListEntriesCache(effectiveExpiry);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static LoadingCache<Key<PremiumListEntry>, Optional<PremiumListEntry>>
|
||||
createPremiumListEntriesCache(Duration cachePersistDuration) {
|
||||
return CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
|
||||
.maximumSize(getStaticPremiumListMaxCachedEntries())
|
||||
.build(
|
||||
new CacheLoader<Key<PremiumListEntry>, Optional<PremiumListEntry>>() {
|
||||
@Override
|
||||
public Optional<PremiumListEntry> load(final Key<PremiumListEntry> entryKey) {
|
||||
return ofyTm()
|
||||
.doTransactionless(
|
||||
() -> Optional.ofNullable(auditedOfy().load().key(entryKey).now()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Optional<PremiumList> getLatestRevision(String name) {
|
||||
return premiumListCache.getUnchecked(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the premium price for the specified list, label, and TLD, or absent if the label is not
|
||||
* premium.
|
||||
*/
|
||||
public static Optional<Money> getPremiumPrice(String premiumListName, String label, String tld) {
|
||||
DateTime startTime = DateTime.now(UTC);
|
||||
Optional<PremiumList> maybePremumList = getLatestRevision(premiumListName);
|
||||
if (!maybePremumList.isPresent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
PremiumList premiumList = maybePremumList.get();
|
||||
// If we're dealing with a list from SQL, reload from Datastore if necessary
|
||||
if (premiumList.getRevisionKey() == null) {
|
||||
Optional<PremiumList> fromDatastore = getLatestRevision(premiumList.getName());
|
||||
if (fromDatastore.isPresent()) {
|
||||
premiumList = fromDatastore.get();
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
PremiumListRevision revision;
|
||||
try {
|
||||
revision = premiumListRevisionsCache.get(premiumList.getRevisionKey());
|
||||
} catch (InvalidCacheLoadException | ExecutionException e) {
|
||||
throw new RuntimeException(
|
||||
"Could not load premium list revision " + premiumList.getRevisionKey(), e);
|
||||
}
|
||||
checkState(
|
||||
revision.getProbablePremiumLabels() != null,
|
||||
"Probable premium labels Bloom filter is null on revision '%s'",
|
||||
premiumList.getRevisionKey());
|
||||
|
||||
CheckResults checkResults = checkStatus(revision, label);
|
||||
DomainLabelMetrics.recordPremiumListCheckOutcome(
|
||||
tld,
|
||||
premiumList.getName(),
|
||||
checkResults.checkOutcome(),
|
||||
DateTime.now(UTC).getMillis() - startTime.getMillis());
|
||||
|
||||
return checkResults.premiumPrice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a new or updated PremiumList object and its descendant entities to Datastore.
|
||||
*
|
||||
* <p>The flow here is: save the new premium list entries parented on that revision entity,
|
||||
* save/update the PremiumList, and then delete the old premium list entries associated with the
|
||||
* old revision.
|
||||
*
|
||||
* <p>This is the only valid way to save these kinds of entities!
|
||||
*/
|
||||
public static PremiumList save(String name, List<String> inputData) {
|
||||
PremiumList premiumList = new PremiumList.Builder().setName(name).build();
|
||||
ImmutableMap<String, PremiumListEntry> premiumListEntries = premiumList.parse(inputData);
|
||||
final Optional<PremiumList> oldPremiumList = getLatestRevisionUncached(premiumList.getName());
|
||||
|
||||
// Create the new revision (with its Bloom filter) and parent the entries on it.
|
||||
final PremiumListRevision newRevision =
|
||||
PremiumListRevision.create(premiumList, premiumListEntries.keySet());
|
||||
final Key<PremiumListRevision> newRevisionKey = Key.create(newRevision);
|
||||
ImmutableSet<PremiumListEntry> parentedEntries =
|
||||
parentPremiumListEntriesOnRevision(premiumListEntries.values(), newRevisionKey);
|
||||
|
||||
// Save the new child entities in a series of transactions.
|
||||
for (final List<PremiumListEntry> batch : partition(parentedEntries, TRANSACTION_BATCH_SIZE)) {
|
||||
ofyTm().transactNew(() -> auditedOfy().save().entities(batch));
|
||||
}
|
||||
|
||||
// Save the new PremiumList and revision itself.
|
||||
return ofyTm()
|
||||
.transactNew(
|
||||
() -> {
|
||||
DateTime now = ofyTm().getTransactionTime();
|
||||
// Assert that the premium list hasn't been changed since we started this process.
|
||||
Key<PremiumList> key =
|
||||
Key.create(getCrossTldKey(), PremiumList.class, premiumList.getName());
|
||||
Optional<PremiumList> existing =
|
||||
ofyTm().loadByKeyIfPresent(VKey.createOfy(PremiumList.class, key));
|
||||
checkOfyFieldsEqual(existing, oldPremiumList);
|
||||
PremiumList newList =
|
||||
premiumList
|
||||
.asBuilder()
|
||||
.setLastUpdateTime(now)
|
||||
.setCreationTime(
|
||||
oldPremiumList.isPresent() ? oldPremiumList.get().creationTime : now)
|
||||
.setRevision(newRevisionKey)
|
||||
.build();
|
||||
auditedOfy().save().entities(newList, newRevision);
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
return newList;
|
||||
});
|
||||
}
|
||||
|
||||
public static void delete(PremiumList premiumList) {
|
||||
ofyTm().transactNew(() -> auditedOfy().delete().entity(premiumList));
|
||||
if (premiumList.getRevisionKey() == null) {
|
||||
return;
|
||||
}
|
||||
for (final List<Key<PremiumListEntry>> batch :
|
||||
partition(
|
||||
auditedOfy()
|
||||
.load()
|
||||
.type(PremiumListEntry.class)
|
||||
.ancestor(premiumList.revisionKey)
|
||||
.keys(),
|
||||
TRANSACTION_BATCH_SIZE)) {
|
||||
ofyTm().transactNew(() -> auditedOfy().delete().keys(batch));
|
||||
batch.forEach(premiumListEntriesCache::invalidate);
|
||||
}
|
||||
ofyTm().transactNew(() -> auditedOfy().delete().key(premiumList.getRevisionKey()));
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
premiumListRevisionsCache.invalidate(premiumList.getRevisionKey());
|
||||
}
|
||||
|
||||
/** Re-parents the given {@link PremiumListEntry}s on the given {@link PremiumListRevision}. */
|
||||
@VisibleForTesting
|
||||
public static ImmutableSet<PremiumListEntry> parentPremiumListEntriesOnRevision(
|
||||
Iterable<PremiumListEntry> entries, final Key<PremiumListRevision> revisionKey) {
|
||||
return Streams.stream(entries)
|
||||
.map((PremiumListEntry entry) -> entry.asBuilder().setParent(revisionKey).build())
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link PremiumListEntry PremiumListEntries} in the given {@code premiumList}.
|
||||
*
|
||||
* <p>This is an expensive operation and should only be used when the entire list is required.
|
||||
*/
|
||||
public static Iterable<PremiumListEntry> loadPremiumListEntriesUncached(PremiumList premiumList) {
|
||||
return auditedOfy()
|
||||
.load()
|
||||
.type(PremiumListEntry.class)
|
||||
.ancestor(premiumList.revisionKey)
|
||||
.iterable();
|
||||
}
|
||||
|
||||
private static Optional<PremiumList> getLatestRevisionUncached(String name) {
|
||||
return Optional.ofNullable(
|
||||
auditedOfy().load().key(Key.create(getCrossTldKey(), PremiumList.class, name)).now());
|
||||
}
|
||||
|
||||
private static void checkOfyFieldsEqual(
|
||||
Optional<PremiumList> oneOptional, Optional<PremiumList> twoOptional) {
|
||||
if (!oneOptional.isPresent()) {
|
||||
checkState(!twoOptional.isPresent(), "Premium list concurrently deleted");
|
||||
return;
|
||||
} else {
|
||||
checkState(twoOptional.isPresent(), "Premium list concurrently deleted");
|
||||
}
|
||||
PremiumList one = oneOptional.get();
|
||||
PremiumList two = twoOptional.get();
|
||||
checkState(
|
||||
Objects.equals(one.revisionKey, two.revisionKey),
|
||||
"Premium list revision key concurrently edited");
|
||||
checkState(Objects.equals(one.name, two.name), "Premium list name concurrently edited");
|
||||
checkState(Objects.equals(one.parent, two.parent), "Premium list parent concurrently edited");
|
||||
checkState(
|
||||
Objects.equals(one.creationTime, two.creationTime),
|
||||
"Premium list creation time concurrently edited");
|
||||
}
|
||||
|
||||
private static CheckResults checkStatus(PremiumListRevision premiumListRevision, String label) {
|
||||
if (!premiumListRevision.getProbablePremiumLabels().mightContain(label)) {
|
||||
return CheckResults.create(BLOOM_FILTER_NEGATIVE, Optional.empty());
|
||||
}
|
||||
|
||||
Key<PremiumListEntry> entryKey =
|
||||
Key.create(Key.create(premiumListRevision), PremiumListEntry.class, label);
|
||||
try {
|
||||
// getIfPresent() returns null if the key is not in the cache
|
||||
Optional<PremiumListEntry> entry = premiumListEntriesCache.getIfPresent(entryKey);
|
||||
if (entry != null) {
|
||||
if (entry.isPresent()) {
|
||||
return CheckResults.create(CACHED_POSITIVE, Optional.of(entry.get().getValue()));
|
||||
} else {
|
||||
return CheckResults.create(CACHED_NEGATIVE, Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
entry = premiumListEntriesCache.get(entryKey);
|
||||
if (entry.isPresent()) {
|
||||
return CheckResults.create(UNCACHED_POSITIVE, Optional.of(entry.get().getValue()));
|
||||
} else {
|
||||
return CheckResults.create(UNCACHED_NEGATIVE, Optional.empty());
|
||||
}
|
||||
} catch (InvalidCacheLoadException | ExecutionException e) {
|
||||
throw new RuntimeException("Could not load premium list entry " + entryKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Value type class used by {@link #checkStatus} to return the results of a premiumness check. */
|
||||
@AutoValue
|
||||
abstract static class CheckResults {
|
||||
static CheckResults create(PremiumListCheckOutcome checkOutcome, Optional<Money> premiumPrice) {
|
||||
return new AutoValue_PremiumListDatastoreDao_CheckResults(checkOutcome, premiumPrice);
|
||||
}
|
||||
|
||||
abstract PremiumListCheckOutcome checkOutcome();
|
||||
|
||||
abstract Optional<Money> premiumPrice();
|
||||
}
|
||||
|
||||
private PremiumListDatastoreDao() {}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
// Copyright 2021 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.registry.label;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.DatabaseMigrationUtils.suppressExceptionUnlessInTest;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.BigMoney;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
|
||||
/**
|
||||
* DAO for {@link PremiumList} objects that handles the branching paths for SQL and Datastore.
|
||||
*
|
||||
* <p>For write actions, this class will perform the action against Cloud SQL then, after that
|
||||
* success or failure, against Datastore. If Datastore fails, an error is logged (but not thrown).
|
||||
*
|
||||
* <p>For read actions, when retrieving a price, we will log if the primary and secondary databases
|
||||
* have different values (or if the retrieval from Datastore fails).
|
||||
*/
|
||||
public class PremiumListDualDao {
|
||||
|
||||
/**
|
||||
* Retrieves from Cloud SQL and returns the most recent premium list with the given name, or
|
||||
* absent if no such list exists.
|
||||
*/
|
||||
public static Optional<PremiumList> getLatestRevision(String premiumListName) {
|
||||
return PremiumListSqlDao.getLatestRevision(premiumListName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the premium price for the specified label and registry.
|
||||
*
|
||||
* <p>Returns absent if the label is not premium or there is no premium list for this registry.
|
||||
*
|
||||
* <p>Retrieves the price from both primary and secondary databases, and logs in the event of a
|
||||
* failure in Datastore (but does not throw an exception).
|
||||
*/
|
||||
public static Optional<Money> getPremiumPrice(String label, Registry registry) {
|
||||
if (registry.getPremiumList() == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String premiumListName = registry.getPremiumList().getName();
|
||||
Optional<Money> primaryResult;
|
||||
primaryResult = PremiumListSqlDao.getPremiumPrice(premiumListName, label);
|
||||
// Also load the value from Datastore, compare the two results, and log if different.
|
||||
suppressExceptionUnlessInTest(
|
||||
() -> {
|
||||
Optional<Money> secondaryResult =
|
||||
PremiumListDatastoreDao.getPremiumPrice(premiumListName, label, registry.getTldStr());
|
||||
checkState(
|
||||
primaryResult.equals(secondaryResult),
|
||||
"Unequal prices for domain %s.%s from primary SQL DB (%s) and secondary Datastore db"
|
||||
+ " (%s).",
|
||||
label,
|
||||
registry.getTldStr(),
|
||||
primaryResult,
|
||||
secondaryResult);
|
||||
},
|
||||
String.format(
|
||||
"Error loading price of domain %s.%s from Datastore.", label, registry.getTldStr()));
|
||||
return primaryResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given list data to both primary and secondary databases.
|
||||
*
|
||||
* <p>Logs but doesn't throw an exception in the event of a failure when writing to Datastore.
|
||||
*/
|
||||
public static PremiumList save(String name, List<String> inputData) {
|
||||
PremiumList result = PremiumListSqlDao.save(name, inputData);
|
||||
suppressExceptionUnlessInTest(
|
||||
() -> PremiumListDatastoreDao.save(name, inputData),
|
||||
"Error when saving premium list to Datastore.");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the premium list.
|
||||
*
|
||||
* <p>Logs but doesn't throw an exception in the event of a failure when deleting from Datastore.
|
||||
*/
|
||||
public static void delete(PremiumList premiumList) {
|
||||
PremiumListSqlDao.delete(premiumList);
|
||||
suppressExceptionUnlessInTest(
|
||||
() -> PremiumListDatastoreDao.delete(premiumList),
|
||||
"Error when deleting premium list from Datastore.");
|
||||
}
|
||||
|
||||
/** Returns whether or not there exists a premium list with the given name. */
|
||||
public static boolean exists(String premiumListName) {
|
||||
// It may seem like overkill, but loading the list has ways been the way we check existence and
|
||||
// given that we usually load the list around the time we check existence, we'll hit the cache
|
||||
return PremiumListSqlDao.getLatestRevision(premiumListName).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link PremiumListEntry PremiumListEntries} in the list with the given name.
|
||||
*
|
||||
* <p>This is an expensive operation and should only be used when the entire list is required.
|
||||
*/
|
||||
public static Iterable<PremiumListEntry> loadAllPremiumListEntries(String premiumListName) {
|
||||
PremiumList premiumList =
|
||||
getLatestRevision(premiumListName)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("No premium list with name %s.", premiumListName)));
|
||||
CurrencyUnit currencyUnit = premiumList.getCurrency();
|
||||
return Streams.stream(PremiumListSqlDao.loadPremiumListEntriesUncached(premiumList))
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(BigMoney.of(currencyUnit, premiumEntry.getPrice()).toMoney())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.build())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private PremiumListDualDao() {}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package google.registry.schema.tld;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.config.RegistryConfig.getDomainLabelListCacheDuration;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCachePersistDuration;
|
||||
import static google.registry.config.RegistryConfig.getStaticPremiumListMaxCachedEntries;
|
||||
|
@ -25,6 +26,7 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
|
@ -32,6 +34,8 @@ import java.math.BigDecimal;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.joda.money.BigMoney;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
|
@ -43,7 +47,7 @@ import org.joda.time.Duration;
|
|||
* {@link PremiumList} object in SQL, and caching these entries so that future lookups can be
|
||||
* quicker.
|
||||
*/
|
||||
public class PremiumListSqlDao {
|
||||
public class PremiumListDao {
|
||||
|
||||
/**
|
||||
* In-memory cache for premium lists.
|
||||
|
@ -188,7 +192,7 @@ public class PremiumListSqlDao {
|
|||
*
|
||||
* <p>This is an expensive operation and should only be used when the entire list is required.
|
||||
*/
|
||||
public static Iterable<PremiumEntry> loadPremiumListEntriesUncached(PremiumList premiumList) {
|
||||
public static Iterable<PremiumEntry> loadPremiumListEntries(PremiumList premiumList) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
|
@ -220,6 +224,29 @@ public class PremiumListSqlDao {
|
|||
.findFirst());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link PremiumListEntry PremiumListEntries} in the list with the given name.
|
||||
*
|
||||
* <p>This is an expensive operation and should only be used when the entire list is required.
|
||||
*/
|
||||
public static Iterable<PremiumListEntry> loadAllPremiumListEntries(String premiumListName) {
|
||||
PremiumList premiumList =
|
||||
getLatestRevision(premiumListName)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("No premium list with name %s.", premiumListName)));
|
||||
CurrencyUnit currencyUnit = premiumList.getCurrency();
|
||||
return Streams.stream(loadPremiumListEntries(premiumList))
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(BigMoney.of(currencyUnit, premiumEntry.getPrice()).toMoney())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.build())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class RevisionIdAndLabel {
|
||||
abstract long revisionId();
|
||||
|
@ -227,9 +254,9 @@ public class PremiumListSqlDao {
|
|||
abstract String label();
|
||||
|
||||
static RevisionIdAndLabel create(long revisionId, String label) {
|
||||
return new AutoValue_PremiumListSqlDao_RevisionIdAndLabel(revisionId, label);
|
||||
return new AutoValue_PremiumListDao_RevisionIdAndLabel(revisionId, label);
|
||||
}
|
||||
}
|
||||
|
||||
private PremiumListSqlDao() {}
|
||||
private PremiumListDao() {}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
// Copyright 2021 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.tools;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumListDatastoreDao;
|
||||
import google.registry.schema.tld.PremiumEntry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.BigMoney;
|
||||
|
||||
/** Command to compare all PremiumLists in Datastore to all PremiumLists in Cloud SQL. */
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Compare all the PremiumLists in Datastore to those in Cloud SQL.")
|
||||
final class ComparePremiumListsCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ImmutableSet<String> datastoreLists =
|
||||
ofyTm().loadAllOf(PremiumList.class).stream()
|
||||
.map(PremiumList::getName)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
ImmutableSet<String> sqlLists =
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
jpaTm().loadAllOf(PremiumList.class).stream()
|
||||
.map(PremiumList::getName)
|
||||
.collect(toImmutableSet()));
|
||||
|
||||
int listsWithDiffs = 0;
|
||||
|
||||
for (String listName : Sets.difference(datastoreLists, sqlLists)) {
|
||||
listsWithDiffs++;
|
||||
System.out.printf(
|
||||
"PremiumList '%s' is present in Datastore, but not in Cloud SQL.%n", listName);
|
||||
}
|
||||
for (String listName : Sets.difference(sqlLists, datastoreLists)) {
|
||||
listsWithDiffs++;
|
||||
System.out.printf(
|
||||
"PremiumList '%s' is present in Cloud SQL, but not in Datastore.%n", listName);
|
||||
}
|
||||
|
||||
for (String listName : Sets.intersection(datastoreLists, sqlLists)) {
|
||||
Optional<PremiumList> sqlList = PremiumListSqlDao.getLatestRevision(listName);
|
||||
|
||||
// Datastore and Cloud SQL use different objects to represent premium list entries
|
||||
// so the best way to compare them is to compare their string representations.
|
||||
ImmutableSet<String> datastoreListStrings =
|
||||
Streams.stream(
|
||||
PremiumListDatastoreDao.loadPremiumListEntriesUncached(
|
||||
PremiumListDatastoreDao.getLatestRevision(listName).get()))
|
||||
.map(PremiumListEntry::toString)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
Iterable<PremiumEntry> sqlListEntries =
|
||||
jpaTm().transact(() -> PremiumListSqlDao.loadPremiumListEntriesUncached(sqlList.get()));
|
||||
|
||||
ImmutableSet<String> sqlListStrings =
|
||||
Streams.stream(sqlListEntries)
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(
|
||||
BigMoney.of(sqlList.get().getCurrency(), premiumEntry.getPrice())
|
||||
.toMoney())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.build()
|
||||
.toString())
|
||||
.collect(toImmutableSet());
|
||||
|
||||
// This will only print out the name of the unequal list. GetPremiumListCommand
|
||||
// should be used to determine what the actual differences are.
|
||||
if (!datastoreListStrings.equals(sqlListStrings)) {
|
||||
listsWithDiffs++;
|
||||
System.out.printf("PremiumList '%s' has different entries in each database.%n", listName);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.printf("Found %d unequal list(s).%n", listsWithDiffs);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ package google.registry.tools;
|
|||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
@ -51,7 +51,7 @@ abstract class CreateOrUpdatePremiumListCommand extends MutatingCommand {
|
|||
String message = String.format("Saved premium list %s with %d entries", name, inputData.size());
|
||||
try {
|
||||
logger.atInfo().log("Saving premium list for TLD %s", name);
|
||||
PremiumListSqlDao.save(name, inputData);
|
||||
PremiumListDao.save(name, inputData);
|
||||
logger.atInfo().log(message);
|
||||
} catch (Throwable e) {
|
||||
message = "Unexpected error saving premium list from nomulus tool command";
|
||||
|
|
|
@ -31,7 +31,7 @@ import google.registry.model.registry.Registry;
|
|||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.Registry.TldType;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.tools.params.OptionalStringParameter;
|
||||
import google.registry.tools.params.TransitionListParameter.BillingCostTransitions;
|
||||
import google.registry.tools.params.TransitionListParameter.TldStateTransitions;
|
||||
|
@ -344,7 +344,7 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
|||
if (premiumListName != null) {
|
||||
if (premiumListName.isPresent()) {
|
||||
Optional<PremiumList> premiumList =
|
||||
PremiumListDualDao.getLatestRevision(premiumListName.get());
|
||||
PremiumListDao.getLatestRevision(premiumListName.get());
|
||||
checkArgument(
|
||||
premiumList.isPresent(),
|
||||
String.format("The premium list '%s' doesn't exist", premiumListName.get()));
|
||||
|
|
|
@ -25,7 +25,7 @@ import com.google.common.base.Strings;
|
|||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
|||
protected void init() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
checkArgument(
|
||||
!PremiumListSqlDao.getLatestRevision(name).isPresent(),
|
||||
!PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"A premium list already exists by this name");
|
||||
if (!override) {
|
||||
// refer to CreatePremiumListAction.java
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.beust.jcommander.Parameters;
|
|||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -42,10 +42,10 @@ final class DeletePremiumListCommand extends ConfirmingCommand implements Comman
|
|||
@Override
|
||||
protected void init() {
|
||||
checkArgument(
|
||||
PremiumListDualDao.exists(name),
|
||||
PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"Cannot delete the premium list %s because it doesn't exist.",
|
||||
name);
|
||||
premiumList = PremiumListDualDao.getLatestRevision(name).get();
|
||||
premiumList = PremiumListDao.getLatestRevision(name).get();
|
||||
ImmutableSet<String> tldsUsedOn = premiumList.getReferencingTlds();
|
||||
checkArgument(
|
||||
tldsUsedOn.isEmpty(),
|
||||
|
@ -60,7 +60,7 @@ final class DeletePremiumListCommand extends ConfirmingCommand implements Comman
|
|||
|
||||
@Override
|
||||
protected String execute() {
|
||||
PremiumListDualDao.delete(premiumList);
|
||||
PremiumListDao.delete(premiumList);
|
||||
return String.format("Deleted premium list '%s'.\n", premiumList.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import com.beust.jcommander.Parameter;
|
|||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -33,11 +33,11 @@ public class GetPremiumListCommand implements CommandWithRemoteApi {
|
|||
@Override
|
||||
public void run() {
|
||||
for (String premiumListName : mainParameters) {
|
||||
if (PremiumListDualDao.exists(premiumListName)) {
|
||||
if (PremiumListDao.getLatestRevision(premiumListName).isPresent()) {
|
||||
System.out.printf(
|
||||
"%s:\n%s\n",
|
||||
premiumListName,
|
||||
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
|
||||
Streams.stream(PremiumListDao.loadAllPremiumListEntries(premiumListName))
|
||||
.sorted(Comparator.comparing(PremiumListEntry::getLabel))
|
||||
.map(PremiumListEntry::toString)
|
||||
.collect(Collectors.joining("\n")));
|
||||
|
|
|
@ -38,7 +38,6 @@ public final class RegistryTool {
|
|||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("check_domain", CheckDomainCommand.class)
|
||||
.put("check_domain_claims", CheckDomainClaimsCommand.class)
|
||||
.put("compare_premium_lists", ComparePremiumListsCommand.class)
|
||||
.put("compare_reserved_lists", CompareReservedListsCommand.class)
|
||||
.put("convert_idn", ConvertIdnCommand.class)
|
||||
.put("count_domains", CountDomainsCommand.class)
|
||||
|
|
|
@ -29,7 +29,7 @@ import google.registry.model.registry.label.PremiumList;
|
|||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumEntry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
@ -79,12 +79,12 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
|||
assertThat(persistedList.size()).isEqualTo(1);
|
||||
*/
|
||||
protected ImmutableSet<String> getExistingPremiumListEntry(String name) {
|
||||
Optional<PremiumList> list = PremiumListSqlDao.getLatestRevision(name);
|
||||
Optional<PremiumList> list = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
list.isPresent(),
|
||||
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||
Iterable<PremiumEntry> sqlListEntries =
|
||||
jpaTm().transact(() -> PremiumListSqlDao.loadPremiumListEntriesUncached(list.get()));
|
||||
jpaTm().transact(() -> PremiumListDao.loadPremiumListEntries(list.get()));
|
||||
return Streams.stream(sqlListEntries)
|
||||
.map(
|
||||
premiumEntry ->
|
||||
|
|
|
@ -21,10 +21,10 @@ import static google.registry.request.Action.Method.POST;
|
|||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -51,7 +51,9 @@ public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
|||
@Override
|
||||
protected void save() {
|
||||
checkArgument(
|
||||
!PremiumListDualDao.exists(name), "A premium list of this name already exists: %s", name);
|
||||
!PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"A premium list of this name already exists: %s",
|
||||
name);
|
||||
if (!override) {
|
||||
assertTldExists(
|
||||
name,
|
||||
|
@ -62,7 +64,7 @@ public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
|||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumListDualDao.save(name, inputDataPreProcessed);
|
||||
PremiumListDao.save(name, inputDataPreProcessed);
|
||||
String message =
|
||||
String.format("Saved premium list %s with %d entries", name, inputDataPreProcessed.size());
|
||||
logger.atInfo().log(message);
|
||||
|
|
|
@ -21,9 +21,9 @@ import static google.registry.request.Action.Method.POST;
|
|||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
@ -55,7 +55,7 @@ public final class ListPremiumListsAction extends ListObjectsAction<PremiumList>
|
|||
() ->
|
||||
jpaTm().loadAllOf(PremiumList.class).stream()
|
||||
.map(PremiumList::getName)
|
||||
.map(PremiumListDualDao::getLatestRevision)
|
||||
.map(PremiumListDao::getLatestRevision)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.peek(list -> Hibernate.initialize(list.getLabelsToPrices()))
|
||||
|
|
|
@ -21,9 +21,9 @@ import com.google.common.base.Splitter;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
@ -47,7 +47,7 @@ public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
|||
@Override
|
||||
protected void save() {
|
||||
checkArgument(
|
||||
PremiumListDualDao.exists(name),
|
||||
PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
"Could not update premium list %s because it doesn't exist.",
|
||||
name);
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
|||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumList newPremiumList = PremiumListDualDao.save(name, inputDataPreProcessed);
|
||||
PremiumList newPremiumList = PremiumListDao.save(name, inputDataPreProcessed);
|
||||
|
||||
String message =
|
||||
String.format(
|
||||
|
|
|
@ -37,8 +37,8 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.storage.drive.DriveConnection;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import java.io.IOException;
|
||||
|
@ -75,7 +75,7 @@ public class ExportPremiumTermsActionTest {
|
|||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
createTld("tld");
|
||||
PremiumList pl = PremiumListDualDao.save("pl-name", PREMIUM_NAMES);
|
||||
PremiumList pl = PremiumListDao.save("pl-name", PREMIUM_NAMES);
|
||||
persistResource(
|
||||
Registry.get("tld").asBuilder().setPremiumList(pl).setDriveFolderId("folder_id").build());
|
||||
when(driveConnection.createOrUpdateFile(
|
||||
|
@ -143,7 +143,7 @@ public class ExportPremiumTermsActionTest {
|
|||
|
||||
@Test
|
||||
void test_exportPremiumTerms_failure_noPremiumList() {
|
||||
PremiumListDualDao.delete(new PremiumList.Builder().setName("pl-name").build());
|
||||
PremiumListDao.delete(new PremiumList.Builder().setName("pl-name").build());
|
||||
assertThrows(RuntimeException.class, () -> runAction("tld"));
|
||||
|
||||
verifyNoInteractions(driveConnection);
|
||||
|
|
|
@ -42,8 +42,8 @@ import google.registry.model.EntityTestCase;
|
|||
import google.registry.model.registry.Registry.RegistryNotFoundException;
|
||||
import google.registry.model.registry.Registry.TldState;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
|
@ -251,7 +251,7 @@ public final class RegistryTest extends EntityTestCase {
|
|||
Registry registry = Registry.get("tld").asBuilder().setPremiumList(pl2).build();
|
||||
Key<PremiumList> plKey = registry.getPremiumList();
|
||||
assertThat(plKey).isNotNull();
|
||||
PremiumList stored = PremiumListDualDao.getLatestRevision(plKey.getName()).get();
|
||||
PremiumList stored = PremiumListDao.getLatestRevision(plKey.getName()).get();
|
||||
assertThat(stored.getName()).isEqualTo("tld2");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,326 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.registry.label;
|
||||
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static com.google.monitoring.metrics.contrib.DistributionMetricSubject.assertThat;
|
||||
import static com.google.monitoring.metrics.contrib.LongMetricSubject.assertThat;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.BLOOM_FILTER_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.CACHED_POSITIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.UNCACHED_NEGATIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.PremiumListCheckOutcome.UNCACHED_POSITIVE;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.premiumListChecks;
|
||||
import static google.registry.model.registry.label.DomainLabelMetrics.premiumListProcessingTime;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadPremiumListEntries;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.time.Duration.standardDays;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListRevision;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link PremiumListDatastoreDao}. */
|
||||
public class PremiumListDatastoreDaoTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
|
||||
|
||||
// Set long persist times on caches so they can be tested (cache times default to 0 in tests).
|
||||
@RegisterExtension
|
||||
public final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder()
|
||||
.withPremiumListsCache(standardDays(1))
|
||||
.withPremiumListEntriesCache(standardDays(1))
|
||||
.build();
|
||||
|
||||
private PremiumList pl;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
// createTld() overwrites the premium list, so call it before persisting pl.
|
||||
createTld("tld");
|
||||
pl =
|
||||
persistPremiumList(
|
||||
"tld",
|
||||
"lol,USD 999 # yup",
|
||||
"rich,USD 1999 #tada",
|
||||
"icann,JPY 100",
|
||||
"johnny-be-goode,USD 20.50");
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
premiumListChecks.reset();
|
||||
premiumListProcessingTime.reset();
|
||||
}
|
||||
|
||||
void assertMetricOutcomeCount(
|
||||
int checkCount, DomainLabelMetrics.PremiumListCheckOutcome outcome) {
|
||||
assertThat(premiumListChecks)
|
||||
.hasValueForLabels(checkCount, "tld", "tld", outcome.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(premiumListProcessingTime)
|
||||
.hasAnyValueForLabels("tld", "tld", outcome.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_largeNumberOfEntries_succeeds() {
|
||||
PremiumList premiumList = persistHumongousPremiumList("tld", 2500);
|
||||
assertThat(loadPremiumListEntries(premiumList)).hasSize(2500);
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "7", "tld"))
|
||||
.hasValue(Money.parse("USD 100"));
|
||||
assertMetricOutcomeCount(1, UNCACHED_POSITIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_updateTime_isUpdatedOnEverySave() {
|
||||
PremiumList pl = PremiumListDatastoreDao.save("tld3", ImmutableList.of("slime,USD 10"));
|
||||
PremiumList newPl = PremiumListDatastoreDao.save("tld3", ImmutableList.of("mutants,USD 20"));
|
||||
assertThat(newPl.getLastUpdateTime()).isGreaterThan(pl.getLastUpdateTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_creationTime_onlyUpdatedOnFirstCreation() {
|
||||
PremiumList pl = persistPremiumList("tld3", "sludge,JPY 1000");
|
||||
PremiumList newPl =
|
||||
PremiumListDatastoreDao.save("tld3", ImmutableList.of("sleighbells,CHF 2000"));
|
||||
assertThat(newPl.creationTime).isEqualTo(pl.creationTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExists() {
|
||||
assertThat(PremiumListDatastoreDao.getLatestRevision("tld")).isPresent();
|
||||
assertThat(PremiumListDatastoreDao.getLatestRevision("nonExistentPremiumList")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_comesFromBloomFilter() throws Exception {
|
||||
PremiumList pl = PremiumListDatastoreDao.getLatestRevision("tld").get();
|
||||
PremiumListEntry entry =
|
||||
persistResource(
|
||||
new PremiumListEntry.Builder()
|
||||
.setParent(pl.getRevisionKey())
|
||||
.setLabel("missingno")
|
||||
.setPrice(Money.parse("USD 1000"))
|
||||
.build());
|
||||
// "missingno" shouldn't be in the Bloom filter, thus it should return not premium without
|
||||
// attempting to load the entity that is actually present.
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "missingno", "tld")).isEmpty();
|
||||
// However, if we manually query the cache to force an entity load, it should be found.
|
||||
assertThat(
|
||||
PremiumListDatastoreDao.premiumListEntriesCache.get(
|
||||
Key.create(pl.getRevisionKey(), PremiumListEntry.class, "missingno")))
|
||||
.hasValue(entry);
|
||||
assertMetricOutcomeCount(1, BLOOM_FILTER_NEGATIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_cachedSecondTime() {
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "rich", "tld"))
|
||||
.hasValue(Money.parse("USD 1999"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "rich", "tld"))
|
||||
.hasValue(Money.parse("USD 1999"));
|
||||
assertThat(premiumListChecks)
|
||||
.hasValueForLabels(1, "tld", "tld", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "tld", CACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(premiumListProcessingTime)
|
||||
.hasAnyValueForLabels("tld", "tld", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "tld", CACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_bloomFilterFalsePositive() {
|
||||
// Remove one of the premium list entries from behind the Bloom filter's back.
|
||||
tm().transactNew(
|
||||
() ->
|
||||
auditedOfy()
|
||||
.delete()
|
||||
.keys(Key.create(pl.getRevisionKey(), PremiumListEntry.class, "rich")));
|
||||
auditedOfy().clearSessionCache();
|
||||
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "rich", "tld")).isEmpty();
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "rich", "tld")).isEmpty();
|
||||
|
||||
assertThat(premiumListChecks)
|
||||
.hasValueForLabels(1, "tld", "tld", UNCACHED_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "tld", CACHED_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(premiumListProcessingTime)
|
||||
.hasAnyValueForLabels("tld", "tld", UNCACHED_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "tld", CACHED_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_removedPremiumListEntries_areNoLongerInDatastore() {
|
||||
PremiumList pl = persistPremiumList("tld", "genius,USD 10", "dolt,JPY 1000");
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "genius", "tld"))
|
||||
.hasValue(Money.parse("USD 10"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "dolt", "tld"))
|
||||
.hasValue(Money.parse("JPY 1000"));
|
||||
assertThat(
|
||||
auditedOfy()
|
||||
.load()
|
||||
.type(PremiumListEntry.class)
|
||||
.parent(pl.getRevisionKey())
|
||||
.id("dolt")
|
||||
.now()
|
||||
.price)
|
||||
.isEqualTo(Money.parse("JPY 1000"));
|
||||
PremiumListDatastoreDao.save("tld", ImmutableList.of("genius,USD 10", "savant,USD 90"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "genius", "tld"))
|
||||
.hasValue(Money.parse("USD 10"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "savant", "tld"))
|
||||
.hasValue(Money.parse("USD 90"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "dolt", "tld")).isEmpty();
|
||||
// TODO(b/79888775): Assert that the old premium list is enqueued for later deletion.
|
||||
assertThat(premiumListChecks)
|
||||
.hasValueForLabels(4, "tld", "tld", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "tld", BLOOM_FILTER_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(premiumListProcessingTime)
|
||||
.hasAnyValueForLabels("tld", "tld", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "tld", BLOOM_FILTER_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_allLabelsAreNonPremium_whenNotInList() {
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "blah", "tld")).isEmpty();
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld", "slinge", "tld")).isEmpty();
|
||||
assertMetricOutcomeCount(2, BLOOM_FILTER_NEGATIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_simple() {
|
||||
PremiumList pl =
|
||||
PremiumListDatastoreDao.save("tld2", ImmutableList.of("lol , USD 999 # yupper rooni "));
|
||||
persistResource(Registry.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld2", "lol", "tld"))
|
||||
.hasValue(Money.parse("USD 999"));
|
||||
assertThat(PremiumListDatastoreDao.getPremiumPrice("tld2", "lol ", "tld")).isEmpty();
|
||||
ImmutableMap<String, PremiumListEntry> entries =
|
||||
Streams.stream(PremiumListDatastoreDao.loadPremiumListEntriesUncached(pl))
|
||||
.collect(toImmutableMap(PremiumListEntry::getLabel, Function.identity()));
|
||||
assertThat(entries.keySet()).containsExactly("lol");
|
||||
assertThat(entries).doesNotContainKey("lol ");
|
||||
PremiumListEntry entry = entries.get("lol");
|
||||
assertThat(entry.comment).isEqualTo("yupper rooni");
|
||||
assertThat(entry.price).isEqualTo(Money.parse("USD 999"));
|
||||
assertThat(entry.label).isEqualTo("lol");
|
||||
assertThat(premiumListChecks)
|
||||
.hasValueForLabels(1, "tld", "tld2", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasValueForLabels(1, "tld", "tld2", BLOOM_FILTER_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
assertThat(premiumListProcessingTime)
|
||||
.hasAnyValueForLabels("tld", "tld2", UNCACHED_POSITIVE.toString())
|
||||
.and()
|
||||
.hasAnyValueForLabels("tld", "tld2", BLOOM_FILTER_NEGATIVE.toString())
|
||||
.and()
|
||||
.hasNoOtherValues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_saveAndUpdateEntriesTwice() {
|
||||
PremiumList firstSave = PremiumListDatastoreDao.save("tld", ImmutableList.of("test,USD 1"));
|
||||
ImmutableMap<String, PremiumListEntry> entries =
|
||||
Streams.stream(PremiumListDatastoreDao.loadPremiumListEntriesUncached(firstSave))
|
||||
.collect(toImmutableMap(PremiumListEntry::getLabel, Function.identity()));
|
||||
assertThat(entries.keySet()).containsExactly("test");
|
||||
// Save again with no changes, and clear the cache to force a re-load from Datastore.
|
||||
PremiumList resaved = PremiumListDatastoreDao.save("tld", ImmutableList.of("test,USD 1"));
|
||||
auditedOfy().clearSessionCache();
|
||||
Map<String, PremiumListEntry> entriesReloaded =
|
||||
Streams.stream(PremiumListDatastoreDao.loadPremiumListEntriesUncached(resaved))
|
||||
.collect(toImmutableMap(PremiumListEntry::getLabel, Function.identity()));
|
||||
assertThat(entriesReloaded).hasSize(1);
|
||||
assertThat(entriesReloaded).containsKey("test");
|
||||
assertThat(entriesReloaded.get("test").parent).isEqualTo(resaved.getRevisionKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete() {
|
||||
persistPremiumList("gtld1", "trombone,USD 10");
|
||||
Optional<PremiumList> gtld1 = PremiumListDatastoreDao.getLatestRevision("gtld1");
|
||||
assertThat(gtld1).isPresent();
|
||||
Key<PremiumListRevision> parent = gtld1.get().getRevisionKey();
|
||||
PremiumListDatastoreDao.delete(gtld1.get());
|
||||
assertThat(PremiumListDatastoreDao.getLatestRevision("gtld1")).isEmpty();
|
||||
assertThat(auditedOfy().load().type(PremiumListEntry.class).ancestor(parent).list()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_largeNumberOfEntries_succeeds() {
|
||||
PremiumList large = persistHumongousPremiumList("ginormous", 2500);
|
||||
PremiumListDatastoreDao.delete(large);
|
||||
assertThat(PremiumListDatastoreDao.getLatestRevision("ginormous")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_savePremiumList_clearsCache() {
|
||||
assertThat(PremiumListDatastoreDao.premiumListCache.getIfPresent("tld")).isNull();
|
||||
PremiumList pl = PremiumListDatastoreDao.getLatestRevision("tld").get();
|
||||
assertThat(PremiumListDatastoreDao.premiumListCache.getIfPresent("tld").get()).isEqualTo(pl);
|
||||
transactIfJpaTm(() -> PremiumListDatastoreDao.save("tld", ImmutableList.of("test,USD 1")));
|
||||
assertThat(PremiumListDatastoreDao.premiumListCache.getIfPresent("tld")).isNull();
|
||||
}
|
||||
|
||||
/** Persists a premium list with a specified number of nonsense entries. */
|
||||
private PremiumList persistHumongousPremiumList(String name, int size) {
|
||||
String[] entries = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
entries[i] = String.format("%d,USD 100 # blahz", i);
|
||||
}
|
||||
return persistPremiumList(name, entries);
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright 2021 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.registry.label;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newRegistry;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.joda.time.Duration.standardDays;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.dns.writer.VoidDnsWriter;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link PremiumListDualDao}. */
|
||||
public class PremiumListDualDaoTest extends EntityTestCase {
|
||||
|
||||
// Set long persist times on caches so they can be tested (cache times default to 0 in tests).
|
||||
@RegisterExtension
|
||||
public final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder()
|
||||
.withPremiumListsCache(standardDays(1))
|
||||
.withPremiumListEntriesCache(standardDays(1))
|
||||
.build();
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
createTld("tld");
|
||||
fakeClock.setAutoIncrementByOneMilli();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void after() {
|
||||
fakeClock.disableAutoIncrement();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_secondaryLoadMissingOfy() {
|
||||
PremiumList premiumList = PremiumListDatastoreDao.getLatestRevision("tld").get();
|
||||
PremiumListDatastoreDao.delete(premiumList);
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> PremiumListDualDao.getPremiumPrice("brass", Registry.get("tld"))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Unequal prices for domain brass.tld from primary SQL DB (Optional[USD 20.00]) "
|
||||
+ "and secondary Datastore db (Optional.empty).");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_secondaryDifferentOfy() {
|
||||
PremiumListDatastoreDao.save("tld", ImmutableList.of("brass,USD 50"));
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> PremiumListDualDao.getPremiumPrice("brass", Registry.get("tld"))))
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
"Unequal prices for domain brass.tld from primary SQL DB "
|
||||
+ "(Optional[USD 20.00]) and secondary Datastore db (Optional[USD 50.00]).");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_returnsNoPriceWhenNoPremiumListConfigured() {
|
||||
createTld("ghost");
|
||||
persistResource(
|
||||
new Registry.Builder()
|
||||
.setTldStr("ghost")
|
||||
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
|
||||
.setDnsWriters(ImmutableSet.of(VoidDnsWriter.NAME))
|
||||
.build());
|
||||
assertThat(Registry.get("ghost").getPremiumList()).isNull();
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("ghost"))).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPremiumPrice_emptyWhenPremiumListDeleted() {
|
||||
PremiumList toDelete = PremiumListDualDao.getLatestRevision("tld").get();
|
||||
PremiumListDualDao.delete(toDelete);
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("blah", Registry.get("tld"))).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPremiumPrice_returnsNoneWhenNoPremiumListConfigured() {
|
||||
persistResource(newRegistry("foobar", "FOOBAR").asBuilder().setPremiumList(null).build());
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("rich", Registry.get("foobar"))).isEmpty();
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.hash.BloomFilter;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.joda.money.Money;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -68,7 +69,7 @@ public class PremiumListTest {
|
|||
|
||||
@Test
|
||||
void testBloomFilter() {
|
||||
PremiumList pl = PremiumListDualDao.getLatestRevision("tld").get();
|
||||
PremiumList pl = PremiumListDao.getLatestRevision("tld").get();
|
||||
BloomFilter<String> bloomFilter = pl.getBloomFilter();
|
||||
assertThat(bloomFilter.mightContain("notpremium")).isFalse();
|
||||
for (String label : ImmutableList.of("rich", "lol", "johnny-be-goode", "icann")) {
|
||||
|
@ -84,7 +85,7 @@ public class PremiumListTest {
|
|||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
PremiumListDualDao.getLatestRevision("tld")
|
||||
PremiumListDao.getLatestRevision("tld")
|
||||
.get()
|
||||
.parse(
|
||||
ImmutableList.of(
|
||||
|
|
|
@ -42,7 +42,7 @@ import google.registry.schema.integration.SqlIntegrationTestSuite.AfterSuiteTest
|
|||
import google.registry.schema.integration.SqlIntegrationTestSuite.BeforeSuiteTest;
|
||||
import google.registry.schema.registrar.RegistrarDaoTest;
|
||||
import google.registry.schema.replay.SqlReplayCheckpointTest;
|
||||
import google.registry.schema.tld.PremiumListSqlDaoTest;
|
||||
import google.registry.schema.tld.PremiumListDaoTest;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -92,7 +92,7 @@ import org.junit.runner.RunWith;
|
|||
KmsSecretRevisionSqlDaoTest.class,
|
||||
LockTest.class,
|
||||
PollMessageTest.class,
|
||||
PremiumListSqlDaoTest.class,
|
||||
PremiumListDaoTest.class,
|
||||
RdeRevisionTest.class,
|
||||
RegistrarDaoTest.class,
|
||||
RegistryTest.class,
|
||||
|
|
|
@ -40,8 +40,8 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link PremiumListSqlDao}. */
|
||||
public class PremiumListSqlDaoTest {
|
||||
/** Unit tests for {@link PremiumListDao}. */
|
||||
public class PremiumListDaoTest {
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
|
@ -58,7 +58,6 @@ public class PremiumListSqlDaoTest {
|
|||
public final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder()
|
||||
.withPremiumListsCache(standardDays(1))
|
||||
.withPremiumListEntriesCache(standardDays(1))
|
||||
.build();
|
||||
|
||||
private ImmutableMap<String, BigDecimal> testPrices;
|
||||
|
@ -86,12 +85,11 @@ public class PremiumListSqlDaoTest {
|
|||
|
||||
@Test
|
||||
void saveNew_worksSuccessfully() {
|
||||
PremiumListSqlDao.save(testList);
|
||||
PremiumListDao.save(testList);
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<PremiumList> persistedListOpt =
|
||||
PremiumListSqlDao.getLatestRevision("testname");
|
||||
Optional<PremiumList> persistedListOpt = PremiumListDao.getLatestRevision("testname");
|
||||
assertThat(persistedListOpt).isPresent();
|
||||
PremiumList persistedList = persistedListOpt.get();
|
||||
assertThat(persistedList.getLabelsToPrices()).containsExactlyEntriesIn(testPrices);
|
||||
|
@ -101,11 +99,11 @@ public class PremiumListSqlDaoTest {
|
|||
|
||||
@Test
|
||||
void update_worksSuccessfully() {
|
||||
PremiumListSqlDao.save(testList);
|
||||
Optional<PremiumList> persistedList = PremiumListSqlDao.getLatestRevision("testname");
|
||||
PremiumListDao.save(testList);
|
||||
Optional<PremiumList> persistedList = PremiumListDao.getLatestRevision("testname");
|
||||
assertThat(persistedList).isPresent();
|
||||
long firstRevisionId = persistedList.get().getRevisionId();
|
||||
PremiumListSqlDao.save(
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("testname")
|
||||
.setCurrency(USD)
|
||||
|
@ -122,7 +120,7 @@ public class PremiumListSqlDaoTest {
|
|||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<PremiumList> savedListOpt = PremiumListSqlDao.getLatestRevision("testname");
|
||||
Optional<PremiumList> savedListOpt = PremiumListDao.getLatestRevision("testname");
|
||||
assertThat(savedListOpt).isPresent();
|
||||
PremiumList savedList = savedListOpt.get();
|
||||
assertThat(savedList.getLabelsToPrices())
|
||||
|
@ -142,26 +140,26 @@ public class PremiumListSqlDaoTest {
|
|||
|
||||
@Test
|
||||
void checkExists_worksSuccessfully() {
|
||||
assertThat(PremiumListSqlDao.getLatestRevision("testname")).isEmpty();
|
||||
PremiumListSqlDao.save(testList);
|
||||
assertThat(PremiumListSqlDao.getLatestRevision("testname")).isPresent();
|
||||
assertThat(PremiumListDao.getLatestRevision("testname")).isEmpty();
|
||||
PremiumListDao.save(testList);
|
||||
assertThat(PremiumListDao.getLatestRevision("testname")).isPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLatestRevision_returnsEmptyForNonexistentList() {
|
||||
assertThat(PremiumListSqlDao.getLatestRevision("nonexistentlist")).isEmpty();
|
||||
assertThat(PremiumListDao.getLatestRevision("nonexistentlist")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLatestRevision_worksSuccessfully() {
|
||||
PremiumListSqlDao.save(
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(JPY)
|
||||
.setLabelsToPrices(ImmutableMap.of("wrong", BigDecimal.valueOf(1000.50)))
|
||||
.setCreationTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
PremiumListSqlDao.save(
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(JPY)
|
||||
|
@ -171,7 +169,7 @@ public class PremiumListSqlDaoTest {
|
|||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<PremiumList> persistedList = PremiumListSqlDao.getLatestRevision("list1");
|
||||
Optional<PremiumList> persistedList = PremiumListDao.getLatestRevision("list1");
|
||||
assertThat(persistedList).isPresent();
|
||||
assertThat(persistedList.get().getName()).isEqualTo("list1");
|
||||
assertThat(persistedList.get().getCurrency()).isEqualTo(JPY);
|
||||
|
@ -191,18 +189,16 @@ public class PremiumListSqlDaoTest {
|
|||
google.registry.model.registry.label.PremiumList.class,
|
||||
"premlist"))
|
||||
.build());
|
||||
PremiumListSqlDao.save(
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(testPrices)
|
||||
.setCreationTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "silver"))
|
||||
.hasValue(Money.of(USD, 10.23));
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "gold"))
|
||||
.hasValue(Money.of(USD, 1305.47));
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "zirconium")).isEmpty();
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "silver")).hasValue(Money.of(USD, 10.23));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "gold")).hasValue(Money.of(USD, 1305.47));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "zirconium")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -216,7 +212,7 @@ public class PremiumListSqlDaoTest {
|
|||
google.registry.model.registry.label.PremiumList.class,
|
||||
"premlist"))
|
||||
.build());
|
||||
PremiumListSqlDao.save(
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(JPY)
|
||||
|
@ -230,20 +226,20 @@ public class PremiumListSqlDaoTest {
|
|||
BigDecimal.valueOf(15000)))
|
||||
.setCreationTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "silver")).hasValue(moneyOf(JPY, 10));
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "gold")).hasValue(moneyOf(JPY, 1000));
|
||||
assertThat(PremiumListSqlDao.getPremiumPrice("premlist", "palladium"))
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "silver")).hasValue(moneyOf(JPY, 10));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "gold")).hasValue(moneyOf(JPY, 1000));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "palladium"))
|
||||
.hasValue(moneyOf(JPY, 15000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_savePremiumList_clearsCache() {
|
||||
assertThat(PremiumListSqlDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
PremiumListSqlDao.save(testList);
|
||||
PremiumList pl = PremiumListSqlDao.getLatestRevision("testname").get();
|
||||
assertThat(PremiumListSqlDao.premiumListCache.getIfPresent("testname").get()).isEqualTo(pl);
|
||||
transactIfJpaTm(() -> PremiumListSqlDao.save("testname", ImmutableList.of("test,USD 1")));
|
||||
assertThat(PremiumListSqlDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
PremiumListDao.save(testList);
|
||||
PremiumList pl = PremiumListDao.getLatestRevision("testname").get();
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname").get()).isEqualTo(pl);
|
||||
transactIfJpaTm(() -> PremiumListDao.save("testname", ImmutableList.of("test,USD 1")));
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
}
|
||||
|
||||
private static Money moneyOf(CurrencyUnit unit, double amount) {
|
|
@ -30,11 +30,10 @@ import static google.registry.model.EppResourceUtils.createRepoId;
|
|||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
|
||||
import static google.registry.model.ResourceTransferUtils.createTransferResponse;
|
||||
import static google.registry.model.ofy.ObjectifyService.allocateId;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.registry.label.PremiumListDatastoreDao.parentPremiumListEntriesOnRevision;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyTmOrDoNothing;
|
||||
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
|
||||
|
@ -99,8 +98,6 @@ import google.registry.model.registry.Registry.TldState;
|
|||
import google.registry.model.registry.Registry.TldType;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListRevision;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.model.registry.label.ReservedListDualDatabaseDao;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
|
@ -110,6 +107,7 @@ import google.registry.model.transfer.DomainTransferData;
|
|||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
@ -391,33 +389,14 @@ public class DatabaseHelper {
|
|||
toImmutableMap(
|
||||
Map.Entry::getKey, entry -> entry.getValue().getValue().getAmount())))
|
||||
.build();
|
||||
PremiumListRevision revision = PremiumListRevision.create(premiumList, entries.keySet());
|
||||
|
||||
ImmutableList<Object> premiumListOfyObjects =
|
||||
ImmutableList.of(
|
||||
premiumList.asBuilder().setRevision(Key.create(revision)).build(), revision);
|
||||
ImmutableSet<PremiumListEntry> entriesOnRevision =
|
||||
parentPremiumListEntriesOnRevision(entries.values(), Key.create(revision));
|
||||
if (alwaysSaveWithBackup) {
|
||||
ofyTm()
|
||||
.transact(
|
||||
() -> {
|
||||
ofyTm().putAll(premiumListOfyObjects);
|
||||
ofyTm().putAll(entriesOnRevision);
|
||||
});
|
||||
} else {
|
||||
ofyTm().putAllWithoutBackup(premiumListOfyObjects);
|
||||
ofyTm().putAllWithoutBackup(entriesOnRevision);
|
||||
}
|
||||
// Since we used to persist a PremiumList to Datastore here, it is necessary to allocate an ID
|
||||
// here to prevent breaking some of the hard-coded flow tests. IDs in tests are allocated in a
|
||||
// strictly increasing sequence, if we don't pad out the ID here, we would have to renumber
|
||||
// hundreds of unit tests.
|
||||
allocateId();
|
||||
jpaTm().transact(() -> jpaTm().insert(premiumList));
|
||||
maybeAdvanceClock();
|
||||
// The above premiumList is in the session cache and it is different from the corresponding
|
||||
// entity stored in Datastore because it has some @Ignore fields set dedicated for SQL. This
|
||||
// breaks the assumption we have in our application code, see
|
||||
// PremiumListUtils.savePremiumListAndEntries(). Clearing the session cache can help make sure
|
||||
// we always get the same list.
|
||||
tm().clearSessionCache();
|
||||
return transactIfJpaTm(() -> tm().loadByEntity(premiumList));
|
||||
return premiumList;
|
||||
}
|
||||
|
||||
/** Creates and persists a tld. */
|
||||
|
@ -1243,7 +1222,7 @@ public class DatabaseHelper {
|
|||
/** Returns the entire map of {@link PremiumListEntry}s for the given {@link PremiumList}. */
|
||||
public static ImmutableMap<String, PremiumListEntry> loadPremiumListEntries(
|
||||
PremiumList premiumList) {
|
||||
return Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumList.getName()))
|
||||
return Streams.stream(PremiumListDao.loadAllPremiumListEntries(premiumList.getName()))
|
||||
.collect(toImmutableMap(PremiumListEntry::getLabel, Function.identity()));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.index.ForeignKeyIndex;
|
||||
import google.registry.model.registry.label.PremiumListDatastoreDao;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
|
@ -72,17 +71,7 @@ public class TestCacheExtension implements BeforeEachCallback, AfterEachCallback
|
|||
public Builder withPremiumListsCache(Duration expiry) {
|
||||
cacheHandlerMap.put(
|
||||
"PremiumListSqlDao.premiumListCache",
|
||||
new TestCacheHandler(PremiumListSqlDao::setPremiumListCacheForTest, expiry));
|
||||
cacheHandlerMap.put(
|
||||
"PremiumListDatastoreDao.premiumListCache",
|
||||
new TestCacheHandler(PremiumListDatastoreDao::setPremiumListCacheForTest, expiry));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPremiumListEntriesCache(Duration expiry) {
|
||||
cacheHandlerMap.put(
|
||||
"PremiumList.cachePremiumListEntries",
|
||||
new TestCacheHandler(PremiumListDatastoreDao::setPremiumListEntriesCacheForTest, expiry));
|
||||
new TestCacheHandler(PremiumListDao::setPremiumListCacheForTest, expiry));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2021 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.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDatastoreDao;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import java.math.BigDecimal;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ComparePremiumListsCommandTest extends CommandTestCase<ComparePremiumListsCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
persistPremiumList("xn--q9jyb4c", "rich,USD 100");
|
||||
persistPremiumList("how", "richer,JPY 10000");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_success() throws Exception {
|
||||
runCommand();
|
||||
assertThat(getStdoutAsString()).isEqualTo("Found 0 unequal list(s).\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_listMissingFromCloudSql() throws Exception {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
PremiumList premiumList = PremiumListSqlDao.getLatestRevision("how").get();
|
||||
PremiumListSqlDao.delete(premiumList);
|
||||
});
|
||||
runCommand();
|
||||
assertThat(getStdoutAsString())
|
||||
.isEqualTo(
|
||||
"PremiumList 'how' is present in Datastore, but not in Cloud SQL.\n"
|
||||
+ "Found 1 unequal list(s).\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_listMissingFromDatastore() throws Exception {
|
||||
PremiumList premiumList = PremiumListDatastoreDao.getLatestRevision("how").get();
|
||||
ofyTm().transact(() -> ofyTm().delete(premiumList));
|
||||
runCommand();
|
||||
assertThat(getStdoutAsString())
|
||||
.isEqualTo(
|
||||
"PremiumList 'how' is present in Cloud SQL, but not in Datastore.\n"
|
||||
+ "Found 1 unequal list(s).\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_listsDiffer() throws Exception {
|
||||
PremiumListSqlDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("how")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(ImmutableMap.of("silver", BigDecimal.valueOf(30.03)))
|
||||
.setCreationTime(fakeClock.nowUtc())
|
||||
.build());
|
||||
runCommand();
|
||||
assertThat(getStdoutAsString())
|
||||
.isEqualTo(
|
||||
"PremiumList 'how' has different entries in each database.\n"
|
||||
+ "Found 1 unequal list(s).\n");
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -101,7 +101,7 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
|||
persistResource(
|
||||
Registry.get("baar")
|
||||
.asBuilder()
|
||||
.setPremiumList(PremiumListDualDao.getLatestRevision("baar").get())
|
||||
.setPremiumList(PremiumListDao.getLatestRevision("baar").get())
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
|
|
|
@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
|
||||
import com.google.common.io.Files;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -40,14 +40,14 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
|||
void verify_registryIsSetUpCorrectly() {
|
||||
// ensure that no premium list is created before running the command
|
||||
// this check also implicitly verifies the TLD is successfully created;
|
||||
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isFalse();
|
||||
assertThat(PremiumListDao.getLatestRevision(TLD_TEST).isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandRun_successCreateList() throws Exception {
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + premiumTermsPath);
|
||||
assertThat(registry.getTld().toString()).isEqualTo(TLD_TEST);
|
||||
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||
assertThat(PremiumListDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link DeletePremiumListCommand}. */
|
||||
|
@ -35,7 +35,7 @@ class DeletePremiumListCommandTest extends CommandTestCase<DeletePremiumListComm
|
|||
PremiumList premiumList = persistPremiumList("xn--q9jyb4c", "blah,USD 100");
|
||||
assertThat(loadPremiumListEntries(premiumList)).hasSize(1);
|
||||
runCommand("--force", "--name=xn--q9jyb4c");
|
||||
assertThat(PremiumListDualDao.getLatestRevision("xn--q9jyb4c")).isEmpty();
|
||||
assertThat(PremiumListDao.getLatestRevision("xn--q9jyb4c")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -56,7 +56,7 @@ class DeletePremiumListCommandTest extends CommandTestCase<DeletePremiumListComm
|
|||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> runCommandForced("--name=" + premiumList.getName()));
|
||||
assertThat(PremiumListDualDao.getLatestRevision(premiumList.getName())).isPresent();
|
||||
assertThat(PremiumListDao.getLatestRevision(premiumList.getName())).isPresent();
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Cannot delete premium list because it is used on these tld(s): xn--q9jyb4c");
|
||||
|
|
|
@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.Files;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -41,7 +41,7 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
|||
@Test
|
||||
void verify_registryIsSetUpCorrectly() {
|
||||
// ensure that no premium list is created before running the command
|
||||
assertThat(PremiumListSqlDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||
assertThat(PremiumListDao.getLatestRevision(TLD_TEST).isPresent()).isTrue();
|
||||
// ensure that there's value in existing premium list;
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
ImmutableSet<String> entries = command.getExistingPremiumListEntry(TLD_TEST);
|
||||
|
|
|
@ -20,9 +20,8 @@ import static google.registry.testing.DatabaseHelper.createTlds;
|
|||
import static google.registry.testing.DatabaseHelper.loadPremiumListEntries;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.FakeJsonResponse;
|
||||
import org.joda.money.Money;
|
||||
|
@ -45,7 +44,7 @@ public class CreatePremiumListActionTest {
|
|||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTlds("foo", "xn--q9jyb4c", "how");
|
||||
PremiumListDualDao.delete(PremiumListDualDao.getLatestRevision("foo").get());
|
||||
PremiumListDao.delete(PremiumListDao.getLatestRevision("foo").get());
|
||||
action = new CreatePremiumListAction();
|
||||
response = new FakeJsonResponse();
|
||||
action.response = response;
|
||||
|
@ -89,7 +88,7 @@ public class CreatePremiumListActionTest {
|
|||
action.override = true;
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(loadPremiumListEntries(PremiumListDualDao.getLatestRevision("zanzibar").get()))
|
||||
assertThat(loadPremiumListEntries(PremiumListDao.getLatestRevision("zanzibar").get()))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
|
@ -99,10 +98,10 @@ public class CreatePremiumListActionTest {
|
|||
action.inputData = "rich,USD 25\nricher,USD 1000\n";
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
PremiumList premiumList = PremiumListDualDao.getLatestRevision("foo").get();
|
||||
PremiumList premiumList = PremiumListDao.getLatestRevision("foo").get();
|
||||
assertThat(loadPremiumListEntries(premiumList)).hasSize(2);
|
||||
assertThat(PremiumListDualDao.getPremiumPrice("rich", Registry.get("foo")))
|
||||
assertThat(PremiumListDao.getPremiumPrice(premiumList.getName(), "rich"))
|
||||
.hasValue(Money.parse("USD 25"));
|
||||
assertThat(PremiumListDualDao.getPremiumPrice("diamond", Registry.get("foo"))).isEmpty();
|
||||
assertThat(PremiumListDao.getPremiumPrice(premiumList.getName(), "diamond")).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,8 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
|
|||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumListDualDao;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeJsonResponse;
|
||||
|
@ -81,26 +79,24 @@ class UpdatePremiumListActionTest {
|
|||
.omitEmptyStrings()
|
||||
.splitToList(
|
||||
readResourceUtf8(DatabaseHelper.class, "default_premium_list_testdata.csv"));
|
||||
PremiumListDualDao.save("foo", inputLines);
|
||||
PremiumListDao.save("foo", inputLines);
|
||||
action.name = "foo";
|
||||
action.inputData = "rich,USD 75\nricher,USD 5000\npoor, USD 0.99";
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
Registry registry = Registry.get("foo");
|
||||
assertThat(loadPremiumListEntries(PremiumListDualDao.getLatestRevision("foo").get()))
|
||||
.hasSize(3);
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("rich", registry))
|
||||
assertThat(loadPremiumListEntries(PremiumListDao.getLatestRevision("foo").get())).hasSize(3);
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "rich"))
|
||||
.hasValue(Money.parse("USD 75"));
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("richer", registry))
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "richer"))
|
||||
.hasValue(Money.parse("USD 5000"));
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("poor", registry))
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "poor"))
|
||||
.hasValue(Money.parse("USD 0.99"));
|
||||
Truth8.assertThat(PremiumListDualDao.getPremiumPrice("diamond", registry)).isEmpty();
|
||||
Truth8.assertThat(PremiumListDao.getPremiumPrice("foo", "diamond")).isEmpty();
|
||||
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
PremiumList persistedList = PremiumListSqlDao.getLatestRevision("foo").get();
|
||||
PremiumList persistedList = PremiumListDao.getLatestRevision("foo").get();
|
||||
assertThat(persistedList.getLabelsToPrices())
|
||||
.containsEntry("rich", new BigDecimal("75.00"));
|
||||
assertThat(persistedList.getLabelsToPrices())
|
||||
|
|
Loading…
Add table
Reference in a new issue