65257: create and read for collection template items

This commit is contained in:
Peter Nijs
2019-10-09 13:59:56 +02:00
parent c7d3c3e18c
commit f20687ad36
3 changed files with 324 additions and 16 deletions

View File

@@ -0,0 +1,74 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.app.rest.repository.CollectionRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/" + CollectionRest.CATEGORY + "/" + CollectionRest.PLURAL_NAME)
public class CollectionRestController {
/**
* Regular expression in the request mapping to accept UUID as identifier
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID =
"{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}";
@Autowired
protected Utils utils;
@Autowired
private CollectionRestRepository collectionRestRepository;
@PreAuthorize("hasAuthority('AUTHENTICATED')")
@RequestMapping(method = RequestMethod.POST,
value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/itemtemplate")
public ResponseEntity<ResourceSupport> createTemplateItem(HttpServletRequest request, @PathVariable UUID uuid)
throws SQLException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
ItemRest templateItem = collectionRestRepository.createTemplateItem(context, uuid);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null,
new ItemResource(templateItem, utils));
}
@PreAuthorize("hasAuthority('AUTHENTICATED')")
@RequestMapping(method = RequestMethod.GET,
value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/itemtemplate")
public ItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid)
throws SQLException {
Context context = ContextUtil.obtainContext(request);
ItemRest templateItem = collectionRestRepository.getTemplateItem(context, uuid);
return new ItemResource(templateItem, utils);
}
}

View File

@@ -17,11 +17,11 @@ import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.Parameter; import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.BitstreamConverter; import org.dspace.app.rest.converter.BitstreamConverter;
import org.dspace.app.rest.converter.CollectionConverter; import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.converter.MetadataConverter; import org.dspace.app.rest.converter.MetadataConverter;
import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
@@ -29,6 +29,7 @@ import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.CollectionRest; import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.CommunityRest; import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.CollectionResource; import org.dspace.app.rest.model.hateoas.CollectionResource;
import org.dspace.app.rest.model.patch.Patch; import org.dspace.app.rest.model.patch.Patch;
import org.dspace.app.rest.repository.patch.DSpaceObjectPatch; import org.dspace.app.rest.repository.patch.DSpaceObjectPatch;
@@ -38,8 +39,10 @@ import org.dspace.content.Bitstream;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Community; import org.dspace.content.Community;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
import org.dspace.content.Item;
import org.dspace.content.service.CollectionService; import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService; import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -60,9 +63,6 @@ import org.springframework.web.multipart.MultipartFile;
@Component(CollectionRest.CATEGORY + "." + CollectionRest.NAME) @Component(CollectionRest.CATEGORY + "." + CollectionRest.NAME)
public class CollectionRestRepository extends DSpaceObjectRestRepository<Collection, CollectionRest> { public class CollectionRestRepository extends DSpaceObjectRestRepository<Collection, CollectionRest> {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(CollectionRestRepository.class);
@Autowired @Autowired
CommunityService communityService; CommunityService communityService;
@@ -84,6 +84,16 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
@Autowired @Autowired
private BitstreamService bitstreamService; private BitstreamService bitstreamService;
@Autowired
private ItemConverter itemConverter;
@Autowired
private ItemService itemService;
@Autowired
private ObjectMapper mapper;
public CollectionRestRepository(CollectionService dsoService, public CollectionRestRepository(CollectionService dsoService,
CollectionConverter dsoConverter) { CollectionConverter dsoConverter) {
super(dsoService, dsoConverter, new DSpaceObjectPatch<CollectionRest>() {}); super(dsoService, dsoConverter, new DSpaceObjectPatch<CollectionRest>() {});
@@ -249,17 +259,8 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
@Override @Override
@PreAuthorize("hasPermission(#id, 'COLLECTION', 'DELETE')") @PreAuthorize("hasPermission(#id, 'COLLECTION', 'DELETE')")
protected void delete(Context context, UUID id) throws AuthorizeException { protected void delete(Context context, UUID id) throws AuthorizeException {
Collection collection = null;
try {
collection = cs.find(context, id);
if (collection == null) {
throw new ResourceNotFoundException(
CollectionRest.CATEGORY + "." + CollectionRest.NAME + " with id: " + id + " not found");
}
} catch (SQLException e) {
throw new RuntimeException("Unable to find Collection with id = " + id, e);
}
try { try {
Collection collection = getCollection(context, id);
cs.delete(context, collection); cs.delete(context, collection);
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException("Unable to delete Collection with id = " + id, e); throw new RuntimeException("Unable to delete Collection with id = " + id, e);
@@ -280,15 +281,71 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository<Collect
* @throws SQLException * @throws SQLException
*/ */
public BitstreamRest setLogo(Context context, Collection collection, MultipartFile uploadfile) public BitstreamRest setLogo(Context context, Collection collection, MultipartFile uploadfile)
throws IOException, AuthorizeException, SQLException { throws IOException, AuthorizeException, SQLException {
if (collection.getLogo() != null) { if (collection.getLogo() != null) {
throw new UnprocessableEntityException( throw new UnprocessableEntityException(
"The collection with the given uuid already has a logo: " + collection.getID()); "The collection with the given uuid already has a logo: " + collection.getID());
} }
Bitstream bitstream = cs.setLogo(context, collection, uploadfile.getInputStream()); Bitstream bitstream = cs.setLogo(context, collection, uploadfile.getInputStream());
cs.update(context, collection); cs.update(context, collection);
bitstreamService.update(context, bitstream); bitstreamService.update(context, bitstream);
return bitstreamConverter.fromModel(context.reloadEntity(bitstream)); return bitstreamConverter.fromModel(context.reloadEntity(bitstream));
} }
public ItemRest createTemplateItem(Context context, UUID uuid) throws SQLException, AuthorizeException {
Collection collection = getCollection(context, uuid);
if (collection.getTemplateItem() != null) {
throw new UnprocessableEntityException("Collection with ID " + uuid + " already contains a template item");
}
HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest();
ItemRest inputItemRest;
try {
ServletInputStream input = req.getInputStream();
inputItemRest = mapper.readValue(input, ItemRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body", e1);
}
if (inputItemRest.getInArchive() || inputItemRest.getDiscoverable() || inputItemRest.getWithdrawn()) {
throw new UnprocessableEntityException(
"The template item should not be archived, discoverable or withdrawn");
}
cs.createTemplateItem(context, collection);
Item templateItem = collection.getTemplateItem();
metadataConverter.setMetadata(context, templateItem, inputItemRest.getMetadata());
templateItem.setDiscoverable(false);
cs.update(context, collection);
itemService.update(context, templateItem);
context.commit();
return itemConverter.fromModel(templateItem);
}
public ItemRest getTemplateItem(Context context, UUID uuid) throws SQLException {
Collection collection = getCollection(context, uuid);
Item item = collection.getTemplateItem();
if (item == null) {
throw new ResourceNotFoundException(
"TemplateItem from " + CollectionRest.CATEGORY + "." + CollectionRest.NAME + " with id: "
+ uuid + " not found");
}
return itemConverter.fromModel(item);
}
private Collection getCollection(Context context, UUID uuid) throws SQLException {
Collection collection = null;
collection = cs.find(context, uuid);
if (collection == null) {
throw new ResourceNotFoundException(
CollectionRest.CATEGORY + "." + CollectionRest.NAME + " with id: " + uuid + " not found");
}
return collection;
}
} }

