From 0ee78acba3afb29a8d88d50df71bd0c7d573571d Mon Sep 17 00:00:00 2001
From: Michael W Spalti
Date: Thu, 15 Aug 2019 13:18:54 -0700
Subject: [PATCH] [DS-4153] Added repository create method that takes a
parameter for the parent object.
Implementions are added to the collection and community repository classes.
---
.../app/rest/RestResourceController.java | 11 ++-
.../repository/CollectionRestRepository.java | 42 ++++-----
.../repository/CommunityRestRepository.java | 52 ++++++++----
.../rest/repository/DSpaceRestRepository.java | 67 ++++++++++++---
.../app/rest/CommunityRestRepositoryIT.java | 85 ++++++++++++++-----
5 files changed, 179 insertions(+), 78 deletions(-)
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
index 9863ed0ca2..02c7f4fe46 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java
@@ -51,6 +51,7 @@ import org.dspace.app.rest.repository.LinkRestRepository;
import org.dspace.app.rest.utils.RestRepositoryUtils;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
+import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -441,7 +442,15 @@ public class RestResourceController implements InitializingBean {
throws HttpRequestMethodNotSupportedException {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model);
- RestAddressableModel modelObject = repository.createAndReturn();
+
+ String parentCommunityString = request.getParameter("parent");
+ RestAddressableModel modelObject;
+ if (parentCommunityString != null) {
+ UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
+ modelObject = repository.createAndReturn(parentCommunityUuid);
+ } else {
+ modelObject = repository.createAndReturn();
+ }
if (modelObject == null) {
return ControllerUtils.toEmptyResponse(HttpStatus.CREATED);
}
diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
index cceb32b546..c09178eae6 100644
--- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
+++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java
@@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.CollectionConverter;
@@ -38,7 +37,6 @@ import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
-import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -169,8 +167,19 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository wrapResource(T model, String... rels);
+ /**
+ * Create and return a new instance. Data are usually retrieved from the thread bound http request
+ *
+ * @return the created REST object
+ */
+ public T createAndReturn() {
+ Context context = null;
+ try {
+ context = obtainContext();
+ T entity = thisRepository.createAndReturn(context);
+ context.commit();
+ return entity;
+ } catch (AuthorizeException e) {
+ throw new RESTAuthorizationException(e);
+ } catch (SQLException ex) {
+ throw new RuntimeException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create and return a new instance after adding to the parent. Data are usually retrieved from
+ * the thread bound http request.
+ *
+ * @param uuid the id of the parent object
+ * @return the created REST object
+ */
+ public T createAndReturn(UUID uuid) {
+ Context context = null;
+ try {
+ context = obtainContext();
+ T entity = thisRepository.createAndReturn(context, uuid);
+ context.commit();
+ return entity;
+ } catch (AuthorizeException e) {
+ throw new RESTAuthorizationException(e);
+ } catch (SQLException ex) {
+ throw new RuntimeException(ex.getMessage(), ex);
+ }
+ }
+
/**
* Create and return a new instance. Data is recovered from the thread bound HTTP request and the list
* of DSpaceObjects provided in the uri-list body
@@ -281,22 +322,22 @@ public abstract class DSpaceRestRepositorySome cool HTML code here
"))
- .put("dc.description.abstract",
- new MetadataValueRest("Sample top-level community created via the REST API"))
- .put("dc.description.tableofcontents",
- new MetadataValueRest("HTML News
"))
- .put("dc.rights",
- new MetadataValueRest("Custom Copyright Text"))
- .put("dc.title",
- new MetadataValueRest("Title Text")));
+ // Anonymous user tries to create a community.
+ // Should fail because user is not authenticated. Error 401.
+ getClient().perform(post("/api/core/communities")
+ .content(mapper.writeValueAsBytes(comm))
+ .param("parent", parentCommunity.getID().toString())
+ .contentType(contentType))
+ .andExpect(status().isUnauthorized());
- String authToken = getAuthToken(admin.getEmail(), password);
- // Capture the UUID of the created Community (see andDo() below)
+ // Non-admin Eperson tries to create a community.
+ // Should fail because user doesn't have permissions. Error 403.
+ String authToken = getAuthToken(eperson.getEmail(), password);
+ getClient(authToken).perform(post("/api/core/communities")
+ .content(mapper.writeValueAsBytes(comm))
+ .param("parent", parentCommunity.getID().toString())
+ .contentType(contentType))
+ .andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void createSubCommunityAuthorizedTest() throws Exception {
+ //We turn off the authorization system in order to create the structure as defined below
+ context.turnOffAuthorisationSystem();
+
+ // Create a parent community to POST a new sub-community to
+ parentCommunity = CommunityBuilder.createCommunity(context)
+ .withName("Parent Community")
+ .build();
+
+ // ADD authorization on parent community
+ context.setCurrentUser(eperson);
+ authorizeService.addPolicy(context, parentCommunity, Constants.ADD, eperson);
+
+ context.restoreAuthSystemState();
+
+ String authToken = getAuthToken(eperson.getEmail(), password);
+
+ ObjectMapper mapper = new ObjectMapper();
+ CommunityRest comm = new CommunityRest();
+ // We send a name but the created community should set this to the title
+ comm.setName("Test Sub-Level Community");
+
+ comm.setMetadata(new MetadataRest()
+ .put("dc.description",
+ new MetadataValueRest("Some cool HTML code here
"))
+ .put("dc.description.abstract",
+ new MetadataValueRest("Sample top-level community created via the REST API"))
+ .put("dc.description.tableofcontents",
+ new MetadataValueRest("HTML News
"))
+ .put("dc.rights",
+ new MetadataValueRest("Custom Copyright Text"))
+ .put("dc.title",
+ new MetadataValueRest("Title Text")));
+
+ // Capture the UUID of the created Community (see andDo() below)
AtomicReference idRef = new AtomicReference();
try {
getClient(authToken).perform(post("/api/core/communities")
- .content(mapper.writeValueAsBytes(comm))
- .param("parent", parentCommunity.getID().toString())
- .contentType(contentType))
+ .content(mapper.writeValueAsBytes(comm))
+ .param("parent", parentCommunity.getID().toString())
+ .contentType(contentType))
.andExpect(status().isCreated())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$", Matchers.allOf(
@@ -181,15 +222,15 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
hasJsonPath("$._links.self.href", not(empty())),
hasJsonPath("$.metadata", Matchers.allOf(
MetadataMatcher.matchMetadata("dc.description",
- "Some cool HTML code here
"),
+ "Some cool HTML code here
"),
MetadataMatcher.matchMetadata("dc.description.abstract",
- "Sample top-level community created via the REST API"),
+ "Sample top-level community created via the REST API"),
MetadataMatcher.matchMetadata("dc.description.tableofcontents",
- "HTML News
"),
+ "HTML News
"),
MetadataMatcher.matchMetadata("dc.rights",
- "Custom Copyright Text"),
+ "Custom Copyright Text"),
MetadataMatcher.matchMetadata("dc.title",
- "Title Text")
+ "Title Text")
)
)
)))