diff --git a/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java b/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java index 38d3a6591..fcfaa91e3 100644 --- a/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java +++ b/core/src/main/java/google/registry/model/ofy/DatastoreTransactionManager.java @@ -444,5 +444,10 @@ public class DatastoreTransactionManager implements TransactionManager { public long count() { return buildQuery().count(); } + + @Override + public List list() { + return buildQuery().list(); + } } } diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java index 9381f2c31..06fe2d876 100644 --- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java +++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManagerImpl.java @@ -737,5 +737,10 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager { CriteriaQueryBuilder queryBuilder = CriteriaQueryBuilder.createCount(em, entityClass); return addCriteria(queryBuilder).getSingleResult(); } + + @Override + public List list() { + return buildQuery().getResultList(); + } } } diff --git a/core/src/main/java/google/registry/persistence/transaction/QueryComposer.java b/core/src/main/java/google/registry/persistence/transaction/QueryComposer.java index f53385f01..c145b294b 100644 --- a/core/src/main/java/google/registry/persistence/transaction/QueryComposer.java +++ b/core/src/main/java/google/registry/persistence/transaction/QueryComposer.java @@ -91,6 +91,9 @@ public abstract class QueryComposer { /** Returns the number of results of the query. */ public abstract long count(); + /** Returns the results of the query as a list. */ + public abstract List list(); + // We have to wrap the CriteriaQueryBuilder predicate factories in our own functions because at // the point where we pass them to the Comparator constructor, the compiler can't determine which // of the overloads to use since there is no "value" object for context. diff --git a/core/src/main/java/google/registry/tools/GenerateLordnCommand.java b/core/src/main/java/google/registry/tools/GenerateLordnCommand.java index 9ec7a60c1..9522c4e39 100644 --- a/core/src/main/java/google/registry/tools/GenerateLordnCommand.java +++ b/core/src/main/java/google/registry/tools/GenerateLordnCommand.java @@ -14,9 +14,10 @@ package google.registry.tools; -import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.QueryComposer.Comparator; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -24,9 +25,11 @@ import com.google.common.collect.ImmutableList; import google.registry.model.domain.DomainBase; import google.registry.tmch.LordnTaskUtils; import google.registry.tools.params.PathParameter; +import google.registry.util.Clock; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import javax.inject.Inject; import org.joda.time.DateTime; /** Command to generate a LORDN CSV file for an entire TLD. */ @@ -53,22 +56,21 @@ final class GenerateLordnCommand implements CommandWithRemoteApi { required = true) private Path sunriseOutputPath; + @Inject Clock clock; + @Override public void run() throws IOException { - DateTime now = DateTime.now(UTC); + DateTime now = clock.nowUtc(); ImmutableList.Builder claimsCsv = new ImmutableList.Builder<>(); ImmutableList.Builder sunriseCsv = new ImmutableList.Builder<>(); - for (DomainBase domain : ofy().load().type(DomainBase.class).filter("tld", tld)) { - String status = " "; - if (domain.getLaunchNotice() == null && domain.getSmdId() != null) { - sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain, domain.getCreationTime())); - status = "S"; - } else if (domain.getLaunchNotice() != null || domain.getSmdId() != null) { - claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain, domain.getCreationTime())); - status = "C"; - } - System.out.printf("%s[%s] ", domain.getDomainName(), status); - } + transactIfJpaTm( + () -> + tm() + .createQueryComposer(DomainBase.class) + .where("tld", Comparator.EQ, tld) + .orderBy("repoId") + .stream() + .forEach(domain -> processDomain(claimsCsv, sunriseCsv, domain))); ImmutableList claimsRows = claimsCsv.build(); ImmutableList claimsAll = new ImmutableList.Builder() @@ -86,4 +88,19 @@ final class GenerateLordnCommand implements CommandWithRemoteApi { Files.write(claimsOutputPath, claimsAll, UTF_8); Files.write(sunriseOutputPath, sunriseAll, UTF_8); } + + private static void processDomain( + ImmutableList.Builder claimsCsv, + ImmutableList.Builder sunriseCsv, + DomainBase domain) { + String status = " "; + if (domain.getLaunchNotice() == null && domain.getSmdId() != null) { + sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain, domain.getCreationTime())); + status = "S"; + } else if (domain.getLaunchNotice() != null || domain.getSmdId() != null) { + claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain, domain.getCreationTime())); + status = "C"; + } + System.out.printf("%s[%s] ", domain.getDomainName(), status); + } } diff --git a/core/src/test/java/google/registry/persistence/transaction/QueryComposerTest.java b/core/src/test/java/google/registry/persistence/transaction/QueryComposerTest.java index 70935e465..25e895053 100644 --- a/core/src/test/java/google/registry/persistence/transaction/QueryComposerTest.java +++ b/core/src/test/java/google/registry/persistence/transaction/QueryComposerTest.java @@ -206,6 +206,17 @@ public class QueryComposerTest { .isEqualTo(ImmutableList.of(alpha, bravo)); } + @TestOfyAndSql + public void testListQueries() { + assertThat( + transactIfJpaTm( + () -> + tm().createQueryComposer(TestEntity.class) + .where("name", Comparator.GT, "alpha") + .list())) + .isEqualTo(ImmutableList.of(bravo, charlie)); + } + @TestOfyAndSql public void testNonPrimaryKey() { assertThat( diff --git a/core/src/test/java/google/registry/tools/GenerateLordnCommandTest.java b/core/src/test/java/google/registry/tools/GenerateLordnCommandTest.java new file mode 100644 index 000000000..227f9e461 --- /dev/null +++ b/core/src/test/java/google/registry/tools/GenerateLordnCommandTest.java @@ -0,0 +1,73 @@ +// 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.testing.DatabaseHelper.createTld; +import static google.registry.testing.DatabaseHelper.newDomainBase; +import static google.registry.testing.DatabaseHelper.persistResource; +import static google.registry.util.DateTimeUtils.START_OF_TIME; +import static java.nio.charset.StandardCharsets.UTF_8; + +import google.registry.model.domain.launch.LaunchNotice; +import google.registry.testing.DualDatabaseTest; +import google.registry.testing.TestOfyAndSql; +import java.nio.file.Files; +import java.nio.file.Path; +import org.joda.time.DateTime; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; + +@DualDatabaseTest +class GenerateLordnCommandTest extends CommandTestCase { + + @TempDir Path outputDir; + + @BeforeEach + void beforeEach() { + fakeClock.setTo(DateTime.parse("2021-04-16T10:04:00.000Z")); + command.clock = fakeClock; + } + + @TestOfyAndSql + void testExample() throws Exception { + createTld("tld"); + persistResource(newDomainBase("sneezy.tld").asBuilder().setSmdId("smd1").build()); + persistResource(newDomainBase("wheezy.tld").asBuilder().setSmdId("smd2").build()); + persistResource( + newDomainBase("fleecey.tld") + .asBuilder() + .setLaunchNotice(LaunchNotice.create("smd3", "validator", START_OF_TIME, START_OF_TIME)) + .setSmdId("smd3") + .build()); + Path claimsCsv = outputDir.resolve("claims.csv"); + Path sunriseCsv = outputDir.resolve("sunrise.csv"); + runCommand("-t tld", "-c " + claimsCsv, "-s " + sunriseCsv); + assertThat(Files.readAllBytes(claimsCsv)) + .isEqualTo( + ("1,2021-04-16T10:04:00.000Z,1\n" + + "roid,domain-name,notice-id,registrar-id,registration-datetime,ack-datetime,application-datetime\n" + + "6-TLD,fleecey.tld,smd3,1,1970-01-01T00:00:00.000Z,1970-01-01T00:00:00.000Z\n") + .getBytes(UTF_8)); + assertThat(Files.readAllBytes(sunriseCsv)) + .isEqualTo( + ("1,2021-04-16T10:04:00.001Z,2\n" + + "roid,domain-name,SMD-id,registrar-id,registration-datetime," + + "application-datetime\n" + + "2-TLD,sneezy.tld,smd1,1,1970-01-01T00:00:00.000Z\n" + + "4-TLD,wheezy.tld,smd2,1,1970-01-01T00:00:00.000Z\n") + .getBytes(UTF_8)); + } +}