/**
* 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 org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.exception.PaginationException;
import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.utils.Utils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* This is the main entry point of the new REST API. Its responsibility is to
* provide a consistent behaviors for all the exposed resources in terms of
* returned HTTP codes, endpoint URLs, HTTP verbs to methods translation, etc.
* It delegates to the repository the business logic
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
*
*/
@RestController
@RequestMapping("/api/core/{model}")
@SuppressWarnings("rawtypes")
public class RestResourceController implements InitializingBean {
@Autowired
DiscoverableEndpointsService discoverableEndpointsService;
@Autowired
Utils utils;
@Override
public void afterPropertiesSet() {
List<Link> links = new ArrayList<Link>();
for (String r : utils.getRepositories()) {
// this doesn't work as we don't have an active http request
// see https://github.com/spring-projects/spring-hateoas/issues/408
// Link l = linkTo(this.getClass(), r).withRel(r);
String plural = English.plural(r);
Link l = new Link("/api/core/" + plural, plural);
links.add(l);
System.out.println(l.getRel() + " " + l.getHref());
}
discoverableEndpointsService.register(this, links);
}
@RequestMapping(method = RequestMethod.GET, value = "/{id:\\d+}")
@SuppressWarnings("unchecked")
DSpaceResource<RestModel> findOne(@PathVariable String model, @PathVariable Integer id, @RequestParam(required=false) String projection) {
return findOneInternal(model, id, projection);
}
@RequestMapping(method = RequestMethod.GET, value = "/{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}}")
@SuppressWarnings("unchecked")
DSpaceResource<RestModel> findOne(@PathVariable String model, @PathVariable UUID uuid, @RequestParam(required=false) String projection) {
return findOneInternal(model, uuid, projection);
}
private <ID extends Serializable> DSpaceResource<RestModel> findOneInternal(String model, ID id, String projection) {
DSpaceRestRepository<RestModel, ID> repository = utils.getResourceRepository(model);
RestModel modelObject = null;
try {
modelObject = repository.findOne(id);
} catch (ClassCastException e) {
}
if (modelObject == null) {
throw new ResourceNotFoundException(model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
return result;
}
@RequestMapping(method = RequestMethod.GET, value = "/{id:\\d+}/{rel}")
ResourceSupport findRel(@PathVariable String model, @PathVariable Integer id, @PathVariable String rel, @RequestParam(required=false) String projection) {
return findRelInternal(model, id, rel, projection);
}
@RequestMapping(method = RequestMethod.GET, value = "/{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}}/{rel}")
ResourceSupport findRel(@PathVariable String model, @PathVariable UUID uuid, @PathVariable String rel, @RequestParam(required=false) String projection) {
return findRelInternal(model, uuid, rel, projection);
}
private <ID extends Serializable> ResourceSupport findRelInternal(String model, ID uuid, String rel, String projection) {
// FIXME this is a very bad implementation as it leads most of times to
// more round-trip on the database and retrieval of unneeded infromation
DSpaceRestRepository<RestModel, ID> repository = utils.getResourceRepository(model);
RestModel modelObject = repository.findOne(uuid);
DSpaceResource result = repository.wrapResource(modelObject, rel);
if (result.getLink(rel) == null) {
//TODO create a custom exception
throw new ResourceNotFoundException(rel + "undefined for "+ model);
}
ResourceSupport resu = (ResourceSupport) result.getEmbedded().get(rel);
return resu;
}
@RequestMapping(method = RequestMethod.GET)
@SuppressWarnings("unchecked")
<T extends RestModel> PagedResources<DSpaceResource<T>> findAll(@PathVariable String model, Pageable page, PagedResourcesAssembler assembler, @RequestParam(required=false) String projection) {
DSpaceRestRepository<T, ?> repository = utils.getResourceRepository(model);
// Link link = entityLinks.linkFor(getResourceClass(model), model, page).withSelfRel();
Link link = linkTo(this.getClass(), model).withSelfRel();
Page<DSpaceResource<T>> resources;
try {
resources = repository.findAll(page).map(repository::wrapResource);
// resources.forEach(r -> {
// Link linkToSingleResource = Utils.linkToSingleResource(r, Link.REL_SELF);
// r.add(linkToSingleResource);
// });
} catch (PaginationException pe) {
resources = new PageImpl<DSpaceResource<T>>(new ArrayList<DSpaceResource<T>>(), page, pe.getTotal());
}
PagedResources<DSpaceResource<T>> result = assembler.toResource(resources, link);
return result;
}
}