Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.tld.Tlds.getTldsOfType;
import static google.registry.tools.server.ListDomainsAction.MAX_DOMAINS;
import static google.registry.util.CollectionUtils.isNullOrEmpty;

import com.beust.jcommander.Parameter;
Expand All @@ -37,8 +38,11 @@ final class ListDomainsCommand extends ListObjectsCommand {

@Parameter(
names = {"-n", "--limit"},
description = "Max number of domains to list, most recent first; defaults to no limit."
)
description =
"Max number of domains to list, most recent first; defaults to all matching domains."
+ " The server lists at most "
+ MAX_DOMAINS
+ " domains per request and returns an error if more match.")
private int maxDomains = Integer.MAX_VALUE;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import static google.registry.request.RequestParameters.PARAM_TLDS;
import static java.lang.Math.min;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResourceUtils;
Expand All @@ -42,6 +44,9 @@ public final class ListDomainsAction extends ListObjectsAction<Domain> {

public static final String PATH = "/_dr/admin/list/domains";

/** The maximum number of domains a request can list, since they are all held in memory. */
public static final int MAX_DOMAINS = 10_000;

@Inject
@Parameter(PARAM_TLDS)
ImmutableSet<String> tlds;
Expand All @@ -52,6 +57,9 @@ public final class ListDomainsAction extends ListObjectsAction<Domain> {

@Inject Clock clock;

/** The maximum number of domains to list, which is only overridden in tests. */
@VisibleForTesting int maxDomains = MAX_DOMAINS;

@Inject
ListDomainsAction() {}

Expand All @@ -64,19 +72,28 @@ public ImmutableSet<String> getPrimaryKeyFields() {
public ImmutableSet<Domain> loadObjects() {
checkArgument(!tlds.isEmpty(), "Must specify TLDs to query");
assertTldsExist(tlds);
ImmutableList<Domain> domains = loadDomains();
// Load at most one domain more than the maximum, which tells us whether the request would have
// exceeded it without loading the full (and potentially very large) result set.
ImmutableList<Domain> domains = loadDomains(min(limit, maxDomains + 1));
checkArgument(
domains.size() <= maxDomains,
"Found more than %s matching domains, which is the most that can be listed in one request."
+ " Specify --limit=%s or less to list only the most recently created domains, or"
+ " export the full list directly from the database.",
maxDomains,
maxDomains);
return ImmutableSet.copyOf(domains.reverse());
}

private ImmutableList<Domain> loadDomains() {
private ImmutableList<Domain> loadDomains(int maxResults) {
return tm().transact(
() ->
tm().query(
"FROM Domain WHERE tld IN (:tlds) AND deletionTime > "
+ "current_timestamp() ORDER BY creationTime DESC",
Domain.class)
.setParameter("tlds", tlds)
.setMaxResults(limit)
.setMaxResults(maxResults)
.getResultStream()
.map(EppResourceUtils.transformAtTime(tm().getTxTime()))
.collect(toImmutableList()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,69 @@ void testRun_limitFiltersOutOldestDomains() {
"^example3.bar$",
"^example4.foo$");
}

@Test
void testRun_moreThanMaxDomainsWithoutLimit_returnsError() {
action.tlds = ImmutableSet.of("foo");
action.maxDomains = 2;
persistActiveDomain("example1.foo", Instant.parse("2017-01-01T00:00:00Z"));
persistActiveDomain("example2.foo", Instant.parse("2017-02-01T00:00:00Z"));
persistActiveDomain("example3.foo", Instant.parse("2017-03-01T00:00:00Z"));
testRunError(
action,
Optional.empty(),
Optional.empty(),
Optional.empty(),
"^Found more than 2 matching domains, which is the most that can be listed in one"
+ " request\\. Specify --limit=2 or less to list only the most recently created"
+ " domains, or export the full list directly from the database\\.$");
}

@Test
void testRun_exactlyMaxDomainsWithoutLimit_listsAll() {
action.tlds = ImmutableSet.of("foo");
action.maxDomains = 2;
persistActiveDomain("example1.foo", Instant.parse("2017-01-01T00:00:00Z"));
persistActiveDomain("example2.foo", Instant.parse("2017-02-01T00:00:00Z"));
testRunSuccess(
action,
Optional.empty(),
Optional.empty(),
Optional.empty(),
"^example1.foo$",
"^example2.foo$");
}

@Test
void testRun_limitWithinMax_listsMostRecentEvenIfMoreMatch() {
action.tlds = ImmutableSet.of("foo");
action.maxDomains = 2;
action.limit = 2;
persistActiveDomain("example1.foo", Instant.parse("2017-01-01T00:00:00Z"));
persistActiveDomain("example2.foo", Instant.parse("2017-02-01T00:00:00Z"));
persistActiveDomain("example3.foo", Instant.parse("2017-03-01T00:00:00Z"));
testRunSuccess(
action,
Optional.empty(),
Optional.empty(),
Optional.empty(),
"^example2.foo$",
"^example3.foo$");
}

@Test
void testRun_limitAboveMax_returnsErrorIfMoreMatch() {
action.tlds = ImmutableSet.of("foo");
action.maxDomains = 2;
action.limit = 3;
persistActiveDomain("example1.foo", Instant.parse("2017-01-01T00:00:00Z"));
persistActiveDomain("example2.foo", Instant.parse("2017-02-01T00:00:00Z"));
persistActiveDomain("example3.foo", Instant.parse("2017-03-01T00:00:00Z"));
testRunError(
action,
Optional.empty(),
Optional.empty(),
Optional.empty(),
"^Found more than 2 matching domains");
}
}
Loading