mirror of
https://github.com/google/nomulus.git
synced 2025-07-25 20:18:34 +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.api.client.http.HttpStatusCodes;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Ascii;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
import google.registry.model.CreateAutoTimestamp;
|
import google.registry.model.CreateAutoTimestamp;
|
||||||
|
@ -33,6 +34,7 @@ import google.registry.ui.server.registrar.JsonGetAction;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
/** Returns a (paginated) list of domains for a particular registrar. */
|
/** 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 =
|
private static final String DOMAIN_QUERY_TEMPLATE =
|
||||||
"FROM Domain WHERE currentSponsorRegistrarId = :registrarId AND deletionTime >"
|
"FROM Domain WHERE currentSponsorRegistrarId = :registrarId AND deletionTime >"
|
||||||
+ " :deletedAfterTime AND creationTime <= :createdBeforeTime";
|
+ " :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 AuthResult authResult;
|
||||||
private final Response response;
|
private final Response response;
|
||||||
|
@ -58,6 +62,7 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
||||||
private final int pageNumber;
|
private final int pageNumber;
|
||||||
private final int resultsPerPage;
|
private final int resultsPerPage;
|
||||||
private final Optional<Long> totalResults;
|
private final Optional<Long> totalResults;
|
||||||
|
private final Optional<String> searchTerm;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ConsoleDomainListAction(
|
public ConsoleDomainListAction(
|
||||||
|
@ -68,7 +73,8 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
||||||
@Parameter("checkpointTime") Optional<DateTime> checkpointTime,
|
@Parameter("checkpointTime") Optional<DateTime> checkpointTime,
|
||||||
@Parameter("pageNumber") Optional<Integer> pageNumber,
|
@Parameter("pageNumber") Optional<Integer> pageNumber,
|
||||||
@Parameter("resultsPerPage") Optional<Integer> resultsPerPage,
|
@Parameter("resultsPerPage") Optional<Integer> resultsPerPage,
|
||||||
@Parameter("totalResults") Optional<Long> totalResults) {
|
@Parameter("totalResults") Optional<Long> totalResults,
|
||||||
|
@Parameter("searchTerm") Optional<String> searchTerm) {
|
||||||
this.authResult = authResult;
|
this.authResult = authResult;
|
||||||
this.response = response;
|
this.response = response;
|
||||||
this.gson = gson;
|
this.gson = gson;
|
||||||
|
@ -77,6 +83,7 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
||||||
this.pageNumber = pageNumber.orElse(0);
|
this.pageNumber = pageNumber.orElse(0);
|
||||||
this.resultsPerPage = resultsPerPage.orElse(DEFAULT_RESULTS_PER_PAGE);
|
this.resultsPerPage = resultsPerPage.orElse(DEFAULT_RESULTS_PER_PAGE);
|
||||||
this.totalResults = totalResults;
|
this.totalResults = totalResults;
|
||||||
|
this.searchTerm = searchTerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,13 +117,13 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
||||||
long actualTotalResults =
|
long actualTotalResults =
|
||||||
totalResults.orElseGet(
|
totalResults.orElseGet(
|
||||||
() ->
|
() ->
|
||||||
tm().query("SELECT COUNT(*) " + DOMAIN_QUERY_TEMPLATE, Long.class)
|
createCountQuery()
|
||||||
.setParameter("registrarId", registrarId)
|
.setParameter("registrarId", registrarId)
|
||||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||||
.setParameter("deletedAfterTime", checkpoint)
|
.setParameter("deletedAfterTime", checkpoint)
|
||||||
.getSingleResult());
|
.getSingleResult());
|
||||||
List<Domain> domains =
|
List<Domain> domains =
|
||||||
tm().query(DOMAIN_QUERY_TEMPLATE + " ORDER BY creationTime DESC", Domain.class)
|
createDomainQuery()
|
||||||
.setParameter("registrarId", registrarId)
|
.setParameter("registrarId", registrarId)
|
||||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||||
.setParameter("deletedAfterTime", checkpoint)
|
.setParameter("deletedAfterTime", checkpoint)
|
||||||
|
@ -127,6 +134,26 @@ public class ConsoleDomainListAction implements JsonGetAction {
|
||||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
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) {
|
private void writeBadRequest(String message) {
|
||||||
response.setPayload(message);
|
response.setPayload(message);
|
||||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||||
|
|
|
@ -226,4 +226,10 @@ public final class RegistrarConsoleModule {
|
||||||
public static Optional<Long> provideTotalResults(HttpServletRequest req) {
|
public static Optional<Long> provideTotalResults(HttpServletRequest req) {
|
||||||
return extractOptionalParameter(req, "totalResults").map(Long::valueOf);
|
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 static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||||
|
|
||||||
import com.google.api.client.http.HttpStatusCodes;
|
import com.google.api.client.http.HttpStatusCodes;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import google.registry.model.EppResourceUtils;
|
import google.registry.model.EppResourceUtils;
|
||||||
import google.registry.model.console.GlobalRole;
|
import google.registry.model.console.GlobalRole;
|
||||||
|
@ -90,7 +91,7 @@ public class ConsoleDomainListActionTest {
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_pages() {
|
void testSuccess_pages() {
|
||||||
// Two pages of results should go in reverse chronological order
|
// 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();
|
action.run();
|
||||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||||
|
@ -98,7 +99,7 @@ public class ConsoleDomainListActionTest {
|
||||||
assertThat(result.totalResults).isEqualTo(10);
|
assertThat(result.totalResults).isEqualTo(10);
|
||||||
|
|
||||||
// Now do the second page
|
// Now do the second page
|
||||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L);
|
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L, null);
|
||||||
action.run();
|
action.run();
|
||||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||||
|
@ -107,7 +108,7 @@ public class ConsoleDomainListActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_partialPage() {
|
void testSuccess_partialPage() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||||
|
@ -116,7 +117,7 @@ public class ConsoleDomainListActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_checkpointTime_createdBefore() {
|
void testSuccess_checkpointTime_createdBefore() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
|
|
||||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
|
@ -127,7 +128,7 @@ public class ConsoleDomainListActionTest {
|
||||||
persistActiveDomain("newdomain.tld", clock.nowUtc());
|
persistActiveDomain("newdomain.tld", clock.nowUtc());
|
||||||
|
|
||||||
// Even though we persisted a new domain, the old checkpoint should return no more results
|
// 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();
|
action.run();
|
||||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains).isEmpty();
|
assertThat(result.domains).isEmpty();
|
||||||
|
@ -136,7 +137,7 @@ public class ConsoleDomainListActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_checkpointTime_deletion() {
|
void testSuccess_checkpointTime_deletion() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
|
|
||||||
|
@ -146,16 +147,50 @@ public class ConsoleDomainListActionTest {
|
||||||
persistDomainAsDeleted(toDelete, clock.nowUtc());
|
persistDomainAsDeleted(toDelete, clock.nowUtc());
|
||||||
|
|
||||||
// Second page should include the domain that is now deleted due to the checkpoint time
|
// 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();
|
action.run();
|
||||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
.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
|
@Test
|
||||||
void testPartialSuccess_pastEnd() {
|
void testPartialSuccess_pastEnd() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||||
assertThat(result.domains).isEmpty();
|
assertThat(result.domains).isEmpty();
|
||||||
|
@ -163,13 +198,13 @@ public class ConsoleDomainListActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_invalidResultsPerPage() {
|
void testFailure_invalidResultsPerPage() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||||
assertThat(response.getPayload())
|
assertThat(response.getPayload())
|
||||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
.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();
|
action.run();
|
||||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||||
assertThat(response.getPayload())
|
assertThat(response.getPayload())
|
||||||
|
@ -178,14 +213,14 @@ public class ConsoleDomainListActionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFailure_invalidPageNumber() {
|
void testFailure_invalidPageNumber() {
|
||||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null);
|
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null, null);
|
||||||
action.run();
|
action.run();
|
||||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||||
assertThat(response.getPayload()).isEqualTo("Page number must be non-negative");
|
assertThat(response.getPayload()).isEqualTo("Page number must be non-negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConsoleDomainListAction createAction(String registrarId) {
|
private ConsoleDomainListAction createAction(String registrarId) {
|
||||||
return createAction(registrarId, null, null, null, null);
|
return createAction(registrarId, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConsoleDomainListAction createAction(
|
private ConsoleDomainListAction createAction(
|
||||||
|
@ -193,7 +228,8 @@ public class ConsoleDomainListActionTest {
|
||||||
@Nullable DateTime checkpointTime,
|
@Nullable DateTime checkpointTime,
|
||||||
@Nullable Integer pageNumber,
|
@Nullable Integer pageNumber,
|
||||||
@Nullable Integer resultsPerPage,
|
@Nullable Integer resultsPerPage,
|
||||||
@Nullable Long totalResults) {
|
@Nullable Long totalResults,
|
||||||
|
@Nullable String searchTerm) {
|
||||||
response = new FakeResponse();
|
response = new FakeResponse();
|
||||||
AuthResult authResult =
|
AuthResult authResult =
|
||||||
AuthResult.createUser(
|
AuthResult.createUser(
|
||||||
|
@ -210,6 +246,7 @@ public class ConsoleDomainListActionTest {
|
||||||
Optional.ofNullable(checkpointTime),
|
Optional.ofNullable(checkpointTime),
|
||||||
Optional.ofNullable(pageNumber),
|
Optional.ofNullable(pageNumber),
|
||||||
Optional.ofNullable(resultsPerPage),
|
Optional.ofNullable(resultsPerPage),
|
||||||
Optional.ofNullable(totalResults));
|
Optional.ofNullable(totalResults),
|
||||||
|
Optional.ofNullable(searchTerm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue