Changing the representation to be more consistent in key naming (breaking change). Also starting work on the ability to "enrich" a representation with e.g. more links to other resources, etc...
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public abstract class LinkAware<T extends LinkAware<? super T>> {
|
||||
|
||||
@JsonProperty("links")
|
||||
protected List<Link> links = new ArrayList<Link>();
|
||||
|
||||
public List<Link> getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public T setLinks(List<Link> links) {
|
||||
if(null == links) {
|
||||
this.links = Collections.emptyList();
|
||||
} else {
|
||||
this.links = links;
|
||||
}
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public T addLink(Link link) {
|
||||
Assert.notNull(link, "Link cannot be null!");
|
||||
links.add(link);
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package org.springframework.data.rest.core;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@@ -17,7 +15,6 @@ public class Links {
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("_links")
|
||||
public List<Link> getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonAnyGetter;
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Abstraction that represents a Map-like REST resource plus a set of links.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class MapResource extends Resource<Map<String, Object>> {
|
||||
|
||||
public MapResource() {
|
||||
}
|
||||
|
||||
public MapResource(Map<String, Object> resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public Map<String, Object> getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> any() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
/**
|
||||
* Implementations of this interface will post-process objects to mutate them in ways meaningful to the context in
|
||||
* which they are called.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public interface PostProcessor<T> {
|
||||
|
||||
/**
|
||||
* Possibly perform some mutation on the object as a post-processing step in a flow.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
T postProcess(T obj);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for turning {@link URI}s into real objects.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public interface Resolver<T> {
|
||||
|
||||
/**
|
||||
* Take a {@link URI} and resolve it to an actual object.
|
||||
*
|
||||
* @param baseUri
|
||||
* The base URI that this resource is relative to.
|
||||
* @param uri
|
||||
* The URI id of the resource.
|
||||
*
|
||||
* @return The resolved object or {@literal null} if not found.
|
||||
*/
|
||||
T resolve(URI baseUri, URI uri);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonPropertyOrder;
|
||||
import org.codehaus.jackson.annotate.JsonUnwrapped;
|
||||
|
||||
/**
|
||||
* Wraps a simple object or a bean plus a set of links.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class Resource<T> extends LinkAware<Resource<T>> {
|
||||
|
||||
@JsonUnwrapped
|
||||
protected T resource;
|
||||
|
||||
public Resource() {
|
||||
}
|
||||
|
||||
public Resource(T resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public T getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setResource(T resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
/**
|
||||
* Abstraction for representing resources to the user agent.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public class Resources extends LinkAware<Resources> {
|
||||
|
||||
@JsonProperty("content")
|
||||
protected List<Resource<?>> resources = new ArrayList<Resource<?>>();
|
||||
|
||||
public List getResources() {
|
||||
return resources;
|
||||
}
|
||||
|
||||
public Resources setResources(List resources) {
|
||||
if(null == resources) {
|
||||
this.resources = Collections.emptyList();
|
||||
} else {
|
||||
this.resources = resources;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Resources addResource(Resource<?> resource) {
|
||||
resources.add((null == resource ? new Resource<Object>() : resource));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.springframework.data.rest.core.Resources;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PageableResources extends Resources {
|
||||
|
||||
protected long resourceCount = 0;
|
||||
@JsonProperty("page")
|
||||
protected PagingMetadata paging = new PagingMetadata(-1, 0);
|
||||
|
||||
public long getResourceCount() {
|
||||
return resourceCount;
|
||||
}
|
||||
|
||||
public PageableResources setResourceCount(long resourceCount) {
|
||||
this.resourceCount = resourceCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PagingMetadata getPaging() {
|
||||
return paging;
|
||||
}
|
||||
|
||||
public PageableResources setPaging(PagingMetadata paging) {
|
||||
this.paging = paging;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class PagingMetadata {
|
||||
|
||||
private int current = 0;
|
||||
private int total = 0;
|
||||
|
||||
public PagingMetadata(int current, int total) {
|
||||
this.current = current;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public PagingMetadata setCurrent(int current) {
|
||||
this.current = current;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public PagingMetadata setTotal(int total) {
|
||||
this.total = total;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.springframework.data.rest.repository;
|
||||
|
||||
import static org.springframework.data.rest.core.util.UriUtils.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.rest.core.Resolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class RepositoryMetadataResolver<M extends RepositoryMetadata<E>, E extends EntityMetadata<? extends AttributeMetadata>>
|
||||
implements Resolver<M>,
|
||||
ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
@Autowired(required = false)
|
||||
private List<RepositoryExporter> repositoryExporters = Collections.emptyList();
|
||||
|
||||
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public List<RepositoryExporter> getRepositoryExporters() {
|
||||
return repositoryExporters;
|
||||
}
|
||||
|
||||
public RepositoryMetadataResolver<M, E> setRepositoryExporters(List<RepositoryExporter> repositoryExporters) {
|
||||
Assert.notNull(repositoryExporters, "List of RepositoryExporters cannot be null!");
|
||||
this.repositoryExporters = repositoryExporters;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override public M resolve(URI baseUri, URI uri) {
|
||||
URI tail;
|
||||
if(null == (tail = tail(baseUri, uri))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String path = tail.getPath();
|
||||
for(RepositoryExporter exporter : repositoryExporters) {
|
||||
RepositoryMetadata repoMeta;
|
||||
if(null != (repoMeta = exporter.repositoryMetadataFor(path))) {
|
||||
return (M)repoMeta;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ public class RepositoryMethodResponse {
|
||||
|
||||
@JsonProperty("results")
|
||||
private List<Object> results = new ArrayList<Object>();
|
||||
@JsonProperty("_links")
|
||||
@JsonProperty("links")
|
||||
private List<Link> links = new ArrayList<Link>();
|
||||
private long totalCount = 0;
|
||||
private int totalPages = 1;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -17,15 +20,17 @@ public class RepositoryRestConfiguration {
|
||||
|
||||
public static final RepositoryRestConfiguration DEFAULT = new RepositoryRestConfiguration();
|
||||
|
||||
private int defaultPageSize = 20;
|
||||
private String pageParamName = "page";
|
||||
private String limitParamName = "limit";
|
||||
private String sortParamName = "sort";
|
||||
private String jsonpParamName = "callback";
|
||||
private String jsonpOnErrParamName = null;
|
||||
private List<HttpMessageConverter<?>> customConverters = Collections.emptyList();
|
||||
private MediaType defaultMediaType = MediaType.APPLICATION_JSON;
|
||||
private boolean dumpErrors = true;
|
||||
private int defaultPageSize = 20;
|
||||
private String pageParamName = "page";
|
||||
private String limitParamName = "limit";
|
||||
private String sortParamName = "sort";
|
||||
private String jsonpParamName = "callback";
|
||||
private String jsonpOnErrParamName = null;
|
||||
private List<HttpMessageConverter<?>> customConverters = Collections.emptyList();
|
||||
private Multimap<Class<?>, ResourcePostProcessor> resourcePostProcessors = ArrayListMultimap.create();
|
||||
private List<ResponsePostProcessor> responsePostProcessors = Collections.emptyList();
|
||||
private MediaType defaultMediaType = MediaType.APPLICATION_JSON;
|
||||
private boolean dumpErrors = true;
|
||||
|
||||
/**
|
||||
* Get the default size of {@link org.springframework.data.domain.Pageable}s. Default is 20.
|
||||
@@ -226,4 +231,52 @@ public class RepositoryRestConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of {@link ResponsePostProcessor}s that will potentially alter the responses going back to the
|
||||
* client.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<ResponsePostProcessor> getResponsePostProcessors() {
|
||||
return responsePostProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link ResponsePostProcessor}s that will potentially alter the responses going back to the
|
||||
*
|
||||
* @param responsePostProcessors
|
||||
*/
|
||||
public void setResponsePostProcessors(List<ResponsePostProcessor> responsePostProcessors) {
|
||||
this.responsePostProcessors = responsePostProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link ResourcePostProcessor} that is responsible for post-processing a particular domain type.
|
||||
*
|
||||
* @param type
|
||||
* @param postProcessor
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public RepositoryRestConfiguration addResourcePostProcessor(Class<?> type, ResourcePostProcessor postProcessor) {
|
||||
resourcePostProcessors.put(type, postProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ResourcePostProcessor}s assigned to a particular domain type.
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Collection<ResourcePostProcessor> getResourcePostProcessors(Class<?> type) {
|
||||
Collection<ResourcePostProcessor> pps = resourcePostProcessors.get(type);
|
||||
if(null == pps) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return pps;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,11 +42,16 @@ import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.rest.core.Handler;
|
||||
import org.springframework.data.rest.core.Link;
|
||||
import org.springframework.data.rest.core.Links;
|
||||
import org.springframework.data.rest.core.MapResource;
|
||||
import org.springframework.data.rest.core.Resource;
|
||||
import org.springframework.data.rest.core.Resources;
|
||||
import org.springframework.data.rest.core.SimpleLink;
|
||||
import org.springframework.data.rest.core.convert.DelegatingConversionService;
|
||||
import org.springframework.data.rest.core.util.UriUtils;
|
||||
import org.springframework.data.rest.repository.AttributeMetadata;
|
||||
import org.springframework.data.rest.repository.EntityMetadata;
|
||||
import org.springframework.data.rest.repository.PageableResources;
|
||||
import org.springframework.data.rest.repository.PagingMetadata;
|
||||
import org.springframework.data.rest.repository.RepositoryConstraintViolationException;
|
||||
import org.springframework.data.rest.repository.RepositoryExporter;
|
||||
import org.springframework.data.rest.repository.RepositoryExporterSupport;
|
||||
@@ -63,7 +68,6 @@ import org.springframework.data.rest.repository.context.BeforeLinkSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.BeforeSaveEvent;
|
||||
import org.springframework.data.rest.repository.context.RepositoryEvent;
|
||||
import org.springframework.data.rest.repository.invoke.CrudMethod;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryMethodResponse;
|
||||
import org.springframework.data.rest.repository.invoke.RepositoryQueryMethod;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -151,7 +155,7 @@ public class RepositoryRestController
|
||||
/**
|
||||
* List of {@link MediaType}s we can support, given the list of {@link HttpMessageConverter}s currently configured.
|
||||
*/
|
||||
private SortedSet<MediaType> availableMediaTypes = new TreeSet<MediaType>();
|
||||
private SortedSet<String> availableMediaTypes = new TreeSet<String>();
|
||||
@Autowired(required = false)
|
||||
private RepositoryRestConfiguration config = RepositoryRestConfiguration.DEFAULT;
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
@@ -312,17 +316,17 @@ public class RepositoryRestController
|
||||
UriComponentsBuilder uriBuilder) throws IOException {
|
||||
URI baseUri = uriBuilder.build().toUri();
|
||||
|
||||
Links links = new Links();
|
||||
Resources resources = new Resources();
|
||||
for(RepositoryExporter repoExporter : repositoryExporters) {
|
||||
for(String name : (Set<String>)repoExporter.repositoryNames()) {
|
||||
RepositoryMetadata repoMeta = repoExporter.repositoryMetadataFor(name);
|
||||
String rel = repoMeta.rel();
|
||||
URI path = buildUri(baseUri, name);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
resources.addLink(new SimpleLink(rel, path));
|
||||
}
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), resources);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,19 +360,19 @@ public class RepositoryRestController
|
||||
return negotiateResponse(request, HttpStatus.METHOD_NOT_ALLOWED, new HttpHeaders(), null);
|
||||
}
|
||||
|
||||
RepositoryMethodResponse response = new RepositoryMethodResponse();
|
||||
|
||||
Iterator allEntities = Collections.emptyList().iterator();
|
||||
final Resources resources;
|
||||
if(repoMeta.repository() instanceof PagingAndSortingRepository) {
|
||||
PageableResources pr = new PageableResources();
|
||||
|
||||
Page page = ((PagingAndSortingRepository)repoMeta.repository()).findAll(pageSort);
|
||||
if(page.hasContent()) {
|
||||
allEntities = page.iterator();
|
||||
}
|
||||
|
||||
// Set page counts in the response
|
||||
response.setTotalCount(page.getTotalElements());
|
||||
response.setTotalPages(page.getTotalPages());
|
||||
response.setCurrentPage(page.getNumber() + 1);
|
||||
pr.setPaging(new PagingMetadata(page.getNumber() + 1, page.getTotalPages()));
|
||||
pr.setResourceCount(page.getTotalElements());
|
||||
|
||||
// Copy over parameters
|
||||
UriComponentsBuilder selfUri = UriComponentsBuilder.fromUri(baseUri).pathSegment(repository);
|
||||
@@ -388,7 +392,7 @@ public class RepositoryRestController
|
||||
!page.isFirstPage() && page.hasPreviousPage(),
|
||||
page.getNumber(),
|
||||
"prev",
|
||||
response.getLinks()
|
||||
pr.getLinks()
|
||||
);
|
||||
maybeAddPrevNextLink(
|
||||
nextPrevBase,
|
||||
@@ -398,35 +402,43 @@ public class RepositoryRestController
|
||||
!page.isLastPage() && page.hasNextPage(),
|
||||
page.getNumber() + 2,
|
||||
"next",
|
||||
response.getLinks()
|
||||
pr.getLinks()
|
||||
);
|
||||
|
||||
resources = pr;
|
||||
} else {
|
||||
Iterable it = repoMeta.repository().findAll();
|
||||
if(null != it) {
|
||||
allEntities = it.iterator();
|
||||
}
|
||||
resources = new Resources();
|
||||
}
|
||||
|
||||
while(allEntities.hasNext()) {
|
||||
Object o = allEntities.next();
|
||||
Serializable id = (Serializable)repoMeta.entityMetadata().idAttribute().get(o);
|
||||
if(shouldReturnLinks(request.getServletRequest().getHeader("Accept"))) {
|
||||
response.addLink(new SimpleLink(repoMeta.rel() + "." + o.getClass().getSimpleName(),
|
||||
buildUri(baseUri, repository, id.toString())));
|
||||
resources.addLink(new SimpleLink(repoMeta.rel() + "." + o.getClass().getSimpleName(),
|
||||
buildUri(baseUri, repository, id.toString())));
|
||||
} else {
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repoMeta.rel(),
|
||||
o,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, id.toString()));
|
||||
addSelfLink(baseUri, entityDto, repository, id.toString());
|
||||
response.addResult(entityDto);
|
||||
MapResource res = createResource(repoMeta.rel(),
|
||||
o,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, id.toString()));
|
||||
|
||||
URI selfUri = buildUri(baseUri, repository, id.toString());
|
||||
res.addLink(new SimpleLink(SELF, selfUri));
|
||||
|
||||
resources.addResource(res);
|
||||
}
|
||||
}
|
||||
response.addLink(new SimpleLink(repoMeta.rel() + ".search",
|
||||
buildUri(baseUri, repository, "search")));
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
|
||||
if(!repoMeta.queryMethods().isEmpty()) {
|
||||
resources.addLink(new SimpleLink(repoMeta.rel() + ".search",
|
||||
buildUri(baseUri, repository, "search")));
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), resources);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,7 +464,7 @@ public class RepositoryRestController
|
||||
URI baseUri = uriBuilder.build().toUri();
|
||||
|
||||
RepositoryMetadata repoMeta = repositoryMetadataFor(repository);
|
||||
Links links = new Links();
|
||||
Resources resources = new Resources();
|
||||
|
||||
for(Map.Entry<String, RepositoryQueryMethod> entry : ((Map<String, RepositoryQueryMethod>)repoMeta.queryMethods())
|
||||
.entrySet()) {
|
||||
@@ -462,7 +474,7 @@ public class RepositoryRestController
|
||||
Method m = entry.getValue().method();
|
||||
if(m.isAnnotationPresent(RestResource.class)) {
|
||||
RestResource resourceAnno = m.getAnnotation(RestResource.class);
|
||||
links.add(new SimpleLink(
|
||||
resources.addLink(new SimpleLink(
|
||||
(StringUtils.hasText(resourceAnno.rel())
|
||||
? repoMeta.rel() + "." + resourceAnno.rel()
|
||||
: repoMeta.rel() + "." + entry.getKey()),
|
||||
@@ -473,12 +485,12 @@ public class RepositoryRestController
|
||||
));
|
||||
} else {
|
||||
// No customizations, use the default
|
||||
links.add(new SimpleLink(repoMeta.rel() + "." + entry.getKey(),
|
||||
buildUri(baseSearchUri, entry.getKey())));
|
||||
resources.addLink(new SimpleLink(repoMeta.rel() + "." + entry.getKey(),
|
||||
buildUri(baseSearchUri, entry.getKey())));
|
||||
}
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), resources);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -567,17 +579,15 @@ public class RepositoryRestController
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryMethodResponse response = new RepositoryMethodResponse();
|
||||
|
||||
Object result;
|
||||
if(null == (result = queryMethod.method().invoke(repo, paramVals))) {
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), new Resources());
|
||||
}
|
||||
|
||||
Resources resources = new Resources();
|
||||
Iterator entities = Collections.emptyList().iterator();
|
||||
if(result instanceof Collection) {
|
||||
entities = ((Collection)result).iterator();
|
||||
response.setTotalCount(((Collection)result).size());
|
||||
} else if(result instanceof Page) {
|
||||
Page page = (Page)result;
|
||||
|
||||
@@ -586,9 +596,9 @@ public class RepositoryRestController
|
||||
}
|
||||
|
||||
// Set page counts in the response
|
||||
response.setTotalCount(page.getTotalElements());
|
||||
response.setTotalPages(page.getTotalPages());
|
||||
response.setCurrentPage(page.getNumber() + 1);
|
||||
PageableResources pr = new PageableResources();
|
||||
pr.setResourceCount(page.getTotalElements());
|
||||
pr.setPaging(new PagingMetadata(page.getNumber() + 1, page.getTotalPages()));
|
||||
|
||||
// Copy over parameters
|
||||
UriComponentsBuilder selfUri = UriComponentsBuilder.fromUri(baseUri).pathSegment(repository, "search", query);
|
||||
@@ -608,7 +618,7 @@ public class RepositoryRestController
|
||||
!page.isFirstPage() && page.hasPreviousPage(),
|
||||
page.getNumber(),
|
||||
"prev",
|
||||
response.getLinks()
|
||||
pr.getLinks()
|
||||
);
|
||||
maybeAddPrevNextLink(
|
||||
nextPrevBase,
|
||||
@@ -618,9 +628,10 @@ public class RepositoryRestController
|
||||
!page.isLastPage() && page.hasNextPage(),
|
||||
page.getNumber() + 2,
|
||||
"next",
|
||||
response.getLinks()
|
||||
pr.getLinks()
|
||||
);
|
||||
|
||||
resources = pr;
|
||||
} else {
|
||||
entities = Collections.singletonList(result).iterator();
|
||||
}
|
||||
@@ -630,7 +641,7 @@ public class RepositoryRestController
|
||||
|
||||
RepositoryMetadata elemRepoMeta;
|
||||
if(null == (elemRepoMeta = repositoryMetadataFor(obj.getClass()))) {
|
||||
response.addResult(obj);
|
||||
resources.addResource(new Resource(obj));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -639,18 +650,21 @@ public class RepositoryRestController
|
||||
if(shouldReturnLinks(request.getServletRequest().getHeader("Accept"))) {
|
||||
String rel = elemRepoMeta.rel() + "." + elemRepoMeta.entityMetadata().type().getSimpleName();
|
||||
URI path = buildUri(baseUri, repository, id);
|
||||
response.addLink(new SimpleLink(rel, path));
|
||||
resources.addLink(new SimpleLink(rel, path));
|
||||
} else {
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repoMeta.rel(),
|
||||
obj,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, id));
|
||||
addSelfLink(baseUri, entityDto, repository, id);
|
||||
response.addResult(entityDto);
|
||||
MapResource res = createResource(repoMeta.rel(),
|
||||
obj,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, id));
|
||||
|
||||
URI selfUri = buildUri(baseUri, repository, id);
|
||||
res.addLink(new SimpleLink(SELF, selfUri));
|
||||
|
||||
resources.addResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), response);
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), resources);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,15 +716,14 @@ public class RepositoryRestController
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set(LOCATION, selfUri.toString());
|
||||
|
||||
Object body = null;
|
||||
if(null != request.getServletRequest().getParameter("returnBody")
|
||||
&& "true".equals(request.getServletRequest().getParameter("returnBody"))) {
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repoMeta.rel(),
|
||||
savedEntity,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, sId));
|
||||
addSelfLink(baseUri, entityDto, repository, sId);
|
||||
body = entityDto;
|
||||
Resource<?> body = null;
|
||||
if(returnBody(request)) {
|
||||
MapResource resource = createResource(repoMeta.rel(),
|
||||
savedEntity,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, sId));
|
||||
resource.addLink(new SimpleLink(SELF, selfUri));
|
||||
body = resource;
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.CREATED, headers, body);
|
||||
@@ -767,13 +780,14 @@ public class RepositoryRestController
|
||||
headers.set("ETag", "\"" + version.toString() + "\"");
|
||||
}
|
||||
}
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(repoMeta.rel(),
|
||||
entity,
|
||||
repoMeta.entityMetadata(),
|
||||
buildUri(baseUri, repository, id));
|
||||
addSelfLink(baseUri, entityDto, repository, id);
|
||||
MapResource res = createResource(repoMeta.rel(),
|
||||
entity,
|
||||
repoMeta.entityMetadata(),
|
||||
baseUri);
|
||||
URI selfUri = buildUri(baseUri, repository, id);
|
||||
res.addLink(new SimpleLink(SELF, selfUri));
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, headers, entityDto);
|
||||
return negotiateResponse(request, HttpStatus.OK, headers, res);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,8 +808,7 @@ public class RepositoryRestController
|
||||
@RequestMapping(
|
||||
value = "/{repository}/{id}",
|
||||
method = {
|
||||
RequestMethod.PUT,
|
||||
RequestMethod.POST
|
||||
RequestMethod.PUT
|
||||
}
|
||||
)
|
||||
@ResponseBody
|
||||
@@ -818,51 +831,64 @@ public class RepositoryRestController
|
||||
CrudRepository repo = repoMeta.repository();
|
||||
Class<?> domainType = repoMeta.entityMetadata().type();
|
||||
|
||||
boolean returnBody = true;
|
||||
if(null != request.getServletRequest().getParameter("returnBody")) {
|
||||
returnBody = Boolean.parseBoolean(request.getServletRequest().getParameter("returnBody"));
|
||||
}
|
||||
|
||||
MediaType incomingMediaType = request.getHeaders().getContentType();
|
||||
Object incoming;
|
||||
if(null == (incoming = readIncoming(request, incomingMediaType, domainType))) {
|
||||
throw new HttpMessageNotReadableException("Could not create an instance of " + domainType.getSimpleName() + " from input.");
|
||||
throw new HttpMessageNotReadableException("Could not create an instance of "
|
||||
+ domainType.getSimpleName() + " from input.");
|
||||
}
|
||||
|
||||
// Set the ID specified in the URL
|
||||
repoMeta.entityMetadata().idAttribute().set(serId, incoming);
|
||||
if(request.getMethod() == HttpMethod.POST) {
|
||||
|
||||
publishEvent(new BeforeSaveEvent(incoming));
|
||||
Object savedEntity = repo.save(incoming);
|
||||
publishEvent(new AfterSaveEvent(savedEntity));
|
||||
|
||||
URI selfUri = buildUri(baseUri, repository, id);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set(LOCATION, selfUri.toString());
|
||||
|
||||
return negotiateResponse(request, HttpStatus.CREATED, headers, (returnBody ? savedEntity : null));
|
||||
} else {
|
||||
Object entity;
|
||||
if(null == (entity = repo.findOne(serId))) {
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
boolean isUpdate = false;
|
||||
Object entity;
|
||||
if(null != (entity = repo.findOne(serId))) {
|
||||
// Updating an existing resource
|
||||
isUpdate = true;
|
||||
for(AttributeMetadata attrMeta : (Collection<AttributeMetadata>)repoMeta.entityMetadata()
|
||||
.embeddedAttributes()
|
||||
.values()) {
|
||||
Object incomingVal = attrMeta.get(incoming);
|
||||
if(null != incomingVal) {
|
||||
Object incomingVal;
|
||||
if(null != (incomingVal = attrMeta.get(incoming))) {
|
||||
attrMeta.set(incomingVal, entity);
|
||||
}
|
||||
}
|
||||
|
||||
publishEvent(new BeforeSaveEvent(entity));
|
||||
Object savedEntity = repo.save(entity);
|
||||
publishEvent(new AfterSaveEvent(savedEntity));
|
||||
|
||||
return negotiateResponse(request, HttpStatus.NO_CONTENT, new HttpHeaders(), (returnBody ? savedEntity : null));
|
||||
} else {
|
||||
entity = incoming;
|
||||
}
|
||||
|
||||
publishEvent(new BeforeSaveEvent(entity));
|
||||
Object savedEntity = repo.save(entity);
|
||||
publishEvent(new AfterSaveEvent(savedEntity));
|
||||
|
||||
URI selfUri = buildUri(baseUri, repository, id);
|
||||
|
||||
Object body = null;
|
||||
if(returnBody(request)) {
|
||||
MapResource res = createResource(repoMeta.rel(),
|
||||
savedEntity,
|
||||
repoMeta.entityMetadata(),
|
||||
baseUri);
|
||||
res.addLink(new SimpleLink(SELF, selfUri));
|
||||
|
||||
body = res;
|
||||
}
|
||||
|
||||
if(!isUpdate) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set(LOCATION, selfUri.toString());
|
||||
|
||||
return negotiateResponse(request,
|
||||
HttpStatus.CREATED,
|
||||
headers,
|
||||
body);
|
||||
} else {
|
||||
return negotiateResponse(request,
|
||||
(null != body ? HttpStatus.OK : HttpStatus.NO_CONTENT),
|
||||
new HttpHeaders(),
|
||||
body);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -968,8 +994,8 @@ public class RepositoryRestController
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
Resources res = new Resources();
|
||||
AttributeMetadata idAttr = propRepoMeta.entityMetadata().idAttribute();
|
||||
Links links = new Links();
|
||||
if(propVal instanceof Collection) {
|
||||
for(Object o : (Collection)propVal) {
|
||||
String propValId = idAttr.get(o).toString();
|
||||
@@ -977,7 +1003,7 @@ public class RepositoryRestController
|
||||
+ entity.getClass().getSimpleName() + "."
|
||||
+ attrType.getSimpleName();
|
||||
URI path = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
res.addLink(new SimpleLink(rel, path));
|
||||
}
|
||||
} else if(propVal instanceof Map) {
|
||||
for(Map.Entry<Object, Object> entry : ((Map<Object, Object>)propVal).entrySet()) {
|
||||
@@ -990,16 +1016,16 @@ public class RepositoryRestController
|
||||
} else {
|
||||
sKey = conversionService.convert(oKey, String.class);
|
||||
}
|
||||
links.add(new SimpleLink(sKey, path));
|
||||
res.addLink(new SimpleLink(sKey, path));
|
||||
}
|
||||
} else {
|
||||
String propValId = idAttr.get(propVal).toString();
|
||||
String rel = repository + "." + entity.getClass().getSimpleName() + "." + property;
|
||||
URI path = buildUri(baseUri, repository, id, property, propValId);
|
||||
links.add(new SimpleLink(rel, path));
|
||||
res.addLink(new SimpleLink(rel, path));
|
||||
}
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), links);
|
||||
return negotiateResponse(request, HttpStatus.OK, new HttpHeaders(), res);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1203,17 +1229,33 @@ public class RepositoryRestController
|
||||
return negotiateResponse(request, HttpStatus.METHOD_NOT_ALLOWED, new HttpHeaders(), null);
|
||||
}
|
||||
|
||||
// Check for the existence of the parent
|
||||
CrudRepository repo = repoMeta.repository();
|
||||
Serializable serId = stringToSerializable(id,
|
||||
(Class<? extends Serializable>)repoMeta.entityMetadata()
|
||||
.idAttribute()
|
||||
.type());
|
||||
if(!repo.exists(serId)) {
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
// Check for the existence of the property
|
||||
AttributeMetadata attrMeta;
|
||||
if(null == (attrMeta = repoMeta.entityMetadata().attribute(property))) {
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
// Find linked entity
|
||||
// Check for the existence of a Repository for the linked entity
|
||||
RepositoryMetadata linkedRepoMeta;
|
||||
if(null == (linkedRepoMeta = repositoryMetadataFor(attrMeta))) {
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
if(!linkedRepoMeta.exportsMethod(CrudMethod.FIND_ONE)) {
|
||||
return negotiateResponse(request, HttpStatus.METHOD_NOT_ALLOWED, new HttpHeaders(), null);
|
||||
}
|
||||
|
||||
// Find the linked entity
|
||||
CrudRepository linkedRepo = linkedRepoMeta.repository();
|
||||
Serializable sChildId = stringToSerializable(linkedId,
|
||||
(Class<? extends Serializable>)linkedRepoMeta.entityMetadata()
|
||||
@@ -1224,18 +1266,18 @@ public class RepositoryRestController
|
||||
return notFoundResponse(request);
|
||||
}
|
||||
|
||||
Map<String, Object> entityDto = extractPropertiesLinkAware(linkedRepoMeta.rel(),
|
||||
linkedEntity,
|
||||
linkedRepoMeta.entityMetadata(),
|
||||
buildUri(baseUri,
|
||||
linkedRepoMeta.name(),
|
||||
linkedId));
|
||||
URI selfUri = addSelfLink(baseUri, entityDto, linkedRepoMeta.name(), linkedId);
|
||||
MapResource res = createResource(linkedRepoMeta.rel(),
|
||||
linkedEntity,
|
||||
linkedRepoMeta.entityMetadata(),
|
||||
baseUri);
|
||||
|
||||
URI selfUri = buildUri(baseUri, linkedRepoMeta.name(), linkedId);
|
||||
res.addLink(new SimpleLink(SELF, selfUri));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("Content-Location", selfUri.toString());
|
||||
|
||||
return negotiateResponse(request, HttpStatus.OK, headers, entityDto);
|
||||
return negotiateResponse(request, HttpStatus.OK, headers, res);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1564,6 +1606,30 @@ public class RepositoryRestController
|
||||
return null;
|
||||
}
|
||||
|
||||
private MapResource createResource(String repoRel,
|
||||
Object entity,
|
||||
EntityMetadata<AttributeMetadata> entityMetadata,
|
||||
URI baseUri) {
|
||||
Map<String, Object> entityDto = new HashMap<String, Object>();
|
||||
MapResource resource = new MapResource(entityDto);
|
||||
|
||||
for(Map.Entry<String, AttributeMetadata> attrMeta : entityMetadata.embeddedAttributes().entrySet()) {
|
||||
String name = attrMeta.getKey();
|
||||
Object val;
|
||||
if(null != (val = attrMeta.getValue().get(entity))) {
|
||||
entityDto.put(name, val);
|
||||
}
|
||||
}
|
||||
|
||||
for(String attrName : entityMetadata.linkedAttributes().keySet()) {
|
||||
URI uri = buildUri(baseUri, attrName);
|
||||
resource.addLink(new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri));
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
/*
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private Map<String, Object> extractPropertiesLinkAware(String repoRel,
|
||||
Object entity,
|
||||
@@ -1592,6 +1658,7 @@ public class RepositoryRestController
|
||||
|
||||
return entityDto;
|
||||
}
|
||||
*/
|
||||
|
||||
private boolean shouldReturnLinks(String acceptHeader) {
|
||||
if(null != acceptHeader) {
|
||||
@@ -1609,6 +1676,15 @@ public class RepositoryRestController
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean returnBody(ServletServerHttpRequest request) {
|
||||
String s = request.getServletRequest().getParameter("returnBody");
|
||||
if(null != s) {
|
||||
return "true".equals(s);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private <E extends RepositoryEvent> void publishEvent(E event) {
|
||||
if(null != applicationContext) {
|
||||
applicationContext.publishEvent(event);
|
||||
|
||||
@@ -12,6 +12,8 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
*/
|
||||
public class RepositoryRestExporterServlet extends DispatcherServlet {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public RepositoryRestExporterServlet() {
|
||||
configure();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package org.springframework.data.rest.webmvc;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
@@ -16,11 +18,17 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
*/
|
||||
public class RepositoryRestHandlerAdapter extends RequestMappingHandlerAdapter {
|
||||
|
||||
@Autowired
|
||||
private ResourcesReturnValueHandler resourcesReturnValueHandler;
|
||||
|
||||
public RepositoryRestHandlerAdapter(RepositoryRestConfiguration config) {
|
||||
setCustomArgumentResolvers(Arrays.asList(
|
||||
new ServerHttpRequestMethodArgumentResolver(),
|
||||
new PagingAndSortingMethodArgumentResolver(config)
|
||||
));
|
||||
setCustomReturnValueHandlers(Arrays.<HandlerMethodReturnValueHandler>asList(
|
||||
resourcesReturnValueHandler
|
||||
));
|
||||
}
|
||||
|
||||
@Override public int getOrder() {
|
||||
|
||||
@@ -92,6 +92,10 @@ public class RepositoryRestMvcConfiguration {
|
||||
return new RepositoryRestController();
|
||||
}
|
||||
|
||||
@Bean ResourcesReturnValueHandler resourcesReturnValueHandler() {
|
||||
return new ResourcesReturnValueHandler(repositoryRestConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special {@link org.springframework.web.servlet.HandlerAdapter} that only recognizes handler methods defined in the
|
||||
* {@link RepositoryRestController} class.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.data.rest.core.PostProcessor;
|
||||
import org.springframework.data.rest.core.Resource;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are allowed to mutate a {@link Resource} being sent back to the client.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public interface ResourcePostProcessor extends PostProcessor<Resource> {
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.rest.core.Resources;
|
||||
import org.springframework.data.rest.repository.RepositoryExporterSupport;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodReturnValueHandler} implementation that applies user-defined post-processors to the
|
||||
* representation being sent back to the client.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class ResourcesReturnValueHandler
|
||||
extends RepositoryExporterSupport<ResourcesReturnValueHandler>
|
||||
implements HandlerMethodReturnValueHandler {
|
||||
|
||||
private RepositoryRestConfiguration config = RepositoryRestConfiguration.DEFAULT;
|
||||
|
||||
public ResourcesReturnValueHandler(RepositoryRestConfiguration config) {
|
||||
if(null != config) {
|
||||
this.config = config;
|
||||
} else {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return Resources.class.isAssignableFrom(returnType.getParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleReturnValue(Object returnValue,
|
||||
MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest) throws Exception {
|
||||
Resources resources = (Resources)returnValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.springframework.data.rest.webmvc;
|
||||
|
||||
import org.springframework.data.rest.core.PostProcessor;
|
||||
import org.springframework.data.rest.core.Resources;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are allowed to mutate the {@link org.springframework.data.rest.core.Resources}
|
||||
* object that is being sent back to the client as a response.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public interface ResponsePostProcessor extends PostProcessor<Resources> {
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class DiscoverySpec extends BaseSpec {
|
||||
response.statusCode == HttpStatus.OK
|
||||
|
||||
when:
|
||||
def links = readJson(response)._links
|
||||
def links = readJson(response).links
|
||||
|
||||
then:
|
||||
links.size() == 4
|
||||
@@ -38,9 +38,13 @@ class DiscoverySpec extends BaseSpec {
|
||||
|
||||
when:
|
||||
def response = controller.listEntities(request, pageSort, baseUri, "people")
|
||||
def body = readJson(response)
|
||||
|
||||
then:
|
||||
response.statusCode == HttpStatus.OK
|
||||
body.resources.size() == 10
|
||||
body.page.total == 2
|
||||
body.page.current == 1
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class QueryMethodsSpec extends BaseSpec {
|
||||
|
||||
then:
|
||||
response.statusCode == HttpStatus.OK
|
||||
body["_links"].size() == 2
|
||||
body.links.size() == 2
|
||||
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class QueryMethodsSpec extends BaseSpec {
|
||||
|
||||
then:
|
||||
response.statusCode == HttpStatus.OK
|
||||
body["results"].size() == 1
|
||||
body.resources.size() == 1
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user