diff --git a/core/src/main/java/google/registry/model/registry/label/PremiumListDualDao.java b/core/src/main/java/google/registry/model/registry/label/PremiumListDualDao.java index 754736943..7e3f81e06 100644 --- a/core/src/main/java/google/registry/model/registry/label/PremiumListDualDao.java +++ b/core/src/main/java/google/registry/model/registry/label/PremiumListDualDao.java @@ -25,6 +25,7 @@ import google.registry.model.registry.label.PremiumList.PremiumListEntry; import google.registry.schema.tld.PremiumListSqlDao; import java.util.List; import java.util.Optional; +import org.joda.money.BigMoney; import org.joda.money.CurrencyUnit; import org.joda.money.Money; @@ -182,7 +183,7 @@ public class PremiumListDualDao { .map( premiumEntry -> new PremiumListEntry.Builder() - .setPrice(Money.of(currencyUnit, premiumEntry.getPrice())) + .setPrice(BigMoney.of(currencyUnit, premiumEntry.getPrice()).toMoney()) .setLabel(premiumEntry.getDomainLabel()) .build()) .collect(toImmutableList()); diff --git a/core/src/main/java/google/registry/tools/ComparePremiumListsCommand.java b/core/src/main/java/google/registry/tools/ComparePremiumListsCommand.java new file mode 100644 index 000000000..b84b567ae --- /dev/null +++ b/core/src/main/java/google/registry/tools/ComparePremiumListsCommand.java @@ -0,0 +1,105 @@ +// Copyright 2021 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.tools; + +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; + +import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import google.registry.model.registry.label.PremiumList; +import google.registry.model.registry.label.PremiumList.PremiumListEntry; +import google.registry.model.registry.label.PremiumListDatastoreDao; +import google.registry.schema.tld.PremiumEntry; +import google.registry.schema.tld.PremiumListSqlDao; +import java.util.Optional; +import org.joda.money.BigMoney; + +/** Command to compare all PremiumLists in Datastore to all PremiumLists in Cloud SQL. */ +@Parameters( + separators = " =", + commandDescription = "Compare all the PremiumLists in Datastore to those in Cloud SQL.") +final class ComparePremiumListsCommand implements CommandWithRemoteApi { + + @Override + public void run() { + ImmutableSet datastoreLists = + ofyTm().loadAllOf(PremiumList.class).stream() + .map(PremiumList::getName) + .collect(toImmutableSet()); + + ImmutableSet sqlLists = + jpaTm() + .transact( + () -> + jpaTm().loadAllOf(PremiumList.class).stream() + .map(PremiumList::getName) + .collect(toImmutableSet())); + + int listsWithDiffs = 0; + + for (String listName : Sets.difference(datastoreLists, sqlLists)) { + listsWithDiffs++; + System.out.printf( + "PremiumList '%s' is present in Datastore, but not in Cloud SQL.%n", listName); + } + for (String listName : Sets.difference(sqlLists, datastoreLists)) { + listsWithDiffs++; + System.out.printf( + "PremiumList '%s' is present in Cloud SQL, but not in Datastore.%n", listName); + } + + for (String listName : Sets.intersection(datastoreLists, sqlLists)) { + Optional sqlList = PremiumListSqlDao.getLatestRevision(listName); + + // Datastore and Cloud SQL use different objects to represent premium list entries + // so the best way to compare them is to compare their string representations. + ImmutableSet datastoreListStrings = + Streams.stream( + PremiumListDatastoreDao.loadPremiumListEntriesUncached( + PremiumListDatastoreDao.getLatestRevision(listName).get())) + .map(PremiumListEntry::toString) + .collect(toImmutableSet()); + + Iterable sqlListEntries = + jpaTm().transact(() -> PremiumListSqlDao.loadPremiumListEntriesUncached(sqlList.get())); + + ImmutableSet sqlListStrings = + Streams.stream(sqlListEntries) + .map( + premiumEntry -> + new PremiumListEntry.Builder() + .setPrice( + BigMoney.of(sqlList.get().getCurrency(), premiumEntry.getPrice()) + .toMoney()) + .setLabel(premiumEntry.getDomainLabel()) + .build() + .toString()) + .collect(toImmutableSet()); + + // This will only print out the name of the unequal list. GetPremiumListCommand + // should be used to determine what the actual differences are. + if (!datastoreListStrings.equals(sqlListStrings)) { + listsWithDiffs++; + System.out.printf("PremiumList '%s' has different entries in each database.%n", listName); + } + } + + System.out.printf("Found %d unequal list(s).%n", listsWithDiffs); + } +} diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java index b6803e5c5..0e2b410d9 100644 --- a/core/src/main/java/google/registry/tools/RegistryTool.java +++ b/core/src/main/java/google/registry/tools/RegistryTool.java @@ -38,6 +38,7 @@ public final class RegistryTool { .put("canonicalize_labels", CanonicalizeLabelsCommand.class) .put("check_domain", CheckDomainCommand.class) .put("check_domain_claims", CheckDomainClaimsCommand.class) + .put("compare_premium_lists", ComparePremiumListsCommand.class) .put("compare_reserved_lists", CompareReservedListsCommand.class) .put("convert_idn", ConvertIdnCommand.class) .put("count_domains", CountDomainsCommand.class) diff --git a/core/src/test/java/google/registry/tools/ComparePremiumListsCommandTest.java b/core/src/test/java/google/registry/tools/ComparePremiumListsCommandTest.java new file mode 100644 index 000000000..0d27eb719 --- /dev/null +++ b/core/src/test/java/google/registry/tools/ComparePremiumListsCommandTest.java @@ -0,0 +1,86 @@ +// Copyright 2021 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.tools; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; +import static google.registry.testing.DatabaseHelper.persistPremiumList; +import static org.joda.money.CurrencyUnit.USD; + +import com.google.common.collect.ImmutableMap; +import google.registry.model.registry.label.PremiumList; +import google.registry.model.registry.label.PremiumListDatastoreDao; +import google.registry.schema.tld.PremiumListSqlDao; +import java.math.BigDecimal; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ComparePremiumListsCommandTest extends CommandTestCase { + + @BeforeEach + void beforeEach() { + persistPremiumList("xn--q9jyb4c", "rich,USD 100"); + persistPremiumList("how", "richer,JPY 10000"); + } + + @Test + void test_success() throws Exception { + runCommand(); + assertThat(getStdoutAsString()).isEqualTo("Found 0 unequal list(s).\n"); + } + + @Test + void test_listMissingFromCloudSql() throws Exception { + jpaTm() + .transact( + () -> { + PremiumList premiumList = PremiumListSqlDao.getLatestRevision("how").get(); + PremiumListSqlDao.delete(premiumList); + }); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "PremiumList 'how' is present in Datastore, but not in Cloud SQL.\n" + + "Found 1 unequal list(s).\n"); + } + + @Test + void test_listMissingFromDatastore() throws Exception { + PremiumList premiumList = PremiumListDatastoreDao.getLatestRevision("how").get(); + ofyTm().transact(() -> ofyTm().delete(premiumList)); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "PremiumList 'how' is present in Cloud SQL, but not in Datastore.\n" + + "Found 1 unequal list(s).\n"); + } + + @Test + void test_listsDiffer() throws Exception { + PremiumListSqlDao.save( + new PremiumList.Builder() + .setName("how") + .setCurrency(USD) + .setLabelsToPrices(ImmutableMap.of("silver", BigDecimal.valueOf(30.03))) + .setCreationTime(fakeClock.nowUtc()) + .build()); + runCommand(); + assertThat(getStdoutAsString()) + .isEqualTo( + "PremiumList 'how' has different entries in each database.\n" + + "Found 1 unequal list(s).\n"); + } +}