[DS-4153] Added repository create method that takes a parameter for the parent object.

Implementions are added to the collection and community repository classes.
This commit is contained in:
Michael W Spalti
2019-08-15 13:18:54 -07:00
parent a4b4045d2e
commit 0ee78acba3
5 changed files with 179 additions and 78 deletions

View File

@@ -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<RestAddressableModel, ID> 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);
}

View File

@@ -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<Collect
}
@Override
@PreAuthorize("hasAuthority('ADMIN')")
protected CollectionRest createAndReturn(Context context) throws AuthorizeException {
throw new DSpaceBadRequestException("Missing parent parameter.");
}
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'ADD')")
protected CollectionRest createAndReturn(Context context, UUID id) throws AuthorizeException {
if (id == null) {
throw new DSpaceBadRequestException("The given parent parameter was invalid: "
+ id);
}
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper();
CollectionRest collectionRest;
@@ -180,36 +189,19 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
}
Collection collection;
String parentCommunityString = req.getParameter("parent");
try {
Community parent = null;
if (StringUtils.isNotBlank(parentCommunityString)) {
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
if (parentCommunityUuid == null) {
throw new DSpaceBadRequestException("The given parent was invalid: "
+ parentCommunityString);
}
parent = communityService.find(context, parentCommunityUuid);
if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
+ parentCommunityUuid + " not found");
}
} else {
throw new DSpaceBadRequestException("The parent parameter cannot be left empty," +
"collections require a parent community.");
Community parent = communityService.find(context, id);
if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
+ id + " not found");
}
collection = cs.create(context, parent);
cs.update(context, collection);
metadataConverter.setMetadata(context, collection, collectionRest.getMetadata());
} catch (SQLException e) {
throw new RuntimeException("Unable to create new Collection under parent Community " +
parentCommunityString, e);
id, e);
}
return converter.convert(collection);
}

View File

@@ -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.CommunityConverter;
@@ -34,7 +33,6 @@ import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Community;
import org.dspace.content.service.CommunityService;
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;
@@ -83,25 +81,45 @@ public class CommunityRestRepository extends DSpaceObjectRestRepository<Communit
}
Community community;
try {
Community parent = null;
String parentCommunityString = req.getParameter("parent");
if (StringUtils.isNotBlank(parentCommunityString)) {
// top-level community
community = cs.create(null, context);
cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString);
if (parentCommunityUuid == null) {
throw new DSpaceBadRequestException("The given parent parameter was invalid: "
+ parentCommunityString);
}
return dsoConverter.convert(community);
}
parent = cs.find(context, parentCommunityUuid);
if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
+ parentCommunityUuid + " not found");
}
@Override
@PreAuthorize("hasPermission(#id, 'COMMUNITY', 'ADD')")
protected CommunityRest createAndReturn(Context context, UUID id) throws AuthorizeException {
if (id == null) {
throw new DSpaceBadRequestException("The given parent parameter was invalid: "
+ id);
}
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ObjectMapper mapper = new ObjectMapper();
CommunityRest communityRest;
try {
ServletInputStream input = req.getInputStream();
communityRest = mapper.readValue(input, CommunityRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body: " + e1.toString());
}
Community community;
try {
Community parent = cs.find(context, id);
if (parent == null) {
throw new UnprocessableEntityException("Parent community for id: "
+ id + " not found");
}
// sub-community
community = cs.create(parent, context);
cs.update(context, community);
metadataConverter.setMetadata(context, community, communityRest.getMetadata());

View File

@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
@@ -259,6 +260,46 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
*/
public abstract DSpaceResource<T> 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 DSpaceRestRepository<T extends RestAddressableModel, ID ex
}
/**
* Create and return a new instance. Data are usually retrieved from the thread bound http request
* Method to implement to support the creation of a new instance. Usually require to retrieve the http request from
* the thread bound attribute
*
* @param context
* the dspace context
* @param uuid
* The uuid of the parent object retrieved from the query param.
* @return the created REST object
* @throws AuthorizeException
* @throws SQLException
* @throws RepositoryMethodNotImplementedException
* returned by the default implementation when the operation is not supported for the entity
*/
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);
}
protected T createAndReturn(Context context, UUID uuid)
throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
}
/**

View File

@@ -132,7 +132,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
}
@Test
public void createWithParentTest() throws Exception {
public void createSubCommunityUnAuthorizedTest() throws Exception {
//We turn off the authorization system in order to create the structure as defined below
context.turnOffAuthorisationSystem();
@@ -140,6 +140,7 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
context.restoreAuthSystemState();
ObjectMapper mapper = new ObjectMapper();
@@ -147,26 +148,66 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest
// 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("<p>Some cool HTML code here</p>"))
.put("dc.description.abstract",
new MetadataValueRest("Sample top-level community created via the REST API"))
.put("dc.description.tableofcontents",
new MetadataValueRest("<p>HTML News</p>"))
.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("<p>Some cool HTML code here</p>"))
.put("dc.description.abstract",
new MetadataValueRest("Sample top-level community created via the REST API"))
.put("dc.description.tableofcontents",
new MetadataValueRest("<p>HTML News</p>"))
.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<UUID> idRef = new AtomicReference<UUID>();
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",
"<p>Some cool HTML code here</p>"),
"<p>Some cool HTML code here</p>"),
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",
"<p>HTML News</p>"),
"<p>HTML News</p>"),
MetadataMatcher.matchMetadata("dc.rights",
"Custom Copyright Text"),
"Custom Copyright Text"),
MetadataMatcher.matchMetadata("dc.title",
"Title Text")
"Title Text")
)
)
)))