View File

@@ -0,0 +1,177 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.builder.CollectionBuilder;
import org.dspace.app.rest.builder.CommunityBuilder;
import org.dspace.app.rest.matcher.MetadataMatcher;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.MetadataRest;
import org.dspace.app.rest.model.MetadataValueRest;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Collection;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.web.servlet.MvcResult;
public class CollectionRestControllerIT extends AbstractControllerIntegrationTest {
ObjectMapper mapper;
private String adminAuthToken;
private Collection childCollection;
ItemRest testTemplateItem;
@Before
public void createStructure() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
childCollection = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1").build();
adminAuthToken = getAuthToken(admin.getEmail(), password);
mapper = new ObjectMapper();
}
private void setupTestTemplate() {
testTemplateItem = new ItemRest();
testTemplateItem.setInArchive(false);
testTemplateItem.setDiscoverable(false);
testTemplateItem.setWithdrawn(false);
testTemplateItem.setMetadata(new MetadataRest()
.put("dc.description", new MetadataValueRest("dc description content"))
.put("dc.description.abstract", new MetadataValueRest("dc description abstract content")));
}
@Test
public void createTemplateItemNotLoggedIn() throws Exception {
setupTestTemplate();
getClient().perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isUnauthorized());
}
@Test
public void createTemplateItem() throws Exception {
setupTestTemplate();
MvcResult mvcResult = getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isCreated())
.andReturn();
String content = mvcResult.getResponse().getContentAsString();
Map<String,Object> map = mapper.readValue(content, Map.class);
String itemUuidString = String.valueOf(map.get("uuid"));
getClient(adminAuthToken).perform(get(getTemplateItemUrlTemplate(childCollection.getID().toString())))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.id", is(itemUuidString)),
hasJsonPath("$.uuid", is(itemUuidString)),
hasJsonPath("$.type", is("item")),
hasJsonPath("$.inArchive", is(false)),
hasJsonPath("$.discoverable", is(false)),
hasJsonPath("$.withdrawn", is(false)),
hasJsonPath("$.metadata", Matchers.allOf(
MetadataMatcher.matchMetadata("dc.description",
"dc description content"),
MetadataMatcher.matchMetadata("dc.description.abstract",
"dc description abstract content")
)))));
}
@Test
public void createIllegal1TemplateItem() throws Exception {
setupTestTemplate();
testTemplateItem.setInArchive(true);
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void createIllegal2TemplateItem() throws Exception {
setupTestTemplate();
testTemplateItem.setDiscoverable(true);
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void createIllegal3TemplateItem() throws Exception {
setupTestTemplate();
testTemplateItem.setWithdrawn(true);
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void createTemplateItemNoRights() throws Exception {
setupTestTemplate();
String userToken = getAuthToken(eperson.getEmail(), password);
getClient(userToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isForbidden());
}
@Test
public void createDuplicateTemplateItem() throws Exception {
setupTestTemplate();
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isCreated());
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate(childCollection.getID().toString()))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isUnprocessableEntity());
}
@Test
public void createTemplateItemForNonexisting() throws Exception {
setupTestTemplate();
getClient(adminAuthToken).perform(post(
getTemplateItemUrlTemplate("16a4b65b-3b3f-4ef5-8058-ef6f5a653ef9"))
.content(mapper.writeValueAsBytes(testTemplateItem)).contentType(contentType))
.andExpect(status().isNotFound());
}
private String getTemplateItemUrlTemplate(String uuid) {
return "/api/core/collections/" + uuid + "/itemtemplate";
}
}