mirror of
https://github.com/google/nomulus
synced 2026-09-03 06:36:56 +00:00
Remove PremiumList from Datastore schema (#1256)
* Remove PremiumList from Datastore schema * Remove commented out code * Change lastUpdateTime to creationTimestamp * Remove extra file * Remove currency unit from input data to parse * Revert extra file * Check currency in parse * Create all PremiumEntries before saving them in bulk * small fixes * Fix merge conflict
This commit is contained in:
@@ -31,7 +31,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
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.PremiumList.PremiumEntry;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
@@ -143,8 +143,8 @@ public class ExportPremiumTermsAction implements Runnable {
|
||||
PremiumListDao.getLatestRevision(premiumListName).isPresent(),
|
||||
"Could not load premium list for " + tld);
|
||||
SortedSet<String> premiumTerms =
|
||||
Streams.stream(PremiumListDao.loadAllPremiumListEntries(premiumListName))
|
||||
.map(PremiumListEntry::toString)
|
||||
Streams.stream(PremiumListDao.loadAllPremiumEntries(premiumListName))
|
||||
.map(PremiumEntry::toString)
|
||||
.collect(ImmutableSortedSet.toImmutableSortedSet(String::compareTo));
|
||||
|
||||
return Joiner.on("\n")
|
||||
|
||||
@@ -48,7 +48,7 @@ public final class ExportUtils {
|
||||
String.format("Reserved list %s does not exist", reservedListName)));
|
||||
if (reservedList.getShouldPublish()) {
|
||||
for (ReservedListEntry entry : reservedList.getReservedListEntries().values()) {
|
||||
reservedTerms.add(entry.getLabel());
|
||||
reservedTerms.add(entry.getDomainLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registry.Registry;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.ReservedList;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.server.Lock;
|
||||
@@ -91,9 +90,6 @@ public final class EntityClasses {
|
||||
PollMessage.class,
|
||||
PollMessage.Autorenew.class,
|
||||
PollMessage.OneTime.class,
|
||||
PremiumList.class,
|
||||
PremiumList.PremiumListEntry.class,
|
||||
PremiumList.PremiumListRevision.class,
|
||||
RdeRevision.class,
|
||||
Registrar.class,
|
||||
RegistrarContact.class,
|
||||
|
||||
@@ -74,14 +74,12 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
|
||||
|
||||
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
|
||||
|
||||
@Transient DateTime creationTime;
|
||||
|
||||
// The list in Cloud SQL is immutable, we only have a creation_timestamp field and it should be
|
||||
// set to the timestamp when the list is created. In Datastore, we have two fields and the
|
||||
// lastUpdateTime is set to the current timestamp when creating and updating a list. So, we use
|
||||
// lastUpdateTime as the creation_timestamp column during the dual-write phase for compatibility.
|
||||
@Column(name = "creation_timestamp")
|
||||
DateTime lastUpdateTime;
|
||||
DateTime creationTimestamp;
|
||||
|
||||
/** Returns the ID of this revision, or throws if null. */
|
||||
public long getRevisionId() {
|
||||
@@ -97,12 +95,8 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
|
||||
}
|
||||
|
||||
/** Returns the creation time of this revision of the reserved list. */
|
||||
public DateTime getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public DateTime getLastUpdateTime() {
|
||||
return lastUpdateTime;
|
||||
public DateTime getCreationTimestamp() {
|
||||
return creationTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +112,7 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
String label = entry.getLabel();
|
||||
String label = entry.getDomainLabel();
|
||||
// Check if the label was already processed for this list (which is an error), and if so,
|
||||
// accumulate it so that a list of all duplicates can be thrown.
|
||||
if (labelsToEntries.containsKey(label)) {
|
||||
@@ -196,13 +190,8 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setCreationTime(DateTime creationTime) {
|
||||
getInstance().creationTime = creationTime;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setLastUpdateTime(DateTime lastUpdateTime) {
|
||||
getInstance().lastUpdateTime = lastUpdateTime;
|
||||
public B setCreationTimestamp(DateTime creationTime) {
|
||||
getInstance().creationTimestamp = creationTime;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,17 +37,15 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
|
||||
|
||||
@Id
|
||||
@javax.persistence.Id
|
||||
@Column(name = "domain_label", nullable = false)
|
||||
String label;
|
||||
|
||||
String comment;
|
||||
@Column(name = "domainLabel", nullable = false)
|
||||
String domainLabel;
|
||||
|
||||
/**
|
||||
* Returns the label of the field, which also happens to be used as the key for the Map object
|
||||
* that is serialized from Datastore.
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
public String getDomainLabel() {
|
||||
return domainLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,30 +70,25 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
|
||||
}
|
||||
|
||||
public B setLabel(String label) {
|
||||
getInstance().label = label;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setComment(String comment) {
|
||||
getInstance().comment = comment;
|
||||
getInstance().domainLabel = label;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T build() {
|
||||
checkArgumentNotNull(emptyToNull(getInstance().label), "Label must be specified");
|
||||
checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified");
|
||||
checkArgument(
|
||||
getInstance().label.equals(canonicalizeDomainName(getInstance().label)),
|
||||
getInstance().domainLabel.equals(canonicalizeDomainName(getInstance().domainLabel)),
|
||||
"Label '%s' must be in puny-coded, lower-case form",
|
||||
getInstance().label);
|
||||
getInstance().domainLabel);
|
||||
checkArgumentNotNull(getInstance().getValue(), "Value must be specified");
|
||||
// Verify that the label creates a valid SLD if we add a TLD to the end of it.
|
||||
// We require that the label is not already a full domain name including a dot.
|
||||
// Domain name validation is tricky, so let InternetDomainName handle it for us.
|
||||
checkArgument(
|
||||
InternetDomainName.from(getInstance().label + ".tld").parts().size() == 2,
|
||||
InternetDomainName.from(getInstance().domainLabel + ".tld").parts().size() == 2,
|
||||
"Label %s must not be a multi-level domain name",
|
||||
getInstance().label);
|
||||
getInstance().domainLabel);
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,45 +18,27 @@ import static com.google.common.base.Charsets.US_ASCII;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.hash.Funnels.stringFunnel;
|
||||
import static com.google.common.hash.Funnels.unencodedCharsFunnel;
|
||||
import static google.registry.model.IdService.allocateId;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.hash.BloomFilter;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Id;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.InCrossTld;
|
||||
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.PremiumEntry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumEntry;
|
||||
import google.registry.schema.replay.SqlOnlyEntity;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.PostPersist;
|
||||
import javax.persistence.PostUpdate;
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.PreRemove;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
@@ -71,16 +53,11 @@ import org.joda.money.Money;
|
||||
* This is fine though, because we only use the list with the highest revisionId.
|
||||
*/
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@javax.persistence.Entity
|
||||
@Table(indexes = {@Index(columnList = "name", name = "premiumlist_name_idx")})
|
||||
public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.PremiumListEntry>
|
||||
implements NonReplicatedEntity {
|
||||
public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEntry>
|
||||
implements SqlOnlyEntity {
|
||||
|
||||
/** Stores the revision key for the set of currently used premium list entry entities. */
|
||||
@Transient Key<PremiumListRevision> revisionKey;
|
||||
|
||||
@Ignore
|
||||
@Column(nullable = false)
|
||||
CurrencyUnit currency;
|
||||
|
||||
@@ -91,80 +68,11 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
* from the immutability contract so we can modify it after construction and we have to handle the
|
||||
* database processing on our own so we can detach it after load.
|
||||
*/
|
||||
@Ignore @ImmutableObject.Insignificant @Transient ImmutableMap<String, BigDecimal> labelsToPrices;
|
||||
@ImmutableObject.Insignificant @Transient ImmutableMap<String, BigDecimal> labelsToPrices;
|
||||
|
||||
@Ignore
|
||||
@Column(nullable = false)
|
||||
BloomFilter<String> bloomFilter;
|
||||
|
||||
/** Virtual parent entity for premium list entry entities associated with a single revision. */
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@InCrossTld
|
||||
public static class PremiumListRevision extends ImmutableObject implements DatastoreOnlyEntity {
|
||||
|
||||
@Parent Key<PremiumList> parent;
|
||||
|
||||
@Id long revisionId;
|
||||
|
||||
/**
|
||||
* A Bloom filter that is used to determine efficiently and quickly whether a label might be
|
||||
* premium.
|
||||
*
|
||||
* <p>If the label might be premium, then the premium list entry must be loaded by key and
|
||||
* checked for existence. Otherwise, we know it's not premium, and no Datastore load is
|
||||
* required.
|
||||
*/
|
||||
private BloomFilter<String> probablePremiumLabels;
|
||||
|
||||
/**
|
||||
* Get the Bloom filter.
|
||||
*
|
||||
* <p>Note that this is not a copy, but the mutable object itself, because copying would be
|
||||
* expensive. You probably should not modify the filter unless you know what you're doing.
|
||||
*/
|
||||
public BloomFilter<String> getProbablePremiumLabels() {
|
||||
return probablePremiumLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum size of the Bloom filter.
|
||||
*
|
||||
* <p>Trying to set it any larger will throw an error, as we know it won't fit into a Datastore
|
||||
* entity. We use 90% of the 1 MB Datastore limit to leave some wriggle room for the other
|
||||
* fields and miscellaneous entity serialization overhead.
|
||||
*/
|
||||
private static final int MAX_BLOOM_FILTER_BYTES = 900000;
|
||||
|
||||
/** Returns a new PremiumListRevision for the given key and premium list map. */
|
||||
@VisibleForTesting
|
||||
public static PremiumListRevision create(PremiumList parent, Set<String> premiumLabels) {
|
||||
PremiumListRevision revision = new PremiumListRevision();
|
||||
revision.parent = Key.create(parent);
|
||||
revision.revisionId = allocateId();
|
||||
// All premium list labels are already punycoded, so don't perform any further character
|
||||
// encoding on them.
|
||||
revision.probablePremiumLabels =
|
||||
BloomFilter.create(unencodedCharsFunnel(), premiumLabels.size());
|
||||
premiumLabels.forEach(revision.probablePremiumLabels::put);
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
revision.probablePremiumLabels.writeTo(bos);
|
||||
checkArgument(
|
||||
bos.size() <= MAX_BLOOM_FILTER_BYTES,
|
||||
"Too many premium labels were specified; Bloom filter exceeds max entity size");
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Could not serialize premium labels Bloom filter", e);
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Key<PremiumListRevision> getRevisionKey() {
|
||||
return revisionKey;
|
||||
}
|
||||
|
||||
/** Returns the {@link CurrencyUnit} used for this list. */
|
||||
public CurrencyUnit getCurrency() {
|
||||
return currency;
|
||||
@@ -179,16 +87,14 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
*/
|
||||
public synchronized ImmutableMap<String, BigDecimal> getLabelsToPrices() {
|
||||
if (labelsToPrices == null) {
|
||||
Iterable<PremiumEntry> entries = PremiumListDao.loadAllPremiumEntries(name);
|
||||
labelsToPrices =
|
||||
jpaTm()
|
||||
.createQueryComposer(PremiumEntry.class)
|
||||
.where("revisionId", EQ, revisionId)
|
||||
.stream()
|
||||
Streams.stream(entries)
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
PremiumEntry::getDomainLabel,
|
||||
// Set the correct amount of precision for the premium list's currency.
|
||||
entry -> convertAmountToMoney(entry.getPrice()).getAmount()));
|
||||
premiumEntry -> convertAmountToMoney(premiumEntry.getValue()).getAmount()));
|
||||
}
|
||||
return labelsToPrices;
|
||||
}
|
||||
@@ -212,29 +118,38 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
}
|
||||
|
||||
/**
|
||||
* A premium list entry entity, persisted to Datastore. Each instance represents the price of a
|
||||
* A premium list entry entity, persisted to Cloud SQL. Each instance represents the price of a
|
||||
* single label on a given TLD.
|
||||
*/
|
||||
@ReportedOn
|
||||
@Entity
|
||||
@InCrossTld
|
||||
public static class PremiumListEntry extends DomainLabelEntry<Money, PremiumListEntry>
|
||||
implements Buildable, DatastoreOnlyEntity {
|
||||
@javax.persistence.Entity(name = "PremiumEntry")
|
||||
public static class PremiumEntry extends DomainLabelEntry<BigDecimal, PremiumList.PremiumEntry>
|
||||
implements Buildable, SqlOnlyEntity, Serializable {
|
||||
|
||||
@Parent
|
||||
Key<PremiumListRevision> parent;
|
||||
@ImmutableObject.Insignificant @javax.persistence.Id Long revisionId;
|
||||
|
||||
Money price;
|
||||
@Column(nullable = false)
|
||||
BigDecimal price;
|
||||
|
||||
@Override
|
||||
public Money getValue() {
|
||||
public BigDecimal getValue() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public static PremiumEntry create(Long revisionId, BigDecimal price, String label) {
|
||||
return new PremiumEntry.Builder()
|
||||
.setRevisionId(revisionId)
|
||||
.setPrice(price)
|
||||
.setLabel(label)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Don't include the comment so that we can use this when exporting the premium list
|
||||
return String.format("%s,%s", label, price);
|
||||
return String.format("%s, %s", domainLabel, price);
|
||||
}
|
||||
|
||||
public String toString(CurrencyUnit currencyUnit) {
|
||||
return String.format("%s,%s %s", domainLabel, currencyUnit, price);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,22 +157,22 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link PremiumListEntry} objects, since they are immutable. */
|
||||
public static class Builder extends DomainLabelEntry.Builder<PremiumListEntry, Builder> {
|
||||
/** A builder for constructing {@link PremiumEntry} objects, since they are immutable. */
|
||||
public static class Builder extends DomainLabelEntry.Builder<PremiumEntry, Builder> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
private Builder(PremiumListEntry instance) {
|
||||
private Builder(PremiumEntry instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
public Builder setParent(Key<PremiumListRevision> parentKey) {
|
||||
getInstance().parent = parentKey;
|
||||
public Builder setPrice(BigDecimal price) {
|
||||
getInstance().price = price;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPrice(Money price) {
|
||||
getInstance().price = price;
|
||||
public Builder setRevisionId(Long revisionId) {
|
||||
getInstance().revisionId = revisionId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -265,19 +180,29 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
PremiumListEntry createFromLine(String originalLine) {
|
||||
PremiumEntry createFromLine(String originalLine) {
|
||||
List<String> lineAndComment = splitOnComment(originalLine);
|
||||
if (lineAndComment.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String line = lineAndComment.get(0);
|
||||
String comment = lineAndComment.get(1);
|
||||
List<String> parts = Splitter.on(',').trimResults().splitToList(line);
|
||||
checkArgument(parts.size() == 2, "Could not parse line in premium list: %s", originalLine);
|
||||
return new PremiumListEntry.Builder()
|
||||
List<String> moneyParts = Splitter.on(' ').trimResults().splitToList(parts.get(1));
|
||||
if (moneyParts.size() == 2 && this.currency != null) {
|
||||
if (!Money.parse(parts.get(1)).getCurrencyUnit().equals(this.currency)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("The currency unit must be %s", this.currency.getCode()));
|
||||
}
|
||||
}
|
||||
BigDecimal price =
|
||||
moneyParts.size() == 2
|
||||
? Money.parse(parts.get(1)).getAmount()
|
||||
: new BigDecimal(parts.get(1));
|
||||
return new PremiumEntry.Builder()
|
||||
.setLabel(parts.get(0))
|
||||
.setPrice(Money.parse(parts.get(1)))
|
||||
.setComment(comment)
|
||||
.setPrice(price)
|
||||
.setRevisionId(revisionId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -300,11 +225,6 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
super(instance);
|
||||
}
|
||||
|
||||
public Builder setRevision(Key<PremiumListRevision> revision) {
|
||||
getInstance().revisionKey = revision;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
@@ -330,42 +250,4 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
|
||||
return super.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
lastUpdateTime = creationTime;
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
creationTime = lastUpdateTime;
|
||||
}
|
||||
|
||||
@PreRemove
|
||||
void preRemove() {
|
||||
jpaTm()
|
||||
.query("DELETE FROM PremiumEntry WHERE revision_id = :revisionId")
|
||||
.setParameter("revisionId", revisionId)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hibernate hook called on the insert of a new PremiumList. Stores the associated {@link
|
||||
* PremiumEntry}'s.
|
||||
*
|
||||
* <p>We need to persist the list entries, but only on the initial insert (not on update) since
|
||||
* the entries themselves never get changed, so we only annotate it with {@link PostPersist}, not
|
||||
* {@link PostUpdate}.
|
||||
*/
|
||||
@PostPersist
|
||||
void postPersist() {
|
||||
// If the price map is loaded, persist it too.
|
||||
if (labelsToPrices != null) {
|
||||
labelsToPrices.entrySet().stream()
|
||||
.forEach(
|
||||
entry ->
|
||||
jpaTm()
|
||||
.insert(PremiumEntry.create(revisionId, entry.getValue(), entry.getKey())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +131,21 @@ public final class ReservedList
|
||||
@Column(nullable = false)
|
||||
ReservationType reservationType;
|
||||
|
||||
String comment;
|
||||
|
||||
/** Mapper for use with @Mapify */
|
||||
static class LabelMapper implements Mapper<String, ReservedListEntry> {
|
||||
|
||||
@Override
|
||||
public String getKey(ReservedListEntry entry) {
|
||||
return entry.getLabel();
|
||||
return entry.getDomainLabel();
|
||||
}
|
||||
}
|
||||
|
||||
public String getComment(String comment) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
/** Creates a {@link ReservedListEntry} from a label, reservation type, and optional comment. */
|
||||
public static ReservedListEntry create(
|
||||
String label, ReservationType reservationType, @Nullable String comment) {
|
||||
@@ -163,7 +169,7 @@ public final class ReservedList
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%s,%s%s", label, reservationType, isNullOrEmpty(comment) ? "" : " # " + comment);
|
||||
"%s,%s%s", domainLabel, reservationType, isNullOrEmpty(comment) ? "" : " # " + comment);
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link ReservedListEntry} objects, since they are immutable. */
|
||||
@@ -180,6 +186,11 @@ public final class ReservedList
|
||||
getInstance().reservationType = reservationType;
|
||||
return this;
|
||||
}
|
||||
|
||||
ReservedListEntry.Builder setComment(String comment) {
|
||||
getInstance().comment = comment;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +228,7 @@ public final class ReservedList
|
||||
.createQueryComposer(ReservedListEntry.class)
|
||||
.where("revisionId", EQ, revisionId)
|
||||
.stream()
|
||||
.collect(toImmutableMap(ReservedListEntry::getLabel, e -> e)));
|
||||
.collect(toImmutableMap(ReservedListEntry::getDomainLabel, e -> e)));
|
||||
}
|
||||
return ImmutableMap.copyOf(nullToEmpty(reservedListMap));
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA converter for a {@link Key} containing a {@link PremiumList} */
|
||||
@Converter(autoApply = true)
|
||||
public class PremiumListKeyConverter implements AttributeConverter<Key<PremiumList>, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Key<PremiumList> attribute) {
|
||||
return (attribute == null) ? null : attribute.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<PremiumList> convertToEntityAttribute(String dbData) {
|
||||
return (dbData == null) ? null : Key.create(getCrossTldKey(), PremiumList.class, dbData);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2019 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.schema.tld;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.schema.replay.SqlOnlyEntity;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* Entity class for the premium price of an individual domain label.
|
||||
*
|
||||
* <p>These are not persisted directly, but rather, using {@link PremiumList#getLabelsToPrices()}.
|
||||
*/
|
||||
@Entity
|
||||
public class PremiumEntry extends ImmutableObject implements Serializable, SqlOnlyEntity {
|
||||
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
Long revisionId;
|
||||
|
||||
@Column(nullable = false)
|
||||
BigDecimal price;
|
||||
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
String domainLabel;
|
||||
|
||||
private PremiumEntry() {}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public String getDomainLabel() {
|
||||
return domainLabel;
|
||||
}
|
||||
|
||||
public static PremiumEntry create(long revisionId, BigDecimal price, String domainLabel) {
|
||||
PremiumEntry result = new PremiumEntry();
|
||||
result.revisionId = revisionId;
|
||||
result.price = price;
|
||||
result.domainLabel = domainLabel;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,15 @@ 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.ImmutableSet;
|
||||
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.PremiumList.PremiumEntry;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
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;
|
||||
@@ -153,18 +153,48 @@ public class PremiumListDao {
|
||||
}
|
||||
}
|
||||
|
||||
public static PremiumList save(String name, List<String> inputData) {
|
||||
return save(PremiumListUtils.parseToPremiumList(name, inputData));
|
||||
public static PremiumList save(String name, CurrencyUnit currencyUnit, List<String> inputData) {
|
||||
return save(PremiumListUtils.parseToPremiumList(name, currencyUnit, inputData));
|
||||
}
|
||||
|
||||
public static PremiumList save(PremiumList premiumList) {
|
||||
jpaTm().transact(() -> jpaTm().insert(premiumList));
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
if (premiumList.getLabelsToPrices() != null) {
|
||||
Optional<PremiumList> savedPremiumList =
|
||||
PremiumListDao.getLatestRevision(premiumList.getName());
|
||||
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
|
||||
premiumList.getLabelsToPrices().entrySet().stream()
|
||||
.forEach(
|
||||
entry ->
|
||||
entries.add(
|
||||
PremiumEntry.create(
|
||||
savedPremiumList.get().getRevisionId(),
|
||||
entry.getValue(),
|
||||
entry.getKey())));
|
||||
jpaTm().insertAll(entries.build());
|
||||
}
|
||||
});
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
return premiumList;
|
||||
}
|
||||
|
||||
public static void delete(PremiumList premiumList) {
|
||||
jpaTm().transact(() -> getLatestRevision(premiumList.getName()).ifPresent(jpaTm()::delete));
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
Optional<PremiumList> persistedList = getLatestRevision(premiumList.getName());
|
||||
if (persistedList.isPresent()) {
|
||||
jpaTm()
|
||||
.query("DELETE FROM PremiumEntry WHERE revisionId = :revisionId")
|
||||
.setParameter("revisionId", persistedList.get().getRevisionId())
|
||||
.executeUpdate();
|
||||
jpaTm().delete(persistedList.get());
|
||||
}
|
||||
});
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
}
|
||||
|
||||
@@ -183,11 +213,11 @@ public class PremiumListDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link PremiumListEntry PremiumListEntries} in the given {@code premiumList}.
|
||||
* Returns all {@link PremiumEntry PremiumEntries} 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<PremiumEntry> loadPremiumListEntries(PremiumList premiumList) {
|
||||
public static Iterable<PremiumEntry> loadPremiumEntries(PremiumList premiumList) {
|
||||
return jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
@@ -220,24 +250,25 @@ public class PremiumListDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link PremiumListEntry PremiumListEntries} in the list with the given name.
|
||||
* Returns all {@link PremiumEntry PremiumEntries} 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) {
|
||||
public static Iterable<PremiumEntry> loadAllPremiumEntries(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))
|
||||
Iterable<PremiumEntry> entries = loadPremiumEntries(premiumList);
|
||||
return Streams.stream(entries)
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(BigMoney.of(currencyUnit, premiumEntry.getPrice()).toMoney())
|
||||
new PremiumEntry.Builder()
|
||||
.setPrice(premiumEntry.getValue())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.setRevisionId(premiumList.getRevisionId())
|
||||
.build())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
@@ -15,16 +15,12 @@
|
||||
package google.registry.schema.tld;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumEntry;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,28 +30,18 @@ import org.joda.time.DateTime;
|
||||
/** Static utility methods for {@link PremiumList}. */
|
||||
public class PremiumListUtils {
|
||||
|
||||
public static PremiumList parseToPremiumList(String name, List<String> inputData) {
|
||||
ImmutableMap<String, PremiumListEntry> prices =
|
||||
new PremiumList.Builder().setName(name).build().parse(inputData);
|
||||
ImmutableSet<CurrencyUnit> currencies =
|
||||
prices.values().stream()
|
||||
.map(e -> e.getValue().getCurrencyUnit())
|
||||
.distinct()
|
||||
.collect(toImmutableSet());
|
||||
checkArgument(
|
||||
currencies.size() == 1,
|
||||
"The Cloud SQL schema requires exactly one currency, but got: %s",
|
||||
ImmutableSortedSet.copyOf(currencies));
|
||||
CurrencyUnit currency = Iterables.getOnlyElement(currencies);
|
||||
|
||||
Map<String, BigDecimal> priceAmounts =
|
||||
Maps.transformValues(prices, ple -> ple.getValue().getAmount());
|
||||
return new PremiumList.Builder()
|
||||
.setName(name)
|
||||
.setCurrency(currency)
|
||||
.setLabelsToPrices(priceAmounts)
|
||||
.setCreationTime(DateTime.now(UTC))
|
||||
.build();
|
||||
public static PremiumList parseToPremiumList(
|
||||
String name, CurrencyUnit currencyUnit, List<String> inputData) {
|
||||
PremiumList partialPremiumList =
|
||||
new PremiumList.Builder()
|
||||
.setName(name)
|
||||
.setCurrency(currencyUnit)
|
||||
.setCreationTimestamp(DateTime.now(UTC))
|
||||
.build();
|
||||
ImmutableMap<String, PremiumEntry> prices = partialPremiumList.parse(inputData);
|
||||
checkArgument(inputData.size() > 0, "Input cannot be empty");
|
||||
Map<String, BigDecimal> priceAmounts = Maps.transformValues(prices, PremiumEntry::getValue);
|
||||
return partialPremiumList.asBuilder().setLabelsToPrices(priceAmounts).build();
|
||||
}
|
||||
|
||||
private PremiumListUtils() {}
|
||||
|
||||
@@ -21,15 +21,18 @@ import google.registry.tools.params.PathParameter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Base class for specification of command line parameters common to creating and updating premium
|
||||
* lists.
|
||||
*/
|
||||
abstract class CreateOrUpdatePremiumListCommand extends MutatingCommand {
|
||||
abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
||||
implements CommandWithRemoteApi {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
protected List<String> inputData;
|
||||
protected CurrencyUnit currency;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
@@ -51,7 +54,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);
|
||||
PremiumListDao.save(name, inputData);
|
||||
PremiumListDao.save(name, currency, inputData);
|
||||
logger.atInfo().log(message);
|
||||
} catch (Throwable e) {
|
||||
message = "Unexpected error saving premium list from nomulus tool command";
|
||||
|
||||
@@ -22,12 +22,10 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
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.PremiumListDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/** Command to create a {@link PremiumList} on Database. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Database.")
|
||||
@@ -38,9 +36,16 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
description = "Override restrictions on premium list naming")
|
||||
boolean override;
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--currency"},
|
||||
description = "CurrencyUnit for the list",
|
||||
required = true)
|
||||
String currencyUnit;
|
||||
|
||||
@Override
|
||||
// Using CreatePremiumListAction.java as reference;
|
||||
protected void init() throws Exception {
|
||||
protected String prompt() throws Exception {
|
||||
currency = CurrencyUnit.of(currencyUnit);
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
checkArgument(
|
||||
!PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
@@ -53,9 +58,6 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
+ " (unless --override is specified), yet TLD %s does not exist");
|
||||
}
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
// create a premium list with only input data and store as the first version of the entity
|
||||
PremiumList newPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||
stageEntityChange(
|
||||
null, newPremiumList, VKey.createOfy(PremiumList.class, Key.create(newPremiumList)));
|
||||
return String.format("Create new premium list for %s?", name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,7 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||
.setName(name)
|
||||
.setReservedListMapFromLines(allLines)
|
||||
.setShouldPublish(shouldPublish)
|
||||
.setCreationTime(now)
|
||||
.setLastUpdateTime(now)
|
||||
.setCreationTimestamp(now)
|
||||
.build();
|
||||
|
||||
// calls the stageEntityChange method that takes old entity, new entity and a new vkey;
|
||||
|
||||
@@ -17,10 +17,12 @@ package google.registry.tools;
|
||||
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.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumEntry;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Retrieves and prints one or more premium lists. */
|
||||
@@ -33,13 +35,14 @@ public class GetPremiumListCommand implements CommandWithRemoteApi {
|
||||
@Override
|
||||
public void run() {
|
||||
for (String premiumListName : mainParameters) {
|
||||
if (PremiumListDao.getLatestRevision(premiumListName).isPresent()) {
|
||||
Optional<PremiumList> premiumList = PremiumListDao.getLatestRevision(premiumListName);
|
||||
if (premiumList.isPresent()) {
|
||||
System.out.printf(
|
||||
"%s:\n%s\n",
|
||||
premiumListName,
|
||||
Streams.stream(PremiumListDao.loadAllPremiumListEntries(premiumListName))
|
||||
.sorted(Comparator.comparing(PremiumListEntry::getLabel))
|
||||
.map(PremiumListEntry::toString)
|
||||
Streams.stream(PremiumListDao.loadAllPremiumEntries(premiumListName))
|
||||
.sorted(Comparator.comparing(PremiumEntry::getDomainLabel))
|
||||
.map(premiumEntry -> premiumEntry.toString(premiumList.get().getCurrency()))
|
||||
.collect(Collectors.joining("\n")));
|
||||
} else {
|
||||
System.out.printf("No list found with name %s.%n", premiumListName);
|
||||
|
||||
@@ -42,7 +42,7 @@ public class GetReservedListCommand implements CommandWithRemoteApi {
|
||||
.get()
|
||||
.getReservedListEntries()
|
||||
.values())
|
||||
.sorted(Comparator.comparing(ReservedListEntry::getLabel))
|
||||
.sorted(Comparator.comparing(ReservedListEntry::getDomainLabel))
|
||||
.map(ReservedListEntry::toString)
|
||||
.collect(Collectors.joining("\n")));
|
||||
} else {
|
||||
|
||||
@@ -24,17 +24,13 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
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.model.registry.label.PremiumList.PremiumEntry;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.BigMoney;
|
||||
|
||||
/** Command to safely update {@link PremiumList} in Database for a given TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.")
|
||||
@@ -42,23 +38,23 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
@Override
|
||||
// Using UpdatePremiumListAction.java as reference;
|
||||
protected void init() throws Exception {
|
||||
protected String prompt() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
List<String> existingEntry = getExistingPremiumListEntry(name).asList();
|
||||
Optional<PremiumList> list = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
list.isPresent(),
|
||||
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||
List<String> existingEntry = getExistingPremiumEntry(list.get()).asList();
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
|
||||
currency = list.get().getCurrency();
|
||||
// reconstructing existing premium list to bypass Hibernate lazy initialization exception
|
||||
PremiumList existingPremiumList = PremiumListUtils.parseToPremiumList(name, existingEntry);
|
||||
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||
PremiumList existingPremiumList =
|
||||
PremiumListUtils.parseToPremiumList(name, currency, existingEntry);
|
||||
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, currency, inputData);
|
||||
|
||||
// use LabelsToPrices() for comparison between old and new premium lists since they have
|
||||
// different creation date, updated date even if they have same content;
|
||||
if (!existingPremiumList.getLabelsToPrices().equals(updatedPremiumList.getLabelsToPrices())) {
|
||||
stageEntityChange(
|
||||
existingPremiumList,
|
||||
updatedPremiumList,
|
||||
VKey.createOfy(PremiumList.class, Key.create(existingPremiumList)));
|
||||
}
|
||||
return String.format(
|
||||
"Update premium list for %s?\n Old List: %s\n New List: %s",
|
||||
name, existingPremiumList, updatedPremiumList);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -78,22 +74,16 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
assertThat(persistedList.getLabelsToPrices()).containsEntry("foo", new BigDecimal("9000.00"));
|
||||
assertThat(persistedList.size()).isEqualTo(1);
|
||||
*/
|
||||
protected ImmutableSet<String> getExistingPremiumListEntry(String name) {
|
||||
Optional<PremiumList> list = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
list.isPresent(),
|
||||
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||
protected ImmutableSet<String> getExistingPremiumEntry(PremiumList list) {
|
||||
|
||||
Iterable<PremiumEntry> sqlListEntries =
|
||||
jpaTm().transact(() -> PremiumListDao.loadPremiumListEntries(list.get()));
|
||||
jpaTm().transact(() -> PremiumListDao.loadPremiumEntries(list));
|
||||
return Streams.stream(sqlListEntries)
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(
|
||||
BigMoney.of(list.get().getCurrency(), premiumEntry.getPrice()).toMoney())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.build()
|
||||
.toString())
|
||||
String.format(
|
||||
"%s,%s %s",
|
||||
premiumEntry.getDomainLabel(), list.getCurrency(), premiumEntry.getValue()))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* An action that creates a premium list, for use by the {@code nomulus create_premium_list}
|
||||
@@ -43,9 +44,14 @@ public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
public static final String OVERRIDE_PARAM = "override";
|
||||
public static final String PATH = "/_dr/admin/createPremiumList";
|
||||
public static final String CURRENCY = "currency";
|
||||
|
||||
@Inject @Parameter(OVERRIDE_PARAM) boolean override;
|
||||
|
||||
@Inject
|
||||
@Parameter("currency")
|
||||
CurrencyUnit currency;
|
||||
|
||||
@Inject CreatePremiumListAction() {}
|
||||
|
||||
@Override
|
||||
@@ -64,7 +70,7 @@ public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumListDao.save(name, inputDataPreProcessed);
|
||||
PremiumListDao.save(name, currency, inputDataPreProcessed);
|
||||
String message =
|
||||
String.format("Saved premium list %s with %d entries", name, inputDataPreProcessed.size());
|
||||
logger.atInfo().log(message);
|
||||
|
||||
@@ -28,6 +28,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Dagger module for the tools package.
|
||||
@@ -66,6 +67,12 @@ public class ToolsServerModule {
|
||||
return extractRequiredParameter(req, CreatePremiumListAction.NAME_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("currency")
|
||||
static CurrencyUnit provideCurrency(HttpServletRequest req) {
|
||||
return CurrencyUnit.of(extractRequiredParameter(req, CreatePremiumListAction.CURRENCY));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("override")
|
||||
static boolean provideOverride(HttpServletRequest req) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.schema.tld.PremiumListDao;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -46,8 +47,9 @@ public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
@Override
|
||||
protected void save() {
|
||||
Optional<PremiumList> existingList = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
PremiumListDao.getLatestRevision(name).isPresent(),
|
||||
existingList.isPresent(),
|
||||
"Could not update premium list %s because it doesn't exist.",
|
||||
name);
|
||||
|
||||
@@ -55,7 +57,8 @@ public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
logInputData();
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumList newPremiumList = PremiumListDao.save(name, inputDataPreProcessed);
|
||||
PremiumList newPremiumList =
|
||||
PremiumListDao.save(name, existingList.get().getCurrency(), inputDataPreProcessed);
|
||||
|
||||
String message =
|
||||
String.format(
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
<class>google.registry.model.registrar.Registrar</class>
|
||||
<class>google.registry.model.registrar.RegistrarContact</class>
|
||||
<class>google.registry.model.registry.label.PremiumList</class>
|
||||
<class>google.registry.model.registry.label.PremiumList$PremiumEntry</class>
|
||||
<class>google.registry.model.registry.label.ReservedList</class>
|
||||
<class>google.registry.model.registry.label.ReservedList$ReservedListEntry</class>
|
||||
<class>google.registry.model.registry.Registry</class>
|
||||
@@ -74,7 +75,6 @@
|
||||
<class>google.registry.persistence.transaction.TransactionEntity</class>
|
||||
<class>google.registry.schema.domain.RegistryLock</class>
|
||||
<class>google.registry.schema.replay.SqlReplayCheckpoint</class>
|
||||
<class>google.registry.schema.tld.PremiumEntry</class>
|
||||
|
||||
<!-- Customized type converters -->
|
||||
<class>google.registry.persistence.converter.AllocationTokenStatusTransitionConverter</class>
|
||||
@@ -90,7 +90,6 @@
|
||||
<class>google.registry.persistence.converter.InetAddressSetConverter</class>
|
||||
<class>google.registry.persistence.converter.LocalDateConverter</class>
|
||||
<class>google.registry.persistence.converter.PostalInfoChoiceListConverter</class>
|
||||
<class>google.registry.persistence.converter.PremiumListKeyConverter</class>
|
||||
<class>google.registry.persistence.converter.RegistrarPocSetConverter</class>
|
||||
<class>google.registry.persistence.converter.ReservedListKeySetConverter</class>
|
||||
<class>google.registry.persistence.converter.Spec11ThreatMatchThreatTypeSetConverter</class>
|
||||
|
||||
Reference in New Issue
Block a user