diff --git a/docs/architecture.md b/docs/architecture.md index 3d40707ce..c55587225 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -377,11 +377,6 @@ registry codebase: * `Registrar` -- These hold information about client registrars. * `RegistrarContact` -- Registrars have contacts just as domains do. These are stored in a special RegistrarContact entity. -* `RegistrarCredit` and `RegistrarCreditBalance` -- These entities implement - the concept of registrar "credits" which are pools of credit that would - apply to charges owed by the registrar. Note, however, that Nomulus - currently has no support for actually incorporating credits into billing - output, and these entities may be removed at any time. * `Registry` -- These hold information about the TLDs supported by the Registry system. * `RegistryCursor` -- These entities are the predecessor to the Cursor diff --git a/java/google/registry/model/EntityClasses.java b/java/google/registry/model/EntityClasses.java index c5a08fbfb..e599157ca 100644 --- a/java/google/registry/model/EntityClasses.java +++ b/java/google/registry/model/EntityClasses.java @@ -16,9 +16,6 @@ package google.registry.model; import com.google.common.collect.ImmutableSet; import google.registry.model.billing.BillingEvent; -import google.registry.model.billing.RegistrarBillingEntry; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCreditBalance; import google.registry.model.common.Cursor; import google.registry.model.common.EntityGroupRoot; import google.registry.model.common.GaeUserIdConverter; @@ -103,10 +100,7 @@ public final class EntityClasses { PremiumList.PremiumListRevision.class, RdeRevision.class, Registrar.class, - RegistrarBillingEntry.class, RegistrarContact.class, - RegistrarCredit.class, - RegistrarCreditBalance.class, Registry.class, ReservedList.class, ServerSecret.class, diff --git a/java/google/registry/model/billing/RegistrarBillingEntry.java b/java/google/registry/model/billing/RegistrarBillingEntry.java deleted file mode 100644 index e1baf34a1..000000000 --- a/java/google/registry/model/billing/RegistrarBillingEntry.java +++ /dev/null @@ -1,206 +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.billing; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Strings.emptyToNull; -import static com.google.common.base.Verify.verifyNotNull; - -import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Entity; -import com.googlecode.objectify.annotation.Id; -import com.googlecode.objectify.annotation.Index; -import com.googlecode.objectify.annotation.Parent; -import google.registry.model.Buildable; -import google.registry.model.ImmutableObject; -import google.registry.model.JsonMapBuilder; -import google.registry.model.Jsonifiable; -import google.registry.model.registrar.Registrar; -import java.util.Map; -import javax.annotation.Nullable; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** - * Log of monthly invoices and payments for a Registrar customer. - * - *

This is a one-off single-entry bookkeeping system. There is a separate account for each - * (registrar, currency) pair. - * - *

You should never update these entities once they've been inserted into Datastore. If you need - * to change something, add a correction entry. - */ -@Entity -public class RegistrarBillingEntry extends ImmutableObject implements Jsonifiable { - - @Parent - Key parent; - - /** Arbitrary unique identifier. */ - @Id - long id; - - /** - * External transaction identifier or {@code null} if this is an invoice entry. - * - *

This is the ID or token that the payment gateway gives us, which represents the transaction - * in their database. - */ - @Nullable - String transactionId; - - /** - * Time at which this entry was created. - * - *

This value is unique and monotonic for a given ({@link #parent}, {@link #currency}) pair. - */ - @Index - DateTime created; - - /** Completely arbitrary description of payment. */ - String description; - - /** - * Currency of transaction. - * - *

This field is identical to {@code amount.getCurrencyUnit()} and is only here so it can be - * indexed in Datastore. - */ - @Index - CurrencyUnit currency; - - /** - * Amount and currency of invoice or payment. - * - *

This field is positive for debits (e.g. monthly invoice entries) and negative for credits - * (e.g. credit card payment transaction entries.) - */ - Money amount; - - /** - * Balance of account for this currency. - * - *

This is {@code amount + previous.balance}. - */ - Money balance; - - public Key getParent() { - return parent; - } - - public long getId() { - return id; - } - - @Nullable - public String getTransactionId() { - return transactionId; - } - - public DateTime getCreated() { - return verifyNotNull(created, "created missing: %s", this); - } - - public String getDescription() { - return verifyNotNull(description, "description missing: %s", this); - } - - public CurrencyUnit getCurrency() { - return verifyNotNull(currency, "currency missing: %s", this); - } - - public Money getAmount() { - return verifyNotNull(amount, "amount missing: %s", this); - } - - public Money getBalance() { - return verifyNotNull(balance, "balance missing: %s", this); - } - - @Override - public Map toJsonMap() { - return new JsonMapBuilder() - .put("id", id) - .put("transactionId", getTransactionId()) - .putString("created", getCreated()) - .put("description", getDescription()) - .putString("currency", getCurrency()) - .putString("amount", getAmount().getAmount()) - .putString("balance", getBalance().getAmount()) - .build(); - } - - /** A builder for constructing a {@link RegistrarBillingEntry}, since it's immutable. */ - public static class Builder extends Buildable.Builder { - - @Nullable - private RegistrarBillingEntry previous; - - public Builder setParent(Registrar parent) { - getInstance().parent = Key.create(parent); - return this; - } - - public Builder setCreated(DateTime created) { - getInstance().created = created; - return this; - } - - public Builder setPrevious(@Nullable RegistrarBillingEntry previous) { - this.previous = previous; - return this; - } - - public Builder setTransactionId(@Nullable String transactionId) { - getInstance().transactionId = transactionId; - return this; - } - - public Builder setDescription(String description) { - getInstance().description = checkNotNull(emptyToNull(description)); - return this; - } - - public Builder setAmount(Money amount) { - checkArgument(!amount.isZero(), "Amount can't be zero"); - getInstance().amount = amount; - getInstance().currency = amount.getCurrencyUnit(); - return this; - } - - @Override - public RegistrarBillingEntry build() { - checkNotNull(getInstance().parent, "parent"); - checkNotNull(getInstance().created, "created"); - checkNotNull(getInstance().description, "description"); - checkNotNull(getInstance().amount, "amount"); - if (previous == null) { - getInstance().balance = getInstance().amount; - } else { - getInstance().balance = previous.balance.plus(getInstance().amount); - checkState(getInstance().parent.equals(previous.parent), - "Parent not same as previous:\nNew: %s\nPrevious: %s", - getInstance(), previous); - checkState(getInstance().created.isAfter(previous.created), - "Created timestamp not after previous:\nNew: %s\nPrevious: %s", - getInstance(), previous); - } - return cloneEmptyToNull(super.build()); - } - } -} diff --git a/java/google/registry/model/billing/RegistrarBillingUtils.java b/java/google/registry/model/billing/RegistrarBillingUtils.java deleted file mode 100644 index 0e8d22400..000000000 --- a/java/google/registry/model/billing/RegistrarBillingUtils.java +++ /dev/null @@ -1,84 +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.billing; - -import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; -import static google.registry.model.ofy.ObjectifyService.ofy; - -import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; -import com.googlecode.objectify.cmd.Query; -import google.registry.model.CacheUtils; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registries; -import google.registry.model.registry.Registry; -import java.util.Map; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; - -/** Utilities for managing the billing of {@link Registrar} customers. */ -public final class RegistrarBillingUtils { - - private static final Supplier> CURRENCIES_CACHE = - CacheUtils.memoizeWithShortExpiration( - () -> - Registries.getTlds() - .stream() - .map((String tld) -> Registry.get(tld).getCurrency()) - .collect(toImmutableSortedSet(Ordering.natural()))); - - /** - * Returns set of currencies in which registrars may be billed. - * - *

Each TLD has a currency associated with it. We don't do conversions. The registrar customer - * gets a separate bill for each currency. - */ - public static ImmutableSortedSet getCurrencies() { - return CURRENCIES_CACHE.get(); - } - - /** - * Returns query of {@link RegistrarBillingEntry} for each currency, most recent first. - * - *

Note: Currency map keys are returned in sorted order, from {@link #getCurrencies()}. - */ - public static ImmutableMap> getBillingEntryQueries( - final Registrar registrar) { - return Maps.toMap( - getCurrencies(), - (CurrencyUnit currency) -> - ofy() - .load() - .type(RegistrarBillingEntry.class) - .ancestor(registrar) - .filter("currency", currency) - .order("-created")); - } - - /** Returns amount of money registrar currently owes registry in each currency. */ - public static Map loadBalance(Registrar registrar) { - return Maps.transformEntries( - getBillingEntryQueries(registrar), - (CurrencyUnit currency, Query query) -> { - RegistrarBillingEntry entry = query.first().now(); - return entry != null ? entry.getBalance() : Money.zero(currency); - }); - } - - private RegistrarBillingUtils() {} -} diff --git a/java/google/registry/model/billing/RegistrarCredit.java b/java/google/registry/model/billing/RegistrarCredit.java deleted file mode 100644 index 0f8d8fc4a..000000000 --- a/java/google/registry/model/billing/RegistrarCredit.java +++ /dev/null @@ -1,224 +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.billing; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.model.registry.Registries.assertTldExists; - -import com.google.common.base.Joiner; -import com.google.common.collect.ComparisonChain; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Ordering; -import com.google.common.collect.Streams; -import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Entity; -import com.googlecode.objectify.annotation.Id; -import com.googlecode.objectify.annotation.Parent; -import google.registry.model.Buildable; -import google.registry.model.ImmutableObject; -import google.registry.model.annotations.ReportedOn; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import java.util.Optional; -import org.joda.money.CurrencyUnit; -import org.joda.time.DateTime; - -/** - * A per-registrar billing credit, applied toward future charges for registrar activity. - * - *

NOTE: While credits are tracked within the model, there is no built-in support for actually - * incorporating these credits into the generated billing data, and the model support for credits - * should be considered quasi-deprecated (it may be removed without notice). - */ -@ReportedOn -@Entity -public final class RegistrarCredit extends ImmutableObject implements Buildable { - - /** - * The type of credit represented. The ordering below determines the order in which credits of - * of different types will be applied to an invoice charge. - */ - // Note: Right now the ordering is actually maintained manually via a hard-coded table in the - // relevant billing query, so if adding a credit type here, add it there as well. - // TODO(b/19031546): make the query automatically reflect the order in this enum. - public enum CreditType { - /** Credit awarded as an incentive to participate in sunrise/landrush auctions. */ - AUCTION("Auction Credit"), - - /** Credit awarded as part of a promotional deal. */ - PROMOTION("Promotional Credit"); - - /** A descriptive name for a credit of this type. */ - private final String descriptiveName; - - CreditType(String descriptiveName) { - this.descriptiveName = descriptiveName; - } - - public String getDescriptiveName() { - return descriptiveName; - } - } - - @Id - long id; - - /** The registrar to whom this credit belongs. */ - @Parent - Key parent; - - /** The type of credit. */ - CreditType type; - - /** - * The time that this credit was created. If a registrar has multiple credits of a given type, - * the older credits will be applied first. - */ - DateTime creationTime; - - /** The currency in which the balance for this credit is stored. */ - CurrencyUnit currency; - - /** The line item description to use when displaying this credit on an invoice. */ - String description; - - /** - * The TLD in which this credit applies. - * - *

For auction credits, this is also the TLD for which the relevant auctions occurred. - */ - String tld; - - public Key getParent() { - return parent; - } - - public CreditType getType() { - return type; - } - - public DateTime getCreationTime() { - return creationTime; - } - - public CurrencyUnit getCurrency() { - return currency; - } - - public String getDescription() { - return description; - } - - public String getTld() { - return tld; - } - - /** Returns a string representation of this credit. */ - public String getSummary() { - String fields = Joiner.on(' ').join(type, creationTime, tld); - return String.format("%s (%s/%d) - %s", description, parent.getName(), id, fields); - } - - /** Returns the default description for this {@link RegistrarCredit} instance. */ - private String getDefaultDescription() { - return type.getDescriptiveName() + " for ." + tld; - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - /** A Builder for {@link RegistrarCredit}. */ - public static class Builder extends Buildable.Builder { - public Builder() {} - - public Builder(RegistrarCredit instance) { - super(instance); - } - - public Builder setParent(Registrar parent) { - getInstance().parent = Key.create(parent); - return this; - } - - public Builder setType(CreditType type) { - getInstance().type = type; - return this; - } - - public Builder setCreationTime(DateTime creationTime) { - getInstance().creationTime = creationTime; - return this; - } - - public Builder setCurrency(CurrencyUnit currency) { - getInstance().currency = currency; - return this; - } - - public Builder setDescription(String description) { - getInstance().description = description; - return this; - } - - public Builder setTld(String tld) { - getInstance().tld = tld; - return this; - } - - @Override - public RegistrarCredit build() { - RegistrarCredit instance = getInstance(); - checkNotNull(instance.parent, "parent credit"); - checkNotNull(instance.type, "type"); - checkNotNull(instance.creationTime, "creationTime"); - checkNotNull(instance.currency, "currency"); - assertTldExists(checkNotNull(instance.tld, "tld")); - checkArgument( - Registry.get(instance.tld).getCurrency().equals(instance.currency), - "Credits must be in the currency of the assigned TLD"); - instance.description = - Optional.ofNullable(instance.description).orElse(instance.getDefaultDescription()); - return super.build(); - } - } - - /** Ordering that sorts credits first by type and then by creation time. */ - private static final Ordering CREDIT_PRIORITY_ORDERING = - new Ordering() { - @Override - public int compare(RegistrarCredit left, RegistrarCredit right) { - return ComparisonChain.start() - .compare(left.type, right.type) - .compare(left.creationTime, right.creationTime) - .result(); - } - }; - - /** - * Loads all RegistrarCredit entities for the given Registrar. - * - *

The resulting list sorts the credits first by type and then by creation time. - */ - public static ImmutableList loadAllForRegistrar(Registrar registrar) { - return Streams.stream(ofy().load().type(RegistrarCredit.class).ancestor(registrar)) - .sorted(CREDIT_PRIORITY_ORDERING) - .collect(toImmutableList()); - } -} diff --git a/java/google/registry/model/billing/RegistrarCreditBalance.java b/java/google/registry/model/billing/RegistrarCreditBalance.java deleted file mode 100644 index 9090eb395..000000000 --- a/java/google/registry/model/billing/RegistrarCreditBalance.java +++ /dev/null @@ -1,251 +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.billing; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static google.registry.model.ofy.ObjectifyService.ofy; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ForwardingNavigableMap; -import com.google.common.collect.ImmutableSortedMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; -import com.googlecode.objectify.Key; -import com.googlecode.objectify.annotation.Entity; -import com.googlecode.objectify.annotation.Id; -import com.googlecode.objectify.annotation.Parent; -import google.registry.model.Buildable; -import google.registry.model.ImmutableObject; -import google.registry.model.annotations.ReportedOn; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** - * The balance of a {@link RegistrarCredit} at a given point in time. - * - *

A credit balance has two related times in addition to the monetary amount: the effective time, - * which represents the time at which the amount becomes the actual credit balance; and the - * written time, which represents the time at which this balance object was saved. - * - *

The active balance of a credit object before (at) any given point in time T can be found by - * taking the balance object with the latest effective time that is before (before or at) T, and - * breaking any ties by choosing the mostly recently written among those balances. - * - *

NOTE: While credits are tracked within the model, there is no built-in support for actually - * incorporating these credits into the generated billing data, and the model support for credits - * should be considered quasi-deprecated (it may be removed without notice). - */ -@ReportedOn -@Entity -public final class RegistrarCreditBalance extends ImmutableObject implements Buildable { - - @Id - long id; - - /** The registrar credit object for which this represents a balance. */ - @Parent - Key parent; - - /** The time at which this balance amount should become effective. */ - DateTime effectiveTime; - - /** - * The time at which this balance update was written. - * - *

Used to break ties in cases where there are multiple balances with the same effective time, - * as the last written balance will take priority. - */ - DateTime writtenTime; - - /** The monetary amount of credit balance remaining as of the effective time. */ - Money amount; - - public Key getParent() { - return parent; - } - - public DateTime getEffectiveTime() { - return effectiveTime; - } - - public DateTime getWrittenTime() { - return writtenTime; - } - - public Money getAmount() { - return amount; - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - /** A Builder for an {@link RegistrarCreditBalance}. */ - public static class Builder extends Buildable.Builder { - - private CurrencyUnit currency; - - public Builder() {} - - public Builder(RegistrarCreditBalance instance) { - super(instance); - } - - public RegistrarCreditBalance.Builder setParent(RegistrarCredit parent) { - this.currency = parent.getCurrency(); - getInstance().parent = Key.create(parent); - return this; - } - - public RegistrarCreditBalance.Builder setEffectiveTime(DateTime effectiveTime) { - getInstance().effectiveTime = effectiveTime; - return this; - } - - public RegistrarCreditBalance.Builder setWrittenTime(DateTime writtenTime) { - getInstance().writtenTime = writtenTime; - return this; - } - - public RegistrarCreditBalance.Builder setAmount(Money amount) { - checkArgument(amount.isPositiveOrZero(), "Credit balance amount cannot be negative"); - getInstance().amount = amount; - return this; - } - - @Override - public RegistrarCreditBalance build() { - RegistrarCreditBalance instance = getInstance(); - checkNotNull(instance.parent); - checkNotNull(instance.effectiveTime); - checkNotNull(instance.writtenTime); - checkNotNull(instance.amount); - checkState( - instance.amount.getCurrencyUnit().equals(currency), - "Currency of balance amount differs from credit currency (%s vs %s)", - instance.amount.getCurrencyUnit(), - currency); - return super.build(); - } - } - - /** - * A map of maps representing the historical credit balance information for a given credit. - * - *

Specifically, this class provides a high-level view of the balances for a given credit - * by in essence grouping them first by effective time and then by written time. This facilitates - * the printing of a readable representation of a credit's balance history, and the retrieval of - * the active balance at a given time (as described above on RegistrarCreditBalance). - */ - public static class BalanceMap - extends ForwardingNavigableMap> { - - /** - * Constructs a BalanceMap for the given registrar credit by loading all RegistrarCreditBalance - * entities for the credit and then inserting them into a map of maps keyed first by effective - * time and then by written time with the balance amount as the value. - */ - public static BalanceMap createForCredit(RegistrarCredit registrarCredit) { - // Build up the data in a mutable map of maps. - Map> map = new HashMap<>(); - for (RegistrarCreditBalance balance : - ofy().load().type(RegistrarCreditBalance.class).ancestor(registrarCredit)) { - // Create the submap at this key if it doesn't exist already. - Map submap = - Optional.ofNullable(map.get(balance.effectiveTime)).orElse(new HashMap<>()); - submap.put(balance.writtenTime, balance.amount); - map.put(balance.effectiveTime, submap); - } - // Wrap the mutable map of maps in an immutable BalanceMap. - return new BalanceMap(map); - } - - /** The immutable map of maps used as the backing map. */ - private final ImmutableSortedMap> delegate; - - /** - * Constructs an immutable BalanceMap from balance data provided as a map of maps. - * - *

The constructed BalanceMap delegates to an immutable copy of the provided map of maps. - * This copy is created by first making a view of the map in which each submap is replaced by - * an immutable copy, and then making an immutable copy of that view. - */ - @VisibleForTesting - BalanceMap(Map> data) { - delegate = - ImmutableSortedMap.copyOf( - Maps.transformValues( - data, - (Map map) -> ImmutableSortedMap.copyOf(map, Ordering.natural())), - Ordering.natural()); - } - - @Override - protected ImmutableSortedMap> delegate() { - return delegate; - } - - /** - * Returns the most recently written balance for the effective time corresponding to this entry, - * or {@link Optional#absent()} if this entry is null. - */ - private Optional getMostRecentlyWrittenBalance( - Map.Entry> balancesAtEffectiveTime) { - return balancesAtEffectiveTime == null - ? Optional.empty() - // Don't use Optional.ofNullable() here since it's an error if there's a empty submap. - : Optional.of(balancesAtEffectiveTime.getValue().lastEntry().getValue()); - } - - /** - * Returns the active balance at a given time as described above on RegistrarCreditBalance, or - * {@link Optional#absent()} if no balance was active at that time (i.e. the time provided is - * before the first effectiveTime of any balance for the credit this BalanceMap represents). - */ - public Optional getActiveBalanceAtTime(DateTime time) { - return getMostRecentlyWrittenBalance(delegate.floorEntry(time)); - } - - /** - * Returns the active balance before a given time as described above on RegistrarCreditBalance, - * or {@link Optional#absent()} if no balance was active before that time (i.e. the time - * provided is before or at the first effectiveTime of any balance for the credit). - */ - public Optional getActiveBalanceBeforeTime(DateTime time) { - return getMostRecentlyWrittenBalance(delegate.lowerEntry(time)); - } - - /** Returns a string representation of this BalanceMap's data. */ - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - for (Map.Entry> entry : delegate.entrySet()) { - builder.append(String.format(" - %s\n", entry.getKey())); - for (Map.Entry subEntry : entry.getValue().entrySet()) { - builder.append( - String.format(" - %s - %s\n", subEntry.getKey(), subEntry.getValue())); - } - } - return builder.toString(); - } - } -} diff --git a/java/google/registry/tools/CreateAuctionCreditsCommand.java b/java/google/registry/tools/CreateAuctionCreditsCommand.java deleted file mode 100644 index 39308e28f..000000000 --- a/java/google/registry/tools/CreateAuctionCreditsCommand.java +++ /dev/null @@ -1,212 +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.tools; - -import static com.google.common.base.CaseFormat.UPPER_CAMEL; -import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static google.registry.model.registry.Registries.assertTldExists; -import static google.registry.util.PreconditionsUtils.checkArgumentPresent; -import static org.joda.time.DateTimeZone.UTC; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Streams; -import com.google.re2j.Matcher; -import com.google.re2j.Pattern; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.function.Function; -import java.util.stream.Stream; -import org.joda.money.BigMoney; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** - * Command for creating new auction credits based on a CSV file from Pool. - * - *

The CSV file from the auction provider uses double-quotes around every field, so in order to - * extract the raw field value we strip off the quotes after splitting each line by commas. We are - * using a simple parsing strategy that does not support embedded quotation marks, commas, or - * newlines. - * - *

Example file format: - * - *

- * "Affiliate","DomainName","Email","BidderId","BidderStatus","UpdatedAt",
- *      "SalePrice","Commissions","CurrencyCode"
- * "reg1","foo.xn--q9jyb4c","email1@example.com","???_300","INACTIVE","4/3/2014 7:13:09 PM",
- *      "1000.0000","0.0000","JPY"
- * "reg2","foo.xn--q9jyb4c","email2@example.net","???_64","WIN","4/3/2014 7:13:09 PM",
- *      "1000.0000","40.0000","JPY"
- * 
- * - *

We only care about three fields: 1) the "Affiliate" field which corresponds to the registrar - * clientId stored in Datastore, and which we use to determine which registrar gets the credit, 2) - * the "Commissions" field which contains the amount of the auction credit (as determined by logic - * on the auction provider's side, see the Finance Requirements Doc for more information), and 3) - * the "CurrencyCode" field, which we validate matches the TLD-wide currency for this TLD. - */ -// TODO(b/16009815): Switch this file to using a real CSV parser. -@Parameters(separators = " =", commandDescription = "Create new auction credits based on CSV") -final class CreateAuctionCreditsCommand extends MutatingCommand { - - @Parameter( - names = "--input_file", - description = "CSV file for the Pool.com commissions report", - required = true) - private Path inputFile; - - @Parameter( - names = {"-t", "--tld"}, - description = "The TLD corresponding to this commissions report", - required = true) - private String tld; - - @Parameter( - names = "--effective_time", - description = "The time at which these auction credits should become effective", - required = true) - private DateTime effectiveTime; - - /** Enum containing the headers we expect in the Pool.com CSV file, in order. */ - private enum CsvHeader { - AFFILIATE, - DOMAIN_NAME, - EMAIL, - BIDDER_ID, - BIDDER_STATUS, - UPDATED_AT, - SALE_PRICE, - COMMISSIONS, - CURRENCY_CODE; - - public static List getHeaders() { - return Stream.of(values()) - .map(header -> UPPER_UNDERSCORE.to(UPPER_CAMEL, header.name())) - .collect(toImmutableList()); - } - } - - private static final Pattern QUOTED_STRING = Pattern.compile("\"(.*)\""); - - /** Helper function to unwrap a quoted string, failing if the string is not quoted. */ - private static final Function UNQUOTER = - input -> { - Matcher matcher = QUOTED_STRING.matcher(input); - checkArgument(matcher.matches(), "Input not quoted"); - return matcher.group(1); - }; - - /** Returns the input string of quoted CSV values split into the list of unquoted values. */ - private static List splitCsvLine(String line) { - return Streams.stream(Splitter.on(',').split(line)).map(UNQUOTER).collect(toImmutableList()); - } - - @Override - protected void init() throws Exception { - assertTldExists(tld); - ImmutableMultimap creditMap = parseCreditsFromCsv(inputFile, tld); - stageCreditCreations(creditMap); - } - - /** - * Parses the provided CSV file of data from the auction provider and returns a multimap mapping - * each registrar to the collection of auction credit amounts from this TLD's auctions that should - * be awarded to this registrar, and validating that every credit amount's currency is in the - * specified TLD-wide currency. - */ - private static ImmutableMultimap parseCreditsFromCsv( - Path csvFile, String tld) throws IOException { - List lines = Files.readAllLines(csvFile, StandardCharsets.UTF_8); - checkArgument(CsvHeader.getHeaders().equals(splitCsvLine(lines.get(0))), - "Expected CSV header line not present"); - ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder<>(); - for (String line : Iterables.skip(lines, 1)) { - List fields = splitCsvLine(line); - checkArgument(CsvHeader.getHeaders().size() == fields.size(), "Wrong number of fields"); - try { - String clientId = fields.get(CsvHeader.AFFILIATE.ordinal()); - Registrar registrar = - checkArgumentPresent( - Registrar.loadByClientId(clientId), "Registrar %s not found", clientId); - CurrencyUnit tldCurrency = Registry.get(tld).getCurrency(); - CurrencyUnit currency = CurrencyUnit.of((fields.get(CsvHeader.CURRENCY_CODE.ordinal()))); - checkArgument( - tldCurrency.equals(currency), - "Credit in wrong currency (%s should be %s)", - currency, - tldCurrency); - // We use BigDecimal and BigMoney to preserve fractional currency units when computing the - // total amount of each credit (since auction credits are percentages of winning bids). - BigDecimal creditAmount = new BigDecimal(fields.get(CsvHeader.COMMISSIONS.ordinal())); - BigMoney credit = BigMoney.of(currency, creditAmount); - builder.put(registrar, credit); - } catch (IllegalArgumentException | IndexOutOfBoundsException e) { - throw new IllegalArgumentException("Error in line: " + line, e); - } - } - return builder.build(); - } - - /** - * Stages the creation of RegistrarCredit and RegistrarCreditBalance instances for each - * registrar in the provided multimap of credit amounts by registrar. The balance instance - * created is the total of all the credit amounts for a given registrar. - */ - private void stageCreditCreations(ImmutableMultimap creditMap) { - DateTime now = DateTime.now(UTC); - CurrencyUnit currency = Registry.get(tld).getCurrency(); - for (Registrar registrar : creditMap.keySet()) { - // Use RoundingMode.UP to be nice and give registrars the extra fractional units. - Money totalAmount = - BigMoney.total(currency, creditMap.get(registrar)).toMoney(RoundingMode.UP); - System.out.printf("Total auction credit balance for %s: %s\n", - registrar.getClientId(), totalAmount); - - // Create the actual credit and initial credit balance. - RegistrarCredit credit = new RegistrarCredit.Builder() - .setParent(registrar) - .setType(CreditType.AUCTION) - .setCreationTime(now) - .setCurrency(currency) - .setTld(tld) - .build(); - RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder() - .setParent(credit) - .setEffectiveTime(effectiveTime) - .setWrittenTime(now) - .setAmount(totalAmount) - .build(); - stageEntityChange(null, credit); - stageEntityChange(null, creditBalance); - } - } -} diff --git a/java/google/registry/tools/CreateCreditBalanceCommand.java b/java/google/registry/tools/CreateCreditBalanceCommand.java deleted file mode 100644 index 1f426c2fd..000000000 --- a/java/google/registry/tools/CreateCreditBalanceCommand.java +++ /dev/null @@ -1,77 +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.tools; - -import static com.google.common.base.Preconditions.checkNotNull; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.util.PreconditionsUtils.checkArgumentPresent; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import google.registry.util.SystemClock; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** Command for creating a new balance for a registrar credit. */ -@Parameters(separators = " =", commandDescription = "Create a new registrar credit balance") -final class CreateCreditBalanceCommand extends MutatingCommand { - - @Parameter( - names = "--registrar", - description = "Client ID of the registrar owning the credit to create a new balance for", - required = true) - private String clientId; - - @Parameter( - names = "--credit_id", - description = "ID of credit to create a new balance for", - required = true) - private long creditId; - - @Parameter( - names = "--balance", - description = "The new balance amount", - required = true) - private Money balance; - - @Parameter( - names = "--effective_time", - description = "Point in time at which the new balance amount becomes effective", - required = true) - private DateTime effectiveTime; - - @Override - public void init() { - Registrar registrar = - checkArgumentPresent( - Registrar.loadByClientId(clientId), "Registrar %s not found", clientId); - RegistrarCredit credit = ofy().load() - .type(RegistrarCredit.class) - .parent(registrar) - .id(creditId) - .now(); - checkNotNull(credit, "Registrar credit for %s with ID %s not found", clientId, creditId); - RegistrarCreditBalance newBalance = new RegistrarCreditBalance.Builder() - .setParent(credit) - .setEffectiveTime(effectiveTime) - .setWrittenTime(new SystemClock().nowUtc()) - .setAmount(balance) - .build(); - stageEntityChange(null, newBalance); - } -} diff --git a/java/google/registry/tools/CreateCreditCommand.java b/java/google/registry/tools/CreateCreditCommand.java deleted file mode 100644 index 58f6c2452..000000000 --- a/java/google/registry/tools/CreateCreditCommand.java +++ /dev/null @@ -1,93 +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.tools; - -import static google.registry.util.PreconditionsUtils.checkArgumentPresent; -import static org.joda.time.DateTimeZone.UTC; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import javax.annotation.Nullable; -import org.joda.money.Money; -import org.joda.time.DateTime; - -/** Command for creating a registrar credit object with an initial balance. */ -@Parameters(separators = " =", commandDescription = "Create a new registrar credit") -final class CreateCreditCommand extends MutatingCommand { - - @Parameter( - names = "--registrar", - description = "Client ID of the registrar who will be awarded this credit", - required = true) - private String clientId; - - @Parameter( - names = "--type", - description = "Type of credit (AUCTION or PROMOTION)", - required = true) - private CreditType type; - - @Nullable - @Parameter( - names = "--description", - description = "Custom description that will appear on invoice line for this credit") - private String description; - - @Parameter( - names = "--tld", - description = "TLD for which this credit applies", - required = true) - private String tld; - - @Parameter( - names = "--balance", - description = "Initial balance of this credit", - required = true) - private Money balance; - - @Parameter( - names = "--effective_time", - description = "Point in time at which the initial balance becomes effective", - required = true) - private DateTime effectiveTime; - - @Override - protected void init() { - DateTime now = DateTime.now(UTC); - Registrar registrar = - checkArgumentPresent( - Registrar.loadByClientId(clientId), "Registrar %s not found", clientId); - RegistrarCredit credit = new RegistrarCredit.Builder() - .setParent(registrar) - .setType(type) - .setCreationTime(now) - .setCurrency(balance.getCurrencyUnit()) - .setDescription(description) - .setTld(tld) - .build(); - RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder() - .setParent(credit) - .setEffectiveTime(effectiveTime) - .setWrittenTime(now) - .setAmount(balance) - .build(); - stageEntityChange(null, credit); - stageEntityChange(null, creditBalance); - } -} diff --git a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java index e1f126565..e1b88cab1 100644 --- a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java +++ b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java @@ -15,7 +15,6 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.isNull; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.ImmutableSet.toImmutableSet; @@ -29,7 +28,6 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import google.registry.model.billing.RegistrarBillingUtils; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar.BillingMethod; import google.registry.model.registrar.RegistrarAddress; @@ -51,7 +49,6 @@ import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; import org.joda.money.CurrencyUnit; -import org.joda.money.Money; import org.joda.time.DateTime; /** Shared base class for commands to create or update a {@link Registrar}. */ @@ -370,18 +367,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { newBillingAccountMap.putAll(billingAccountMap); builder.setBillingAccountMap(newBillingAccountMap); } - if (billingMethod != null) { - if (oldRegistrar != null && !billingMethod.equals(oldRegistrar.getBillingMethod())) { - Map balances = RegistrarBillingUtils.loadBalance(oldRegistrar); - for (Money balance : balances.values()) { - checkState(balance.isZero(), - "Refusing to change billing method on Registrar '%s' from %s to %s" - + " because current balance is non-zero: %s", - clientId, oldRegistrar.getBillingMethod(), billingMethod, balances); - } - } - builder.setBillingMethod(billingMethod); - } + Optional.ofNullable(billingMethod).ifPresent(builder::setBillingMethod); List streetAddressFields = Arrays.asList(street, city, state, zip, countryCode); checkArgument( streetAddressFields.stream().anyMatch(isNull()) diff --git a/java/google/registry/tools/DeleteCreditCommand.java b/java/google/registry/tools/DeleteCreditCommand.java deleted file mode 100644 index 4a3849d3f..000000000 --- a/java/google/registry/tools/DeleteCreditCommand.java +++ /dev/null @@ -1,61 +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.tools; - -import static com.google.common.base.Preconditions.checkNotNull; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.util.PreconditionsUtils.checkArgumentPresent; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; - -/** Command for deleting a registrar credit object and all of its child balances. */ -@Parameters(separators = " =", commandDescription = "Delete a registrar credit") -final class DeleteCreditCommand extends MutatingCommand { - - @Parameter( - names = "--registrar", - description = "Client ID of the registrar owning the credit to delete", - required = true) - private String clientId; - - @Parameter( - names = "--credit_id", - description = "ID of credit to delete", - required = true) - private long creditId; - - @Override - protected void init() { - Registrar registrar = - checkArgumentPresent( - Registrar.loadByClientId(clientId), "Registrar %s not found", clientId); - RegistrarCredit credit = ofy().load() - .type(RegistrarCredit.class) - .parent(registrar) - .id(creditId) - .now(); - checkNotNull(credit, "Registrar credit for %s with ID %s not found", clientId, creditId); - stageEntityChange(credit, null); - - for (RegistrarCreditBalance balance : - ofy().load().type(RegistrarCreditBalance.class).ancestor(credit)) { - stageEntityChange(balance, null); - } - } -} diff --git a/java/google/registry/tools/ListCreditsCommand.java b/java/google/registry/tools/ListCreditsCommand.java deleted file mode 100644 index dc820393c..000000000 --- a/java/google/registry/tools/ListCreditsCommand.java +++ /dev/null @@ -1,69 +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.tools; - -import static google.registry.model.ofy.ObjectifyService.ofy; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCreditBalance.BalanceMap; -import google.registry.model.registrar.Registrar; -import google.registry.tools.Command.RemoteApiCommand; -import java.util.Optional; -import org.joda.money.Money; - -/** Command to list registrar credits and balances. */ -@Parameters(commandDescription = "List registrar credits and balances") -final class ListCreditsCommand implements RemoteApiCommand { - - @Parameter( - names = {"-a", "--all"}, - description = "Pass this flag to show all credits, even those with a zero active balance") - private boolean showAll; - - @Override - public void run() { - for (Registrar registrar : Registrar.loadAll()) { - ImmutableList creditStrings = createCreditStrings(registrar); - if (!creditStrings.isEmpty()) { - System.out.println(registrar.getClientId()); - System.out.print(Joiner.on("").join(creditStrings)); - System.out.println(); - } - } - } - - private ImmutableList createCreditStrings(final Registrar registrar) { - return ofy() - .transactNewReadOnly( - () -> { - ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (RegistrarCredit credit : RegistrarCredit.loadAllForRegistrar(registrar)) { - BalanceMap balanceMap = BalanceMap.createForCredit(credit); - Optional activeBalance = - balanceMap.getActiveBalanceAtTime(ofy().getTransactionTime()); - // Unless showAll is true, only show credits with a positive active balance (which - // excludes just zero-balance credits since credit balances cannot be negative). - if (showAll || (activeBalance.isPresent() && activeBalance.get().isPositive())) { - builder.add(credit.getSummary() + "\n" + balanceMap); - } - } - return builder.build(); - }); - } -} diff --git a/java/google/registry/tools/RegistryTool.java b/java/google/registry/tools/RegistryTool.java index 41dee0662..f0ba33c5a 100644 --- a/java/google/registry/tools/RegistryTool.java +++ b/java/google/registry/tools/RegistryTool.java @@ -37,11 +37,8 @@ public final class RegistryTool { .put("convert_idn", ConvertIdnCommand.class) .put("count_domains", CountDomainsCommand.class) .put("create_anchor_tenant", CreateAnchorTenantCommand.class) - .put("create_auction_credits", CreateAuctionCreditsCommand.class) .put("create_cdns_tld", CreateCdnsTld.class) .put("create_contact", CreateContactCommand.class) - .put("create_credit", CreateCreditCommand.class) - .put("create_credit_balance", CreateCreditBalanceCommand.class) .put("create_domain", CreateDomainCommand.class) .put("create_host", CreateHostCommand.class) .put("create_lrp_tokens", CreateLrpTokensCommand.class) @@ -50,7 +47,6 @@ public final class RegistryTool { .put("create_registrar_groups", CreateRegistrarGroupsCommand.class) .put("create_reserved_list", CreateReservedListCommand.class) .put("create_tld", CreateTldCommand.class) - .put("delete_credit", DeleteCreditCommand.class) .put("delete_domain", DeleteDomainCommand.class) .put("delete_entity", DeleteEntityCommand.class) .put("delete_host", DeleteHostCommand.class) @@ -86,7 +82,6 @@ public final class RegistryTool { .put("get_tld", GetTldCommand.class) .put("ghostryde", GhostrydeCommand.class) .put("hash_certificate", HashCertificateCommand.class) - .put("list_credits", ListCreditsCommand.class) .put("list_cursors", ListCursorsCommand.class) .put("list_domains", ListDomainsCommand.class) .put("list_hosts", ListHostsCommand.class) diff --git a/javatests/google/registry/export/backup_kinds.txt b/javatests/google/registry/export/backup_kinds.txt index dfb89696a..e799ae568 100644 --- a/javatests/google/registry/export/backup_kinds.txt +++ b/javatests/google/registry/export/backup_kinds.txt @@ -23,9 +23,6 @@ PremiumListRevision RdeRevision Recurring Registrar -RegistrarBillingEntry RegistrarContact -RegistrarCredit -RegistrarCreditBalance Registry ReservedList diff --git a/javatests/google/registry/export/reporting_kinds.txt b/javatests/google/registry/export/reporting_kinds.txt index 2d6a7260d..df5529678 100644 --- a/javatests/google/registry/export/reporting_kinds.txt +++ b/javatests/google/registry/export/reporting_kinds.txt @@ -20,6 +20,4 @@ PremiumListRevision Recurring Registrar RegistrarContact -RegistrarCredit -RegistrarCreditBalance Registry diff --git a/javatests/google/registry/model/billing/RegistrarBillingEntryTest.java b/javatests/google/registry/model/billing/RegistrarBillingEntryTest.java deleted file mode 100644 index c2ac7a03d..000000000 --- a/javatests/google/registry/model/billing/RegistrarBillingEntryTest.java +++ /dev/null @@ -1,197 +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.billing; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.JUnitBackports.assertThrows; -import static org.joda.money.CurrencyUnit.USD; - -import com.google.common.collect.ImmutableMap; -import com.googlecode.objectify.Key; -import google.registry.model.EntityTestCase; -import org.joda.money.CurrencyMismatchException; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link RegistrarBillingEntry}. */ -@RunWith(JUnit4.class) -public final class RegistrarBillingEntryTest extends EntityTestCase { - - @Test - public void testIndexing() throws Exception { - verifyIndexing( - persistResource( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setTransactionId("goblin-market") - .setDescription("USD Invoice for December 1984") - .setAmount(Money.parse("USD 10.00")) - .build()), - "currency", - "created"); - } - - @Test - public void testGetters() { - RegistrarBillingEntry entry = - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setTransactionId("goblin-market") - .setDescription("USD Invoice for December 1984") - .setAmount(Money.parse("USD 10.00")) - .build(); - assertThat(entry.getId()).isEqualTo(1L); - assertThat(entry.getParent()).isEqualTo(Key.create(loadRegistrar("NewRegistrar"))); - assertThat(entry.getCreated()).isEqualTo(DateTime.parse("1984-12-18TZ")); - assertThat(entry.getTransactionId()).isEqualTo("goblin-market"); - assertThat(entry.getDescription()).isEqualTo("USD Invoice for December 1984"); - assertThat(entry.getAmount()).isEqualTo(Money.parse("USD 10.00")); - assertThat(entry.getBalance()).isEqualTo(Money.parse("USD 10.00")); - } - - @Test - public void testToJsonMap() { - assertThat( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setTransactionId("goblin-market") - .setDescription("USD Invoice for December 1984") - .setAmount(Money.parse("USD 10.00")) - .build() - .toJsonMap()) - .isEqualTo( - new ImmutableMap.Builder() - .put("id", 1L) - .put("transactionId", "goblin-market") - .put("created", "1984-12-18T00:00:00.000Z") - .put("description", "USD Invoice for December 1984") - .put("currency", "USD") - .put("amount", "10.00") - .put("balance", "10.00") - .build()); - } - - @Test - public void testBadTimeOrdering_causesError() { - IllegalStateException thrown = - assertThrows( - IllegalStateException.class, - () -> - new RegistrarBillingEntry.Builder() - .setPrevious( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setDescription("USD Invoice for December") - .setAmount(Money.parse("USD 10.00")) - .build()) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-17TZ")) - .setTransactionId("goblin") - .setDescription("USD Invoice for August") - .setAmount(Money.parse("USD 3.50")) - .build()); - assertThat(thrown).hasMessageThat().contains("Created timestamp not after previous"); - } - - @Test - public void testRegistrarMismatch_causesError() { - IllegalStateException thrown = - assertThrows( - IllegalStateException.class, - () -> - new RegistrarBillingEntry.Builder() - .setPrevious( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setDescription("USD Invoice for December") - .setAmount(Money.parse("USD 10.00")) - .build()) - .setParent(loadRegistrar("TheRegistrar")) - .setCreated(DateTime.parse("1984-12-17TZ")) - .setTransactionId("goblin") - .setDescription("USD Invoice for August") - .setAmount(Money.parse("USD 3.50")) - .build()); - assertThat(thrown).hasMessageThat().contains("Parent not same as previous"); - } - - @Test - public void testCurrencyMismatch_causesError() { - assertThrows( - CurrencyMismatchException.class, - () -> - new RegistrarBillingEntry.Builder() - .setPrevious( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setDescription("USD Invoice for December") - .setAmount(Money.parse("USD 10.00")) - .build()) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-17TZ")) - .setTransactionId("goblin") - .setDescription("JPY Invoice for August") - .setAmount(Money.parse("JPY 350")) - .build()); - } - - @Test - public void testZeroAmount_causesError() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setCreated(DateTime.parse("1984-12-18TZ")) - .setDescription("USD Invoice for December") - .setAmount(Money.zero(USD)) - .build()); - assertThat(thrown).hasMessageThat().contains("Amount can't be zero"); - } - - @Test - public void testEmptyTransactionId_becomeNull() { - assertThat( - new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(loadRegistrar("NewRegistrar")) - .setTransactionId("") - .setCreated(DateTime.parse("1984-12-18TZ")) - .setDescription("USD Invoice for December 1984") - .setAmount(Money.parse("USD 10.00")) - .build() - .getTransactionId()) - .isNull(); - } -} diff --git a/javatests/google/registry/model/billing/RegistrarBillingUtilsTest.java b/javatests/google/registry/model/billing/RegistrarBillingUtilsTest.java deleted file mode 100644 index a65976fc0..000000000 --- a/javatests/google/registry/model/billing/RegistrarBillingUtilsTest.java +++ /dev/null @@ -1,144 +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.billing; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.testing.DatastoreHelper.createTlds; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.DatastoreHelper.persistSimpleResources; -import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static java.util.Arrays.asList; -import static org.joda.money.CurrencyUnit.JPY; -import static org.joda.money.CurrencyUnit.USD; - -import com.google.common.collect.ImmutableSortedMap; -import google.registry.model.ofy.Ofy; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import google.registry.testing.AppEngineRule; -import google.registry.testing.FakeClock; -import google.registry.testing.InjectRule; -import java.util.Map; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.joda.time.Duration; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link RegistrarBillingUtils}. */ -@RunWith(JUnit4.class) -public final class RegistrarBillingUtilsTest { - - @Rule - public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); - - @Rule - public InjectRule inject = new InjectRule(); - - private final FakeClock clock = new FakeClock(DateTime.parse("1984-12-18TZ")); - private Registrar registrar; - - @Before - public void before() { - inject.setStaticField(Ofy.class, "clock", clock); - registrar = loadRegistrar("NewRegistrar"); - createTlds("xn--q9jyb4c", "com", "net"); - persistResource( - Registry.get("xn--q9jyb4c").asBuilder() - .setCurrency(JPY) - .setRenewBillingCostTransitions( - ImmutableSortedMap.of(START_OF_TIME, Money.parse("JPY 110"))) - .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.parse("JPY 0"))) - .setCreateBillingCost(Money.parse("JPY 130")) - .setRestoreBillingCost(Money.parse("JPY 170")) - .setServerStatusChangeBillingCost(Money.parse("JPY 190")) - .build()); - } - - @Test - public void testGetCurrencies_returnsAllCurrenciesEnabledOnRegistrySorted() { - assertThat(RegistrarBillingUtils.getCurrencies()) - .containsExactly(JPY, USD) - .inOrder(); - } - - @Test - public void testLoadBalance_noHistory_returnsZeroes() { - Map balance = RegistrarBillingUtils.loadBalance(registrar); - assertThat(balance).hasSize(2); - assertThat(balance).containsEntry(USD, Money.parse("USD 0.00")); - assertThat(balance).containsEntry(JPY, Money.parse("JPY 0")); - } - - @Test - public void testLoadBalance_oneCurrency_hasTwoEntriesWithSumAndZero() { - RegistrarBillingEntry entry1 = new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(registrar) - .setCreated(clock.nowUtc()) - .setDescription("USD Invoice for July") - .setAmount(Money.parse("USD 10.00")) - .build(); - clock.advanceBy(Duration.standardDays(30)); - RegistrarBillingEntry entry2 = new RegistrarBillingEntry.Builder() - .setPrevious(entry1) - .setParent(registrar) - .setCreated(clock.nowUtc()) - .setDescription("USD Invoice for August") - .setAmount(Money.parse("USD 23.00")) - .build(); - persistSimpleResources(asList(entry1, entry2)); - Map balance = RegistrarBillingUtils.loadBalance(registrar); - assertThat(balance).hasSize(2); - assertThat(balance).containsEntry(USD, Money.parse("USD 33.00")); - assertThat(balance).containsEntry(JPY, Money.parse("JPY 0")); - } - - @Test - public void testLoadBalance_twoCurrencies_hasTwoEntriesWithSum() { - RegistrarBillingEntry entry1 = new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(registrar) - .setCreated(clock.nowUtc()) - .setDescription("USD Invoice for July") - .setAmount(Money.parse("USD 10.00")) - .build(); - clock.advanceBy(Duration.standardDays(30)); - RegistrarBillingEntry entry2 = new RegistrarBillingEntry.Builder() - .setPrevious(entry1) - .setParent(registrar) - .setCreated(clock.nowUtc()) - .setDescription("USD Invoice for August") - .setAmount(Money.parse("USD 3.50")) - .build(); - RegistrarBillingEntry entry3 = new RegistrarBillingEntry.Builder() - .setPrevious(null) - .setParent(registrar) - .setCreated(clock.nowUtc()) - .setDescription("JPY Invoice for August") - .setAmount(Money.parse("JPY 666")) - .build(); - persistSimpleResources(asList(entry1, entry2, entry3)); - Map balance = RegistrarBillingUtils.loadBalance(registrar); - assertThat(balance).hasSize(2); - assertThat(balance).containsEntry(USD, Money.parse("USD 13.50")); - assertThat(balance).containsEntry(JPY, Money.parse("JPY 666")); - } -} diff --git a/javatests/google/registry/model/billing/RegistrarCreditBalanceTest.java b/javatests/google/registry/model/billing/RegistrarCreditBalanceTest.java deleted file mode 100644 index faad25cf4..000000000 --- a/javatests/google/registry/model/billing/RegistrarCreditBalanceTest.java +++ /dev/null @@ -1,187 +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.billing; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth8.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.JUnitBackports.assertThrows; -import static google.registry.util.DateTimeUtils.END_OF_TIME; -import static google.registry.util.DateTimeUtils.START_OF_TIME; - -import com.google.common.collect.ImmutableMap; -import google.registry.model.EntityTestCase; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance.BalanceMap; -import google.registry.model.registrar.Registrar; -import java.util.Map; -import org.joda.money.CurrencyUnit; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Before; -import org.junit.Test; - -/** Unit tests for {@link RegistrarCreditBalance}. */ -public class RegistrarCreditBalanceTest extends EntityTestCase { - private DateTime then = clock.nowUtc().plusDays(1); - - private Registrar theRegistrar; - private RegistrarCredit unpersistedCredit; - private RegistrarCredit credit; - private RegistrarCreditBalance balance; - - Map> rawBalanceMap; - - @Before - public void setUp() { - createTld("tld"); - theRegistrar = loadRegistrar("TheRegistrar"); - unpersistedCredit = makeCredit(theRegistrar, clock.nowUtc()); - credit = persistResource(makeCredit(theRegistrar, clock.nowUtc())); - balance = persistResource( - makeBalance(credit, Money.parse("USD 90"), clock.nowUtc(), clock.nowUtc())); - persistResource( - makeBalance(credit, Money.parse("USD 80"), clock.nowUtc(), clock.nowUtc().plusMillis(1))); - persistResource( - makeBalance(credit, Money.parse("USD 70"), clock.nowUtc(), clock.nowUtc().plusMillis(2))); - persistResource( - makeBalance(credit, Money.parse("USD 40"), then, then)); - persistResource( - makeBalance(credit, Money.parse("USD 30"), then, then.plusMillis(1))); - persistResource( - makeBalance(credit, Money.parse("USD 20"), then, then.plusMillis(2))); - rawBalanceMap = ImmutableMap.of( - clock.nowUtc(), - ImmutableMap.of( - clock.nowUtc(), Money.parse("USD 90"), - clock.nowUtc().plusMillis(1), Money.parse("USD 80"), - clock.nowUtc().plusMillis(2), Money.parse("USD 70")), - then, - ImmutableMap.of( - then, Money.parse("USD 40"), - then.plusMillis(1), Money.parse("USD 30"), - then.plusMillis(2), Money.parse("USD 20"))); - } - - @Test - public void testPersistence() { - assertThat(ofy().load().entity(balance).now()).isEqualTo(balance); - } - - @Test - public void testIndexing() throws Exception { - // No indexing needed, so we don't expect any indices. - verifyIndexing(balance); - } - @Test - public void testSuccess_balanceWithUnpersistedCredit() { - balance.asBuilder().setParent(unpersistedCredit).build(); - } - - @Test - public void testFailure_balanceNotInCreditCurrency() { - assertThrows( - IllegalStateException.class, - () -> balance.asBuilder().setAmount(Money.parse("JPY 1")).build()); - } - - @Test - public void testFailure_balanceNotInCreditCurrencyWithUnpersistedCredit() { - assertThrows( - IllegalStateException.class, - () -> - balance - .asBuilder() - .setParent(unpersistedCredit) - .setAmount(Money.parse("JPY 1")) - .build()); - } - - @Test - public void testSuccess_balanceMap_createForCredit() { - assertThat(BalanceMap.createForCredit(credit)).isEqualTo(rawBalanceMap); - } - - @Test - public void testSuccess_balanceMap_createForEmptyCredit() { - assertThat(BalanceMap.createForCredit(makeCredit(theRegistrar, clock.nowUtc()))).isEmpty(); - } - - @Test - public void testSuccess_balanceMap_getActiveBalance_emptyMap() { - BalanceMap map = new BalanceMap(ImmutableMap.of()); - assertThat(map.getActiveBalanceAtTime(START_OF_TIME)).isEmpty(); - assertThat(map.getActiveBalanceAtTime(clock.nowUtc())).isEmpty(); - assertThat(map.getActiveBalanceAtTime(END_OF_TIME)).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(START_OF_TIME)).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(clock.nowUtc())).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(END_OF_TIME)).isEmpty(); - } - - @Test - public void testSuccess_balanceMap_getActiveBalanceAtTime() { - BalanceMap map = new BalanceMap(rawBalanceMap); - assertThat(map.getActiveBalanceAtTime(START_OF_TIME)).isEmpty(); - assertThat(map.getActiveBalanceAtTime(clock.nowUtc().minusMillis(1))).isEmpty(); - assertThat(map.getActiveBalanceAtTime(clock.nowUtc()).get()).isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceAtTime(clock.nowUtc().plusMillis(1)).get()) - .isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceAtTime(then.minusMillis(1)).get()) - .isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceAtTime(then).get()).isEqualTo(Money.parse("USD 20")); - assertThat(map.getActiveBalanceAtTime(then.plusMillis(1)).get()) - .isEqualTo(Money.parse("USD 20")); - assertThat(map.getActiveBalanceAtTime(END_OF_TIME).get()).isEqualTo(Money.parse("USD 20")); - } - - @Test - public void testSuccess_balanceMap_getActiveBalanceBeforeTime() { - BalanceMap map = new BalanceMap(rawBalanceMap); - assertThat(map.getActiveBalanceBeforeTime(START_OF_TIME)).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(clock.nowUtc().minusMillis(1))).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(clock.nowUtc())).isEmpty(); - assertThat(map.getActiveBalanceBeforeTime(clock.nowUtc().plusMillis(1)).get()) - .isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceBeforeTime(then.minusMillis(1)).get()) - .isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceBeforeTime(then).get()).isEqualTo(Money.parse("USD 70")); - assertThat(map.getActiveBalanceBeforeTime(then.plusMillis(1)).get()) - .isEqualTo(Money.parse("USD 20")); - assertThat(map.getActiveBalanceBeforeTime(END_OF_TIME).get()).isEqualTo(Money.parse("USD 20")); - } - - private static RegistrarCredit makeCredit(Registrar parent, DateTime creationTime) { - return new RegistrarCredit.Builder() - .setParent(parent) - .setType(CreditType.PROMOTION) - .setCurrency(CurrencyUnit.USD) - .setTld("tld") - .setCreationTime(creationTime) - .build(); - } - - private static RegistrarCreditBalance makeBalance( - RegistrarCredit parent, Money amount, DateTime effectiveTime, DateTime writtenTime) { - return new RegistrarCreditBalance.Builder() - .setParent(parent) - .setEffectiveTime(effectiveTime) - .setAmount(amount) - .setWrittenTime(writtenTime) - .build(); - } -} diff --git a/javatests/google/registry/model/billing/RegistrarCreditTest.java b/javatests/google/registry/model/billing/RegistrarCreditTest.java deleted file mode 100644 index 1928ae7a6..000000000 --- a/javatests/google/registry/model/billing/RegistrarCreditTest.java +++ /dev/null @@ -1,103 +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.billing; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.JUnitBackports.assertThrows; -import static org.joda.money.CurrencyUnit.JPY; -import static org.joda.money.CurrencyUnit.USD; - -import google.registry.model.EntityTestCase; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import org.joda.money.CurrencyUnit; -import org.junit.Before; -import org.junit.Test; - -/** Unit tests for {@link RegistrarCredit}. */ -public class RegistrarCreditTest extends EntityTestCase { - private RegistrarCredit auctionCredit; - private RegistrarCredit promoCredit; - - @Before - public void setUp() { - createTld("tld"); - Registrar theRegistrar = ofy().load() - .type(Registrar.class) - .parent(getCrossTldKey()) - .id("TheRegistrar").now(); - auctionCredit = persistResource( - new RegistrarCredit.Builder() - .setParent(theRegistrar) - .setType(CreditType.AUCTION) - .setCurrency(CurrencyUnit.USD) - .setTld("tld") - .setCreationTime(clock.nowUtc()) - .build()); - promoCredit = persistResource( - new RegistrarCredit.Builder() - .setParent(theRegistrar) - .setType(CreditType.PROMOTION) - .setCurrency(CurrencyUnit.USD) - .setTld("tld") - .setCreationTime(clock.nowUtc()) - .build()); - } - - @Test - public void testPersistence() { - assertThat(ofy().load().entity(auctionCredit).now()).isEqualTo(auctionCredit); - assertThat(ofy().load().entity(promoCredit).now()).isEqualTo(promoCredit); - } - - @Test - public void testIndexing() throws Exception { - // No indexing needed, so we don't expect any indices. - verifyIndexing(auctionCredit); - verifyIndexing(promoCredit); - } - - @Test - public void testFailure_missingTld() { - NullPointerException thrown = - assertThrows( - NullPointerException.class, () -> promoCredit.asBuilder().setTld(null).build()); - assertThat(thrown).hasMessageThat().contains("tld"); - } - - @Test - public void testFailure_NonexistentTld() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> promoCredit.asBuilder().setTld("example").build()); - assertThat(thrown).hasMessageThat().contains("example"); - } - - @Test - public void testFailure_CurrencyDoesNotMatchTldCurrency() { - assertThat(Registry.get("tld").getCurrency()).isEqualTo(USD); - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> promoCredit.asBuilder().setTld("tld").setCurrency(JPY).build()); - assertThat(thrown).hasMessageThat().contains("currency"); - } -} diff --git a/javatests/google/registry/model/testdata/schema.txt b/javatests/google/registry/model/testdata/schema.txt index 88ac92989..bc1c8b48c 100644 --- a/javatests/google/registry/model/testdata/schema.txt +++ b/javatests/google/registry/model/testdata/schema.txt @@ -70,36 +70,6 @@ class google.registry.model.billing.BillingEvent$Recurring { org.joda.time.DateTime eventTime; org.joda.time.DateTime recurrenceEndTime; } -class google.registry.model.billing.RegistrarBillingEntry { - @Id long id; - @Parent com.googlecode.objectify.Key parent; - java.lang.String description; - java.lang.String transactionId; - org.joda.money.CurrencyUnit currency; - org.joda.money.Money amount; - org.joda.money.Money balance; - org.joda.time.DateTime created; -} -class google.registry.model.billing.RegistrarCredit { - @Id long id; - @Parent com.googlecode.objectify.Key parent; - google.registry.model.billing.RegistrarCredit$CreditType type; - java.lang.String description; - java.lang.String tld; - org.joda.money.CurrencyUnit currency; - org.joda.time.DateTime creationTime; -} -enum google.registry.model.billing.RegistrarCredit$CreditType { - AUCTION; - PROMOTION; -} -class google.registry.model.billing.RegistrarCreditBalance { - @Id long id; - @Parent com.googlecode.objectify.Key parent; - org.joda.money.Money amount; - org.joda.time.DateTime effectiveTime; - org.joda.time.DateTime writtenTime; -} class google.registry.model.common.Cursor { @Id java.lang.String id; @Parent com.googlecode.objectify.Key parent; diff --git a/javatests/google/registry/tools/CreateCreditBalanceCommandTest.java b/javatests/google/registry/tools/CreateCreditBalanceCommandTest.java deleted file mode 100644 index 78167ea14..000000000 --- a/javatests/google/registry/tools/CreateCreditBalanceCommandTest.java +++ /dev/null @@ -1,176 +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.tools; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.JUnitBackports.assertThrows; -import static org.joda.money.CurrencyUnit.USD; -import static org.joda.time.DateTimeZone.UTC; - -import com.beust.jcommander.ParameterException; -import com.google.common.collect.Range; -import com.googlecode.objectify.Key; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Before; -import org.junit.Test; - -/** Unit tests for {@link CreateCreditBalanceCommand}. */ -public class CreateCreditBalanceCommandTest extends CommandTestCase { - - private Registrar registrar; - private RegistrarCredit credit; - private long creditId; - - @Before - public void setUp() { - registrar = loadRegistrar("TheRegistrar"); - createTld("tld"); - assertThat(Registry.get("tld").getCurrency()).isEqualTo(USD); - credit = persistResource( - new RegistrarCredit.Builder() - .setParent(registrar) - .setType(CreditType.PROMOTION) - .setTld("tld") - .setCurrency(USD) - .setCreationTime(Registry.get("tld").getCreationTime().plusMillis(1)) - .build()); - creditId = Key.create(credit).getId(); - } - - @Test - public void testSuccess() throws Exception { - DateTime before = DateTime.now(UTC); - - runCommandForced( - "--registrar=TheRegistrar", - "--credit_id=" + creditId, - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z"); - - RegistrarCreditBalance creditBalance = - getOnlyElement(ofy().load().type(RegistrarCreditBalance.class).ancestor(credit)); - assertThat(creditBalance).isNotNull(); - assertThat(ofy().load().key(creditBalance.getParent()).now()).isEqualTo(credit); - assertThat(creditBalance.getEffectiveTime()).isEqualTo(DateTime.parse("2014-11-01T01:02:03Z")); - assertThat(creditBalance.getWrittenTime()).isIn(Range.closed(before, DateTime.now(UTC))); - assertThat(creditBalance.getAmount()).isEqualTo(Money.of(USD, 100)); - } - - @Test - public void testFailure_nonexistentParentRegistrar() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - runCommandForced( - "--registrar=FakeRegistrar", - "--credit_id=" + creditId, - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("Registrar FakeRegistrar not found"); - } - - @Test - public void testFailure_nonexistentCreditId() { - long badId = creditId + 1; - NullPointerException thrown = - assertThrows( - NullPointerException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--credit_id=" + badId, - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("ID " + badId); - } - - @Test - public void testFailure_negativeBalance() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--credit_id=" + creditId, - "--balance=\"USD -1\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("negative"); - } - - @Test - public void testFailure_noRegistrar() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--credit_id=" + creditId, - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--registrar"); - } - - @Test - public void testFailure_noCreditId() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--credit_id"); - } - - @Test - public void testFailure_noBalance() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--credit_id=" + creditId, - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--balance"); - } - - @Test - public void testFailure_noEffectiveTime() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--credit_id=" + creditId, - "--balance=\"USD 100\"")); - assertThat(thrown).hasMessageThat().contains("--effective_time"); - } -} diff --git a/javatests/google/registry/tools/CreateCreditCommandTest.java b/javatests/google/registry/tools/CreateCreditCommandTest.java deleted file mode 100644 index a79abc4e9..000000000 --- a/javatests/google/registry/tools/CreateCreditCommandTest.java +++ /dev/null @@ -1,224 +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.tools; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.JUnitBackports.assertThrows; -import static org.joda.money.CurrencyUnit.USD; -import static org.joda.time.DateTimeZone.UTC; - -import com.beust.jcommander.ParameterException; -import com.google.common.collect.Range; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Before; -import org.junit.Test; - -/** Unit tests for {@link CreateCreditCommand}. */ -public class CreateCreditCommandTest extends CommandTestCase { - - private Registrar registrar; - - @Before - public void setUp() { - createTld("tld"); - registrar = loadRegistrar("TheRegistrar"); - assertThat(Registry.get("tld").getCurrency()).isEqualTo(USD); - } - - @Test - public void testSuccess() throws Exception { - DateTime before = DateTime.now(UTC); - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--description=\"Some kind of credit\"", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z"); - - RegistrarCredit credit = - getOnlyElement(ofy().load().type(RegistrarCredit.class).ancestor(registrar)); - assertThat(credit).isNotNull(); - assertThat(ofy().load().key(credit.getParent()).now()).isEqualTo(registrar); - assertThat(credit.getType()).isEqualTo(CreditType.PROMOTION); - assertThat(credit.getTld()).isEqualTo("tld"); - assertThat(credit.getDescription()).isEqualTo("Some kind of credit"); - assertThat(credit.getCurrency()).isEqualTo(USD); - assertThat(credit.getCreationTime()).isIn(Range.closed(before, DateTime.now(UTC))); - - RegistrarCreditBalance creditBalance = - getOnlyElement(ofy().load().type(RegistrarCreditBalance.class).ancestor(credit)); - assertThat(creditBalance).isNotNull(); - assertThat(ofy().load().key(creditBalance.getParent()).now()).isEqualTo(credit); - assertThat(creditBalance.getEffectiveTime()).isEqualTo(DateTime.parse("2014-11-01T01:02:03Z")); - assertThat(creditBalance.getWrittenTime()).isEqualTo(credit.getCreationTime()); - assertThat(creditBalance.getAmount()).isEqualTo(Money.of(USD, 100)); - } - - @Test - public void testSuccess_defaultDescription() throws Exception { - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z"); - - RegistrarCredit credit = - getOnlyElement(ofy().load().type(RegistrarCredit.class).ancestor(registrar)); - assertThat(credit).isNotNull(); - assertThat(credit.getDescription()).isEqualTo("Promotional Credit for .tld"); - } - - @Test - public void testFailure_nonexistentParentRegistrar() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - runCommandForced( - "--registrar=FakeRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("Registrar FakeRegistrar not found"); - } - - @Test - public void testFailure_nonexistentTld() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=faketld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("faketld"); - } - - @Test - public void testFailure_nonexistentType() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=BADTYPE", - "--tld=tld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("Invalid value for --type"); - } - - @Test - public void testFailure_negativeBalance() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--balance=\"USD -1\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("negative"); - } - - @Test - public void testFailure_noRegistrar() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--type=PROMOTION", - "--tld=tld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--registrar"); - } - - @Test - public void testFailure_noType() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--tld=tld", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--type"); - } - - @Test - public void testFailure_noTld() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--balance=\"USD 100\"", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--tld"); - } - - @Test - public void testFailure_noBalance() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--effective_time=2014-11-01T01:02:03Z")); - assertThat(thrown).hasMessageThat().contains("--balance"); - } - - @Test - public void testFailure_noEffectiveTime() { - ParameterException thrown = - assertThrows( - ParameterException.class, - () -> - runCommandForced( - "--registrar=TheRegistrar", - "--type=PROMOTION", - "--tld=tld", - "--balance=\"USD 100\"")); - assertThat(thrown).hasMessageThat().contains("--effective_time"); - } -} diff --git a/javatests/google/registry/tools/DeleteCreditCommandTest.java b/javatests/google/registry/tools/DeleteCreditCommandTest.java deleted file mode 100644 index 031aae275..000000000 --- a/javatests/google/registry/tools/DeleteCreditCommandTest.java +++ /dev/null @@ -1,145 +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.tools; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; -import static google.registry.model.ofy.ObjectifyService.ofy; -import static google.registry.testing.DatastoreHelper.createTld; -import static google.registry.testing.DatastoreHelper.loadRegistrar; -import static google.registry.testing.DatastoreHelper.persistResource; -import static google.registry.testing.JUnitBackports.assertThrows; -import static org.joda.money.CurrencyUnit.USD; - -import com.beust.jcommander.ParameterException; -import com.googlecode.objectify.Key; -import google.registry.model.billing.RegistrarCredit; -import google.registry.model.billing.RegistrarCredit.CreditType; -import google.registry.model.billing.RegistrarCreditBalance; -import google.registry.model.registrar.Registrar; -import google.registry.model.registry.Registry; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.Before; -import org.junit.Test; - -/** Unit tests for {@link DeleteCreditCommand}. */ -public class DeleteCreditCommandTest extends CommandTestCase { - - private Registrar registrar; - private RegistrarCredit creditA; - private RegistrarCredit creditB; - private RegistrarCreditBalance balanceA1; - private RegistrarCreditBalance balanceA2; - private RegistrarCreditBalance balanceB1; - private long creditAId; - private long creditBId; - - @Before - public void setUp() { - registrar = loadRegistrar("TheRegistrar"); - createTld("tld"); - assertThat(Registry.get("tld").getCurrency()).isEqualTo(USD); - DateTime creditCreationTime = Registry.get("tld").getCreationTime().plusMillis(1); - creditA = persistResource( - new RegistrarCredit.Builder() - .setParent(registrar) - .setType(CreditType.PROMOTION) - .setTld("tld") - .setCurrency(USD) - .setCreationTime(creditCreationTime) - .build()); - creditB = persistResource( - new RegistrarCredit.Builder() - .setParent(registrar) - .setType(CreditType.AUCTION) - .setTld("tld") - .setCurrency(USD) - .setCreationTime(creditCreationTime) - .build()); - balanceA1 = persistResource( - new RegistrarCreditBalance.Builder() - .setParent(creditA) - .setEffectiveTime(DateTime.parse("2014-11-01T00:00:00Z")) - .setAmount(Money.of(USD, 100)) - .setWrittenTime(creditCreationTime) - .build()); - balanceA2 = persistResource( - new RegistrarCreditBalance.Builder() - .setParent(creditA) - .setEffectiveTime(DateTime.parse("2014-12-01T00:00:00Z")) - .setAmount(Money.of(USD, 50)) - .setWrittenTime(creditCreationTime) - .build()); - balanceB1 = persistResource( - new RegistrarCreditBalance.Builder() - .setParent(creditB) - .setEffectiveTime(DateTime.parse("2014-11-01T00:00:00Z")) - .setAmount(Money.of(USD, 42)) - .setWrittenTime(creditCreationTime) - .build()); - creditAId = Key.create(creditA).getId(); - creditBId = Key.create(creditB).getId(); - } - - @Test - public void testSuccess() throws Exception { - assertThat(RegistrarCredit.loadAllForRegistrar(registrar)).containsAllOf(creditA, creditB); - assertThat(ofy().load().type(RegistrarCreditBalance.class).ancestor(getCrossTldKey())) - .containsAllOf(balanceA1, balanceA2, balanceB1); - - runCommandForced("--registrar=TheRegistrar", "--credit_id=" + creditAId); - - assertThat(RegistrarCredit.loadAllForRegistrar(registrar)).doesNotContain(creditA); - assertThat(RegistrarCredit.loadAllForRegistrar(registrar)).contains(creditB); - assertThat(ofy().load().type(RegistrarCreditBalance.class).ancestor(getCrossTldKey())) - .containsNoneOf(balanceA1, balanceA2); - assertThat(ofy().load().type(RegistrarCreditBalance.class).ancestor(getCrossTldKey())) - .contains(balanceB1); - } - - @Test - public void testFailure_nonexistentParentRegistrar() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> runCommandForced("--registrar=FakeRegistrar", "--credit_id=" + creditAId)); - assertThat(thrown).hasMessageThat().contains("Registrar FakeRegistrar not found"); - } - - @Test - public void testFailure_nonexistentCreditId() { - long badId = creditAId + creditBId + 1; - NullPointerException thrown = - assertThrows( - NullPointerException.class, - () -> runCommandForced("--registrar=TheRegistrar", "--credit_id=" + badId)); - assertThat(thrown).hasMessageThat().contains("ID " + badId); - } - - @Test - public void testFailure_noRegistrar() { - ParameterException thrown = - assertThrows(ParameterException.class, () -> runCommandForced("--credit_id=" + creditAId)); - assertThat(thrown).hasMessageThat().contains("--registrar"); - } - - @Test - public void testFailure_noCreditId() { - ParameterException thrown = - assertThrows(ParameterException.class, () -> runCommandForced("--registrar=TheRegistrar")); - assertThat(thrown).hasMessageThat().contains("--credit_id"); - } -} diff --git a/javatests/google/registry/tools/UpdateRegistrarCommandTest.java b/javatests/google/registry/tools/UpdateRegistrarCommandTest.java index 72a7bd32a..98ee19b2d 100644 --- a/javatests/google/registry/tools/UpdateRegistrarCommandTest.java +++ b/javatests/google/registry/tools/UpdateRegistrarCommandTest.java @@ -28,14 +28,12 @@ import com.beust.jcommander.ParameterException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import google.registry.model.billing.RegistrarBillingEntry; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar.BillingMethod; import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.Registrar.Type; import google.registry.util.CidrAddressBlock; import org.joda.money.CurrencyUnit; -import org.joda.money.Money; import org.joda.time.DateTime; import org.junit.Test; @@ -269,30 +267,6 @@ public class UpdateRegistrarCommandTest extends CommandTestCase runCommand("--billing_method=braintree", "--force", "NewRegistrar")); - assertThat(thrown) - .hasMessageThat() - .isEqualTo( - "Refusing to change billing method on Registrar 'NewRegistrar' from EXTERNAL to " - + "BRAINTREE because current balance is non-zero: {USD=USD 10.00}"); - } - @Test public void testSuccess_streetAddress() throws Exception { runCommand("--street=\"1234 Main St\"", "--street \"4th Floor\"", "--street \"Suite 1\"",