diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java index 9e5c339455..a4b8f64e91 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java @@ -725,8 +725,16 @@ public class RestResourceController implements InitializingBean { if (Page.class.isAssignableFrom(linkMethod.getReturnType())) { Page pageResult = (Page) linkMethod .invoke(linkRepository, request, uuid, page, projection); - Link link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath) - .withSelfRel(); + + Link link = null; + String querystring = request.getQueryString(); + if (querystring != null && querystring.length() > 0) { + link = linkTo(this.getClass(), apiCategory, model).slash(uuid) + .slash(subpath + '?' + querystring).withSelfRel(); + } else { + link = linkTo(this.getClass(), apiCategory, model).slash(uuid).withSelfRel(); + } + Page halResources = pageResult.map(linkRepository::wrapResource); halResources.forEach(linkService::addLinks); diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseEntryLinkRepository.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseEntryLinkRepository.java index 52fe6a894b..998c5eb8bd 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseEntryLinkRepository.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseEntryLinkRepository.java @@ -61,8 +61,11 @@ public class BrowseEntryLinkRepository extends AbstractDSpaceRestRepository // FIXME this should be bind automatically and available as method // argument String scope = null; + String startsWith = null; + if (request != null) { scope = request.getParameter("scope"); + startsWith = request.getParameter("startsWith"); } @@ -103,7 +106,7 @@ public class BrowseEntryLinkRepository extends AbstractDSpaceRestRepository // bs.setJumpToItem(focus); // bs.setJumpToValue(valueFocus); // bs.setJumpToValueLang(valueFocusLang); - // bs.setStartsWith(startsWith); + bs.setStartsWith(startsWith); if (pageable != null) { bs.setOffset(pageable.getOffset()); bs.setResultsPerPage(pageable.getPageSize()); diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseItemLinkRepository.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseItemLinkRepository.java index 348e24152d..243100983e 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseItemLinkRepository.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/repository/BrowseItemLinkRepository.java @@ -61,11 +61,13 @@ public class BrowseItemLinkRepository extends AbstractDSpaceRestRepository String scope = null; String filterValue = null; String filterAuthority = null; + String startsWith = null; if (request != null) { scope = request.getParameter("scope"); filterValue = request.getParameter("filterValue"); filterAuthority = request.getParameter("filterAuthority"); + startsWith = request.getParameter("startsWith"); } Context context = obtainContext(); BrowseEngine be = new BrowseEngine(context); @@ -128,7 +130,7 @@ public class BrowseItemLinkRepository extends AbstractDSpaceRestRepository // bs.setJumpToItem(focus); // bs.setJumpToValue(valueFocus); // bs.setJumpToValueLang(valueFocusLang); - // bs.setStartsWith(startsWith); + bs.setStartsWith(startsWith); if (pageable != null) { bs.setOffset(pageable.getOffset()); bs.setResultsPerPage(pageable.getPageSize()); diff --git a/dspace-spring-rest/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java b/dspace-spring-rest/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java index 0365910570..beacc1b838 100644 --- a/dspace-spring-rest/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java +++ b/dspace-spring-rest/src/test/java/org/dspace/app/rest/BrowsesResourceControllerIT.java @@ -9,6 +9,7 @@ package org.dspace.app.rest; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -498,4 +499,388 @@ public class BrowsesResourceControllerIT extends AbstractControllerIntegrationTe "Item 7", "2016-01-12") ))); } + + @Test + public void testBrowseByEntriesStartsWith() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); + + //2. 7 public items that are readable by Anonymous + Item item1 = ItemBuilder.createItem(context, col1) + .withTitle("Alan Turing") + .withAuthor("Turing, Alan Mathison") + .withIssueDate("1912-06-23") + .withSubject("Computing") + .build(); + + Item item2 = ItemBuilder.createItem(context, col1) + .withTitle("Blade Runner") + .withAuthor("Scott, Ridley") + .withIssueDate("1982-06-25") + .withSubject("Science Fiction") + .build(); + + Item item3 = ItemBuilder.createItem(context, col1) + .withTitle("Python") + .withAuthor("Van Rossum, Guido") + .withIssueDate("1990") + .withSubject("Computing") + .build(); + + Item item4 = ItemBuilder.createItem(context, col2) + .withTitle("Java") + .withAuthor("Gosling, James") + .withIssueDate("1995-05-23") + .withSubject("Computing") + .build(); + + Item item5 = ItemBuilder.createItem(context, col2) + .withTitle("Zeta Reticuli") + .withAuthor("Universe") + .withIssueDate("2018-01-01") + .withSubject("Astronomy") + .build(); + + Item item6 = ItemBuilder.createItem(context, col2) + .withTitle("Moon") + .withAuthor("Universe") + .withIssueDate("2018-01-02") + .withSubject("Astronomy") + .build(); + + Item item7 = ItemBuilder.createItem(context, col2) + .withTitle("T-800") + .withAuthor("Cameron, James") + .withIssueDate("2029") + .withSubject("Science Fiction") + .build(); + + // ---- BROWSES BY ENTRIES ---- + + //** WHEN ** + //An anonymous user browses the entries in the Browse by Author endpoint + //with startsWith set to U + getClient().perform(get("/api/discover/browses/author/entries?startsWith=U") + .param("size", "2")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect only the "Universe" entry to be present + .andExpect(jsonPath("$.page.totalElements", is(1))) + //As entry browsing works as a filter, we expect to be on page 0 + .andExpect(jsonPath("$.page.number", is(0))) + + //Verify that the index filters to the "Universe" entries and Counts 2 Items. + .andExpect(jsonPath("$._embedded.browseEntries", + contains(BrowseEntryResourceMatcher.matchBrowseEntry("Universe", 2) + ))) + //Verify startsWith parameter is included in the links + .andExpect(jsonPath("$._links.self.href", containsString("?startsWith=U"))); + + //** WHEN ** + //An anonymous user browses the entries in the Browse by Author endpoint + //with startsWith set to T and scope set to Col 1 + getClient().perform(get("/api/discover/browses/author/entries?startsWith=T") + .param("scope", col1.getID().toString())) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect only the entry "Turing, Alan Mathison" to be present + .andExpect(jsonPath("$.page.totalElements", is(1))) + //As entry browsing works as a filter, we expect to be on page 0 + .andExpect(jsonPath("$.page.number", is(0))) + + //Verify that the index filters to the "Turing, Alan'" items. + .andExpect(jsonPath("$._embedded.browseEntries", + contains(BrowseEntryResourceMatcher.matchBrowseEntry("Turing, Alan Mathison", 1) + ))) + //Verify that the startsWith paramater is included in the links + .andExpect(jsonPath("$._links.self.href", containsString("?startsWith=T"))); + + //** WHEN ** + //An anonymous user browses the entries in the Browse by Subject endpoint + //with startsWith set to C + getClient().perform(get("/api/discover/browses/subject/entries?startsWith=C")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect only the entry "Computing" to be present + .andExpect(jsonPath("$.page.totalElements", is(1))) + //As entry browsing works as a filter, we expect to be on page 0 + .andExpect(jsonPath("$.page.number", is(0))) + + //Verify that the index filters to the "Computing'" items. + .andExpect(jsonPath("$._embedded.browseEntries", + contains(BrowseEntryResourceMatcher.matchBrowseEntry("Computing", 3) + ))) + //Verify that the startsWith paramater is included in the links + .andExpect(jsonPath("$._links.self.href", containsString("?startsWith=C"))); + + }; + + @Test + public void testBrowseByItemsStartsWith() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); + + //2. 7 public items that are readable by Anonymous + Item item1 = ItemBuilder.createItem(context, col1) + .withTitle("Alan Turing") + .withAuthor("Turing, Alan Mathison") + .withIssueDate("1912-06-23") + .withSubject("Computing") + .build(); + + Item item2 = ItemBuilder.createItem(context, col1) + .withTitle("Blade Runner") + .withAuthor("Scott, Ridley") + .withIssueDate("1982-06-25") + .withSubject("Science Fiction") + .build(); + + Item item3 = ItemBuilder.createItem(context, col1) + .withTitle("Python") + .withAuthor("Van Rossum, Guido") + .withIssueDate("1990") + .withSubject("Computing") + .build(); + + Item item4 = ItemBuilder.createItem(context, col2) + .withTitle("Java") + .withAuthor("Gosling, James") + .withIssueDate("1995-05-23") + .withSubject("Computing") + .build(); + + Item item5 = ItemBuilder.createItem(context, col2) + .withTitle("Zeta Reticuli") + .withAuthor("Universe") + .withIssueDate("2018-01-01") + .withSubject("Astronomy") + .build(); + + Item item6 = ItemBuilder.createItem(context, col2) + .withTitle("Moon") + .withAuthor("Universe") + .withIssueDate("2018-01-02") + .withSubject("Astronomy") + .build(); + + Item item7 = ItemBuilder.createItem(context, col2) + .withTitle("T-800") + .withAuthor("Cameron, James") + .withIssueDate("2029") + .withSubject("Science Fiction") + .build(); + // ---- BROWSES BY ITEM ---- + + //** WHEN ** + //An anonymous user browses the items in the Browse by date issued endpoint + //with startsWith set to 1990 + getClient().perform(get("/api/discover/browses/dateissued/items?startsWith=1990") + .param("size", "2")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect the totalElements to be the 7 items present in the repository + .andExpect(jsonPath("$.page.totalElements", is(7))) + //We expect to jump to page 1 of the index + .andExpect(jsonPath("$.page.number", is(1))) + .andExpect(jsonPath("$.page.size", is(2))) + .andExpect(jsonPath("$._links.first.href", containsString("startsWith=1990"))) + + //Verify that the index jumps to the "Python" item. + .andExpect(jsonPath("$._embedded.items", + contains(ItemMatcher.matchItemWithTitleAndDateIssued(item3, + "Python", "1990"), + ItemMatcher.matchItemWithTitleAndDateIssued(item4, + "Java", "1995-05-23") + ))); + //** WHEN ** + //An anonymous user browses the items in the Browse by Title endpoint + //with startsWith set to T + getClient().perform(get("/api/discover/browses/title/items?startsWith=T") + .param("size", "2")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect the totalElements to be the 7 items present in the repository + .andExpect(jsonPath("$.page.totalElements", is(7))) + //We expect to jump to page 2 in the index + .andExpect(jsonPath("$.page.number", is(2))) + .andExpect(jsonPath("$._links.first.href", containsString("startsWith=T"))) + + //Verify that the index jumps to the "T-800" item. + .andExpect(jsonPath("$._embedded.items", + contains(ItemMatcher.matchItemWithTitleAndDateIssued(item7, + "T-800", "2029"), + ItemMatcher.matchItemWithTitleAndDateIssued(item5, + "Zeta Reticuli", + "2018-01-01") + ))); + + //** WHEN ** + //An anonymous user browses the items in the Browse by Title endpoint + //with startsWith set to Blade and scope set to Col 1 + getClient().perform(get("/api/discover/browses/title/items?startsWith=Blade") + .param("scope", col1.getID().toString()) + .param("size", "2")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect the totalElements to be the 3 items present in the collection + .andExpect(jsonPath("$.page.totalElements", is(3))) + //As this is is a small collection, we expect to go-to page 0 + .andExpect(jsonPath("$.page.number", is(0))) + .andExpect(jsonPath("$._links.first.href", containsString("startsWith=Blade"))) + + //Verify that the index jumps to the "Blade Runner" item. + .andExpect(jsonPath("$._embedded.items", + contains(ItemMatcher.matchItemWithTitleAndDateIssued(item2, + "Blade Runner", + "1982-06-25"), + ItemMatcher.matchItemWithTitleAndDateIssued(item3, + "Python", "1990") + ))); + } + + @Test + public void testBrowseByStartsWithAndPage() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community with sub-community and two collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); + + //2. 7 public items that are readable by Anonymous + Item item1 = ItemBuilder.createItem(context, col1) + .withTitle("Alan Turing") + .withAuthor("Turing, Alan Mathison") + .withIssueDate("1912-06-23") + .withSubject("Computing") + .build(); + + Item item2 = ItemBuilder.createItem(context, col1) + .withTitle("Blade Runner") + .withAuthor("Scott, Ridley") + .withIssueDate("1982-06-25") + .withSubject("Science Fiction") + .build(); + + Item item3 = ItemBuilder.createItem(context, col2) + .withTitle("Java") + .withAuthor("Gosling, James") + .withIssueDate("1995-05-23") + .withSubject("Computing") + .build(); + + Item item4 = ItemBuilder.createItem(context, col2) + .withTitle("Moon") + .withAuthor("Universe") + .withIssueDate("2018-01-02") + .withSubject("Astronomy") + .build(); + + Item item5 = ItemBuilder.createItem(context, col1) + .withTitle("Python") + .withAuthor("Van Rossum, Guido") + .withIssueDate("1990") + .withSubject("Computing") + .build(); + + Item item6 = ItemBuilder.createItem(context, col2) + .withTitle("T-800") + .withAuthor("Cameron, James") + .withIssueDate("2029") + .withSubject("Science Fiction") + .build(); + + Item item7 = ItemBuilder.createItem(context, col2) + .withTitle("Zeta Reticuli") + .withAuthor("Universe") + .withIssueDate("2018-01-01") + .withSubject("Astronomy") + .build(); + + // ---- BROWSES BY ITEM ---- + + //** WHEN ** + //An anonymous user browses the items in the Browse by date issued endpoint + //with startsWith set to 1990 and Page to 3 + getClient().perform(get("/api/discover/browses/dateissued/items?startsWith=1990") + .param("size", "2").param("page", "2")) + + //** THEN ** + //The status has to be 200 OK + .andExpect(status().isOk()) + //We expect the content type to be "application/hal+json;charset=UTF-8" + .andExpect(content().contentType(contentType)) + + //We expect the totalElements to be the 7 items present in the repository + .andExpect(jsonPath("$.page.totalElements", is(7))) + //We expect to jump to page 1 of the index + .andExpect(jsonPath("$.page.number", is(2))) + .andExpect(jsonPath("$.page.size", is(2))) + .andExpect(jsonPath("$._links.first.href", containsString("startsWith=1990"))) + + //Verify that the index jumps to the "Zeta Reticuli" item. + .andExpect(jsonPath("$._embedded.items", + contains(ItemMatcher.matchItemWithTitleAndDateIssued(item7, + "Zeta Reticuli", "2018-01-01"), + ItemMatcher.matchItemWithTitleAndDateIssued(item4, + "Moon", "2018-01-02") + ))); + } + } \ No newline at end of file