mirror of
https://github.com/google/nomulus.git
synced 2025-07-24 19:48:32 +02:00
Add an optional search term for ConsoleDomainListAction (#2225)
It's a case-insensitive query and it can appear anywhere (including TLDs)
This commit is contained in:
parent
2687181045
commit
c3eae7b76f
3 changed files with 87 additions and 17 deletions
|
@ -19,6 +19,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
|||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
|
@ -33,6 +34,7 @@ import google.registry.ui.server.registrar.JsonGetAction;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.TypedQuery;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Returns a (paginated) list of domains for a particular registrar. */
|
||||
|
@ -49,6 +51,8 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
private static final String DOMAIN_QUERY_TEMPLATE =
|
||||
"FROM Domain WHERE currentSponsorRegistrarId = :registrarId AND deletionTime >"
|
||||
+ " :deletedAfterTime AND creationTime <= :createdBeforeTime";
|
||||
private static final String SEARCH_TERM_QUERY = " AND LOWER(domainName) LIKE :searchTerm";
|
||||
private static final String ORDER_BY_STATEMENT = " ORDER BY creationTime DESC";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
|
@ -58,6 +62,7 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
private final int pageNumber;
|
||||
private final int resultsPerPage;
|
||||
private final Optional<Long> totalResults;
|
||||
private final Optional<String> searchTerm;
|
||||
|
||||
@Inject
|
||||
public ConsoleDomainListAction(
|
||||
|
@ -68,7 +73,8 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
@Parameter("checkpointTime") Optional<DateTime> checkpointTime,
|
||||
@Parameter("pageNumber") Optional<Integer> pageNumber,
|
||||
@Parameter("resultsPerPage") Optional<Integer> resultsPerPage,
|
||||
@Parameter("totalResults") Optional<Long> totalResults) {
|
||||
@Parameter("totalResults") Optional<Long> totalResults,
|
||||
@Parameter("searchTerm") Optional<String> searchTerm) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
|
@ -77,6 +83,7 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
this.pageNumber = pageNumber.orElse(0);
|
||||
this.resultsPerPage = resultsPerPage.orElse(DEFAULT_RESULTS_PER_PAGE);
|
||||
this.totalResults = totalResults;
|
||||
this.searchTerm = searchTerm;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,13 +117,13 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
long actualTotalResults =
|
||||
totalResults.orElseGet(
|
||||
() ->
|
||||
tm().query("SELECT COUNT(*) " + DOMAIN_QUERY_TEMPLATE, Long.class)
|
||||
createCountQuery()
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||
.setParameter("deletedAfterTime", checkpoint)
|
||||
.getSingleResult());
|
||||
List<Domain> domains =
|
||||
tm().query(DOMAIN_QUERY_TEMPLATE + " ORDER BY creationTime DESC", Domain.class)
|
||||
createDomainQuery()
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||
.setParameter("deletedAfterTime", checkpoint)
|
||||
|
@ -127,6 +134,26 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
|||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
/** Creates the query to get the total number of matching domains, interpolating as necessary. */
|
||||
private TypedQuery<Long> createCountQuery() {
|
||||
String queryString = "SELECT COUNT(*) " + DOMAIN_QUERY_TEMPLATE;
|
||||
if (searchTerm.isPresent() && !searchTerm.get().isEmpty()) {
|
||||
return tm().query(queryString + SEARCH_TERM_QUERY, Long.class)
|
||||
.setParameter("searchTerm", String.format("%%%s%%", Ascii.toLowerCase(searchTerm.get())));
|
||||
}
|
||||
return tm().query(queryString, Long.class);
|
||||
}
|
||||
|
||||
/** Creates the query to retrieve the matching domains themselves, interpolating as necessary. */
|
||||
private TypedQuery<Domain> createDomainQuery() {
|
||||
if (searchTerm.isPresent() && !searchTerm.get().isEmpty()) {
|
||||
return tm().query(
|
||||
DOMAIN_QUERY_TEMPLATE + SEARCH_TERM_QUERY + ORDER_BY_STATEMENT, Domain.class)
|
||||
.setParameter("searchTerm", String.format("%%%s%%", Ascii.toLowerCase(searchTerm.get())));
|
||||
}
|
||||
return tm().query(DOMAIN_QUERY_TEMPLATE + ORDER_BY_STATEMENT, Domain.class);
|
||||
}
|
||||
|
||||
private void writeBadRequest(String message) {
|
||||
response.setPayload(message);
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
|
|
|
@ -226,4 +226,10 @@ public final class RegistrarConsoleModule {
|
|||
public static Optional<Long> provideTotalResults(HttpServletRequest req) {
|
||||
return extractOptionalParameter(req, "totalResults").map(Long::valueOf);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("searchTerm")
|
||||
public static Optional<String> provideSearchTerm(HttpServletRequest req) {
|
||||
return extractOptionalParameter(req, "searchTerm");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
|||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
|
@ -90,7 +91,7 @@ public class ConsoleDomainListActionTest {
|
|||
@Test
|
||||
void testSuccess_pages() {
|
||||
// Two pages of results should go in reverse chronological order
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
|
@ -98,7 +99,7 @@ public class ConsoleDomainListActionTest {
|
|||
assertThat(result.totalResults).isEqualTo(10);
|
||||
|
||||
// Now do the second page
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L);
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L, null);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
|
@ -107,7 +108,7 @@ public class ConsoleDomainListActionTest {
|
|||
|
||||
@Test
|
||||
void testSuccess_partialPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
|
@ -116,7 +117,7 @@ public class ConsoleDomainListActionTest {
|
|||
|
||||
@Test
|
||||
void testSuccess_checkpointTime_createdBefore() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null, null);
|
||||
action.run();
|
||||
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
|
@ -127,7 +128,7 @@ public class ConsoleDomainListActionTest {
|
|||
persistActiveDomain("newdomain.tld", clock.nowUtc());
|
||||
|
||||
// Even though we persisted a new domain, the old checkpoint should return no more results
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 10, null);
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 10, null, null);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
|
@ -136,7 +137,7 @@ public class ConsoleDomainListActionTest {
|
|||
|
||||
@Test
|
||||
void testSuccess_checkpointTime_deletion() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
|
||||
|
@ -146,16 +147,50 @@ public class ConsoleDomainListActionTest {
|
|||
persistDomainAsDeleted(toDelete, clock.nowUtc());
|
||||
|
||||
// Second page should include the domain that is now deleted due to the checkpoint time
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, null);
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, null, null);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_searchTerm_oneMatch() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "0");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(Iterables.getOnlyElement(result.domains).getDomainName()).isEqualTo("0exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_searchTerm_returnsNone() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "deleted");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_searchTerm_caseInsensitive() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "eXiStS");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(5);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_searchTerm_tld() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "tld");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(5);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPartialSuccess_pastEnd() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
|
@ -163,13 +198,13 @@ public class ConsoleDomainListActionTest {
|
|||
|
||||
@Test
|
||||
void testFailure_invalidResultsPerPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
||||
|
||||
action = createAction("TheRegistrar", null, 0, 501, null);
|
||||
action = createAction("TheRegistrar", null, 0, 501, null, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
|
@ -178,14 +213,14 @@ public class ConsoleDomainListActionTest {
|
|||
|
||||
@Test
|
||||
void testFailure_invalidPageNumber() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null);
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Page number must be non-negative");
|
||||
}
|
||||
|
||||
private ConsoleDomainListAction createAction(String registrarId) {
|
||||
return createAction(registrarId, null, null, null, null);
|
||||
return createAction(registrarId, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private ConsoleDomainListAction createAction(
|
||||
|
@ -193,7 +228,8 @@ public class ConsoleDomainListActionTest {
|
|||
@Nullable DateTime checkpointTime,
|
||||
@Nullable Integer pageNumber,
|
||||
@Nullable Integer resultsPerPage,
|
||||
@Nullable Long totalResults) {
|
||||
@Nullable Long totalResults,
|
||||
@Nullable String searchTerm) {
|
||||
response = new FakeResponse();
|
||||
AuthResult authResult =
|
||||
AuthResult.createUser(
|
||||
|
@ -210,6 +246,7 @@ public class ConsoleDomainListActionTest {
|
|||
Optional.ofNullable(checkpointTime),
|
||||
Optional.ofNullable(pageNumber),
|
||||
Optional.ofNullable(resultsPerPage),
|
||||
Optional.ofNullable(totalResults));
|
||||
Optional.ofNullable(totalResults),
|
||||
Optional.ofNullable(searchTerm));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue