DATAREST-521 - Excerpts now contain all links of the projection target.
Extended the serialization capabilities to explicitly treat projection proxies by wrapping them into a ProjectionResource and collecting links for the target backing the projection if the target is an exported resource.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,7 +43,6 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
|
||||
private final PersistentEntity<?, ?> entity;
|
||||
private final Resources<EmbeddedWrapper> embeddeds;
|
||||
private final boolean enforceAssociationLinks;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistentEntityResource} for the given {@link PersistentEntity}, content, embedded
|
||||
@@ -52,11 +51,10 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param content must not be {@literal null}.
|
||||
* @param links must not be {@literal null}.
|
||||
* @param renderAllAssociations
|
||||
* @param embeddeds can be {@literal null}.
|
||||
*/
|
||||
private PersistentEntityResource(PersistentEntity<?, ?> entity, Object content, Iterable<Link> links,
|
||||
boolean renderAllAssociations, Resources<EmbeddedWrapper> embeddeds) {
|
||||
Resources<EmbeddedWrapper> embeddeds) {
|
||||
|
||||
super(content, links);
|
||||
|
||||
@@ -64,7 +62,6 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
|
||||
this.entity = entity;
|
||||
this.embeddeds = embeddeds == null ? NO_EMBEDDEDS : embeddeds;
|
||||
this.enforceAssociationLinks = renderAllAssociations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +115,6 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
private final List<Link> links = new ArrayList<Link>();
|
||||
|
||||
private Resources<EmbeddedWrapper> embeddeds;
|
||||
private boolean renderAllAssociationLinks = false;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Builder} instance for the given content and {@link PersistentEntity}.
|
||||
@@ -147,17 +143,6 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the builder to render all association links independently of the embedded resources added.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder renderAllAssociationLinks() {
|
||||
|
||||
this.renderAllAssociationLinks = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given {@link Link} to the {@link PersistentEntityResource}.
|
||||
*
|
||||
@@ -178,7 +163,7 @@ public class PersistentEntityResource extends Resource<Object> {
|
||||
* @return
|
||||
*/
|
||||
public PersistentEntityResource build() {
|
||||
return new PersistentEntityResource(entity, content, links, renderAllAssociationLinks, embeddeds);
|
||||
return new PersistentEntityResource(entity, content, links, embeddeds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.data.rest.webmvc.mapping.AssociationLinks;
|
||||
import org.springframework.data.rest.webmvc.support.Projector;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceAssembler;
|
||||
import org.springframework.hateoas.core.EmbeddedWrapper;
|
||||
import org.springframework.hateoas.core.EmbeddedWrappers;
|
||||
@@ -81,9 +80,7 @@ public class PersistentEntityResourceAssembler implements ResourceAssembler<Obje
|
||||
public PersistentEntityResource toResource(Object instance) {
|
||||
|
||||
Assert.notNull(instance, "Entity instance must not be null!");
|
||||
|
||||
return wrap(projector.projectExcerpt(instance), instance).build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,8 +92,7 @@ public class PersistentEntityResourceAssembler implements ResourceAssembler<Obje
|
||||
public PersistentEntityResource toFullResource(Object instance) {
|
||||
|
||||
Assert.notNull(instance, "Entity instance must not be null!");
|
||||
return wrap(projector.project(instance), instance).//
|
||||
renderAllAssociationLinks().build();
|
||||
return wrap(projector.project(instance), instance).build();
|
||||
}
|
||||
|
||||
private Builder wrap(Object instance, Object source) {
|
||||
@@ -165,14 +161,14 @@ public class PersistentEntityResourceAssembler implements ResourceAssembler<Obje
|
||||
|
||||
for (Object element : collection) {
|
||||
if (element != null) {
|
||||
nestedCollection.add(getExcerptResource(element));
|
||||
nestedCollection.add(projector.projectExcerpt(element));
|
||||
}
|
||||
}
|
||||
|
||||
associationProjections.add(wrappers.wrap(nestedCollection, rel));
|
||||
|
||||
} else {
|
||||
associationProjections.add(wrappers.wrap(getExcerptResource(value), rel));
|
||||
associationProjections.add(wrappers.wrap(projector.projectExcerpt(value), rel));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -203,14 +199,4 @@ public class PersistentEntityResourceAssembler implements ResourceAssembler<Obje
|
||||
Link resourceLink = entityLinks.linkToSingleResource(entity.getType(), id);
|
||||
return new Link(resourceLink.getHref(), Link.REL_SELF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Resource} instance for the excerpt of the given source entity.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Resource<Object> getExcerptResource(Object entity) {
|
||||
return new Resource<Object>(projector.projectExcerpt(entity), getSelfLinkFor(entity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,8 +539,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
|
||||
PersistentEntities entities = persistentEntities();
|
||||
|
||||
return new PersistentEntityJackson2Module(resourceMappings(), entities, config(),
|
||||
uriToEntityConverter(defaultConversionService()));
|
||||
return new PersistentEntityJackson2Module(
|
||||
resourceMappings(), entities, config(), uriToEntityConverter(defaultConversionService()), entityLinks());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc.json;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,9 +26,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.projection.TargetAware;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.core.Path;
|
||||
import org.springframework.data.rest.core.UriToEntityConverter;
|
||||
@@ -36,7 +39,9 @@ import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResource;
|
||||
import org.springframework.data.rest.webmvc.mapping.AssociationLinks;
|
||||
import org.springframework.data.rest.webmvc.mapping.LinkCollectingAssociationHandler;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.Links;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.Resources;
|
||||
import org.springframework.hateoas.UriTemplate;
|
||||
@@ -68,6 +73,7 @@ import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
import com.fasterxml.jackson.databind.type.CollectionLikeType;
|
||||
import com.fasterxml.jackson.databind.util.NameTransformer;
|
||||
|
||||
/**
|
||||
* Jackson 2 module to serialize and deserialize {@link PersistentEntityResource}s.
|
||||
@@ -92,7 +98,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
* @param converter must not be {@literal null}.
|
||||
*/
|
||||
public PersistentEntityJackson2Module(ResourceMappings mappings, PersistentEntities entities,
|
||||
RepositoryRestConfiguration config, UriToEntityConverter converter) {
|
||||
RepositoryRestConfiguration config, UriToEntityConverter converter, EntityLinks entityLinks) {
|
||||
|
||||
super(new Version(2, 0, 0, null, "org.springframework.data.rest", "jackson-module"));
|
||||
|
||||
@@ -102,8 +108,12 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
Assert.notNull(converter, "UriToEntityConverter must not be null!");
|
||||
|
||||
AssociationLinks associationLinks = new AssociationLinks(mappings);
|
||||
LinkCollector collector = new LinkCollector(entities, entityLinks, associationLinks);
|
||||
|
||||
addSerializer(new PersistentEntityResourceSerializer(collector));
|
||||
addSerializer(new ProjectionSerializer(collector, mappings));
|
||||
addSerializer(new ProjectionResourceContentSerializer());
|
||||
|
||||
addSerializer(new PersistentEntityResourceSerializer(entities, associationLinks));
|
||||
setSerializerModifier(new AssociationOmittingSerializerModifier(entities, associationLinks, config));
|
||||
setDeserializerModifier(new AssociationUriResolvingDeserializerModifier(entities, converter, associationLinks));
|
||||
}
|
||||
@@ -114,10 +124,10 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class PersistentEntityResourceSerializer extends StdSerializer<PersistentEntityResource> {
|
||||
|
||||
private final PersistentEntities entities;
|
||||
private final AssociationLinks associationLinks;
|
||||
private final LinkCollector collector;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistentEntityResourceSerializer} using the given {@link PersistentEntities} and
|
||||
@@ -127,15 +137,11 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
* @param links must not be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private PersistentEntityResourceSerializer(PersistentEntities entities, AssociationLinks links) {
|
||||
private PersistentEntityResourceSerializer(LinkCollector collector) {
|
||||
|
||||
super((Class) PersistentEntityResource.class);
|
||||
|
||||
Assert.notNull(entities, "PersistentEntities must not be null!");
|
||||
Assert.notNull(links, "AssociationLinks must not be null!");
|
||||
|
||||
this.associationLinks = links;
|
||||
this.entities = entities;
|
||||
this.collector = collector;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -150,23 +156,17 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
LOG.debug("Serializing PersistentEntity " + resource.getPersistentEntity());
|
||||
}
|
||||
|
||||
final Link id = resource.getId();
|
||||
Object content = resource.getContent();
|
||||
|
||||
if (id == null) {
|
||||
throw new JsonGenerationException(String.format("No self link found resource %s!", resource));
|
||||
if (TargetAware.class.isInstance(content)) {
|
||||
|
||||
TargetAware targetAware = (TargetAware) content;
|
||||
Links links = collector.getLinksFor(targetAware.getTarget(), resource.getLinks());
|
||||
provider.defaultSerializeValue(new ProjectionResource(targetAware, links), jgen);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Link> links = new ArrayList<Link>();
|
||||
links.addAll(resource.getLinks());
|
||||
|
||||
Path basePath = new Path(id.expand().getHref());
|
||||
LinkCollectingAssociationHandler associationHandler = new LinkCollectingAssociationHandler(entities, basePath,
|
||||
associationLinks);
|
||||
resource.getPersistentEntity().doWithAssociations(associationHandler);
|
||||
|
||||
for (Link link : associationHandler.getLinks()) {
|
||||
links.add(link);
|
||||
}
|
||||
Links links = collector.getLinksFor(resource.getContent(), resource.getLinks());
|
||||
|
||||
Resource<Object> resourceToRender = new Resource<Object>(resource.getContent(), links) {
|
||||
|
||||
@@ -237,6 +237,8 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is there a default projection?
|
||||
|
||||
if (associationLinks.isLinkableAssociation(persistentProperty)) {
|
||||
continue;
|
||||
}
|
||||
@@ -400,6 +402,222 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
static class ProjectionSerializer extends StdSerializer<TargetAware> {
|
||||
|
||||
private final LinkCollector collector;
|
||||
private final ResourceMappings mappings;
|
||||
private boolean unwrapping;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionSerializer} for the given {@link LinkCollector}.
|
||||
*
|
||||
* @param collector
|
||||
*/
|
||||
public ProjectionSerializer(LinkCollector collector, ResourceMappings mappings) {
|
||||
|
||||
super(TargetAware.class);
|
||||
|
||||
this.collector = collector;
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
public void serialize(TargetAware value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
|
||||
JsonGenerationException {
|
||||
|
||||
Object target = value.getTarget();
|
||||
Links links = mappings.getMetadataFor(value.getTargetClass()).isExported() ? collector.getLinksFor(target)
|
||||
: new Links();
|
||||
|
||||
jgen.writeStartObject();
|
||||
|
||||
provider.//
|
||||
findValueSerializer(ProjectionResource.class, null).//
|
||||
unwrappingSerializer(null).//
|
||||
serialize(new ProjectionResource(value, links), jgen, provider);
|
||||
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isUnwrappingSerializer()
|
||||
*/
|
||||
@Override
|
||||
public boolean isUnwrappingSerializer() {
|
||||
return unwrapping;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#unwrappingSerializer(com.fasterxml.jackson.databind.util.NameTransformer)
|
||||
*/
|
||||
@Override
|
||||
public JsonSerializer<TargetAware> unwrappingSerializer(NameTransformer unwrapper) {
|
||||
this.unwrapping = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static class ProjectionResource extends Resource<ProjectionResourceContent> {
|
||||
|
||||
ProjectionResource(TargetAware projection, Iterable<Link> links) {
|
||||
super(new ProjectionResourceContent(projection, projection.getClass().getInterfaces()[0]), links);
|
||||
}
|
||||
}
|
||||
|
||||
static class ProjectionResourceContent {
|
||||
|
||||
private final Object projection;
|
||||
private final Class<?> projectionInterface;
|
||||
|
||||
/**
|
||||
* @param projection
|
||||
* @param projectionInterface
|
||||
*/
|
||||
public ProjectionResourceContent(Object projection, Class<?> projectionInterface) {
|
||||
this.projection = projection;
|
||||
this.projectionInterface = projectionInterface;
|
||||
}
|
||||
|
||||
public Object getProjection() {
|
||||
return projection;
|
||||
}
|
||||
|
||||
public Class<?> getProjectionInterface() {
|
||||
return projectionInterface;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class ProjectionResourceContentSerializer extends StdSerializer<ProjectionResourceContent> {
|
||||
|
||||
private boolean unwrapping;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionResourceContentSerializer}.
|
||||
*/
|
||||
public ProjectionResourceContentSerializer() {
|
||||
super(ProjectionResourceContent.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
|
||||
*/
|
||||
@Override
|
||||
public void serialize(ProjectionResourceContent value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException, JsonGenerationException {
|
||||
|
||||
provider.//
|
||||
findValueSerializer(value.getProjectionInterface(), null).//
|
||||
unwrappingSerializer(null).//
|
||||
serialize(value.getProjection(), jgen, provider);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isUnwrappingSerializer()
|
||||
*/
|
||||
@Override
|
||||
public boolean isUnwrappingSerializer() {
|
||||
return unwrapping;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#unwrappingSerializer(com.fasterxml.jackson.databind.util.NameTransformer)
|
||||
*/
|
||||
@Override
|
||||
public JsonSerializer<ProjectionResourceContent> unwrappingSerializer(NameTransformer unwrapper) {
|
||||
|
||||
this.unwrapping = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A service to collect all standard links that need to be added to a certain object.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class LinkCollector {
|
||||
|
||||
private final PersistentEntities entities;
|
||||
private final AssociationLinks associationLinks;
|
||||
private final EntityLinks links;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistentEntities}, {@link EntityLinks} and {@link AssociationLinks}.
|
||||
*
|
||||
* @param entities must not be {@literal null}.
|
||||
* @param entityLinks must not be {@literal null}.
|
||||
* @param associationLinks must not be {@literal null}.
|
||||
*/
|
||||
public LinkCollector(PersistentEntities entities, EntityLinks entityLinks, AssociationLinks associationLinks) {
|
||||
|
||||
Assert.notNull(entities, "PersistentEntities must not be null!");
|
||||
Assert.notNull(entityLinks, "EntityLinks must not be null!");
|
||||
Assert.notNull(associationLinks, "AssociationLinks must not be null!");
|
||||
|
||||
this.links = entityLinks;
|
||||
this.entities = entities;
|
||||
this.associationLinks = associationLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link Links} for the given object.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Links getLinksFor(Object object) {
|
||||
return getLinksFor(object, Collections.<Link> emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link Links} for the given object and already existing {@link Link}.
|
||||
*
|
||||
* @param object must not be {@literal null}.
|
||||
* @param existingLinks must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Links getLinksFor(Object object, List<Link> existingLinks) {
|
||||
|
||||
Assert.notNull(object, "Object must not be null!");
|
||||
Assert.notNull(existingLinks, "Existing links must not be null!");
|
||||
|
||||
PersistentEntity<?, ?> entity = entities.getPersistentEntity(object.getClass());
|
||||
|
||||
Link selfLink = getSelfLink(object, entity, new Links(existingLinks));
|
||||
Path path = new Path(selfLink.expand().getHref());
|
||||
|
||||
LinkCollectingAssociationHandler handler = new LinkCollectingAssociationHandler(entities, path, associationLinks);
|
||||
entity.doWithAssociations(handler);
|
||||
|
||||
List<Link> result = new ArrayList<Link>();
|
||||
result.add(getSelfLink(object, entity, new Links(existingLinks)));
|
||||
result.addAll(handler.getLinks());
|
||||
|
||||
return new Links(result);
|
||||
}
|
||||
|
||||
private Link getSelfLink(Object object, PersistentEntity<?, ?> entity, Links existing) {
|
||||
|
||||
if (existing.hasLink(Link.REL_SELF)) {
|
||||
return existing.getLink(Link.REL_SELF);
|
||||
}
|
||||
|
||||
IdentifierAccessor accessor = entity.getIdentifierAccessor(object);
|
||||
return links.linkToSingleResource(entity.getType(), accessor.getIdentifier()).withSelfRel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ValueInstantiator} to create collection or map instances based on the type of the configured
|
||||
* {@link PersistentProperty}.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.jpa;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @soundtrack Elen - Sink like a stone (Elen)
|
||||
*/
|
||||
public interface UserExcerpt {
|
||||
|
||||
UserExcerpt getFather();
|
||||
}
|
||||
@@ -28,6 +28,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.support.Repositories;
|
||||
import org.springframework.data.rest.webmvc.PersistentEntityResource;
|
||||
import org.springframework.data.rest.webmvc.jpa.LineItem;
|
||||
@@ -35,6 +37,7 @@ import org.springframework.data.rest.webmvc.jpa.Order;
|
||||
import org.springframework.data.rest.webmvc.jpa.OrderRepository;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.webmvc.jpa.UserExcerpt;
|
||||
import org.springframework.data.rest.webmvc.mongodb.Address;
|
||||
import org.springframework.data.rest.webmvc.mongodb.User;
|
||||
import org.springframework.data.rest.webmvc.util.TestUtils;
|
||||
@@ -42,6 +45,8 @@ import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.PagedResources;
|
||||
import org.springframework.hateoas.PagedResources.PageMetadata;
|
||||
import org.springframework.hateoas.core.EmbeddedWrapper;
|
||||
import org.springframework.hateoas.core.EmbeddedWrappers;
|
||||
import org.springframework.hateoas.hal.HalLinkDiscoverer;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -49,6 +54,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
/**
|
||||
@@ -71,10 +77,13 @@ public class PersistentEntitySerializationTests {
|
||||
@Autowired OrderRepository orders;
|
||||
|
||||
LinkDiscoverer linkDiscoverer;
|
||||
ProjectionFactory projectionFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
linkDiscoverer = new HalLinkDiscoverer();
|
||||
|
||||
this.linkDiscoverer = new HalLinkDiscoverer();
|
||||
this.projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -191,6 +200,8 @@ public class PersistentEntitySerializationTests {
|
||||
|
||||
String result = mapper.writeValueAsString(persistentEntityResource);
|
||||
|
||||
System.out.println(result);
|
||||
|
||||
assertThat(JsonPath.read(result, "$_embedded.users[*].address"), is(notNullValue()));
|
||||
}
|
||||
|
||||
@@ -218,4 +229,30 @@ public class PersistentEntitySerializationTests {
|
||||
|
||||
assertThat(JsonPath.read(result, "$_embedded.orders[*].lineItems"), is(notNullValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-521
|
||||
*/
|
||||
@Test
|
||||
public void serializesLinksForExcerpts() throws Exception {
|
||||
|
||||
Person oliver = new Person("Oliver August", "Matthews");
|
||||
|
||||
Person dave = new Person("Dave", "Matthews");
|
||||
oliver.setFather(dave);
|
||||
|
||||
UserExcerpt daveExcerpt = projectionFactory.createProjection(UserExcerpt.class, dave);
|
||||
EmbeddedWrapper wrapper = new EmbeddedWrappers(false).wrap(daveExcerpt, "father");
|
||||
|
||||
PersistentEntityResource resource = PersistentEntityResource.//
|
||||
build(oliver, repositories.getPersistentEntity(Person.class)).//
|
||||
withLink(new Link("/people/1")).//
|
||||
withEmbedded(Arrays.asList(wrapper)).//
|
||||
build();
|
||||
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
String result = mapper.writeValueAsString(resource);
|
||||
|
||||
assertThat(JsonPath.read(result, "$_embedded.father[*]._links.self"), is(notNullValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.rest.webmvc.json;
|
||||
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.mockito.Matchers;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -36,6 +40,8 @@ import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.webmvc.mongodb.MongoDbRepositoryConfig;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
@@ -94,8 +100,13 @@ public class RepositoryTestsConfig {
|
||||
|
||||
@Bean
|
||||
public Module persistentEntityModule() {
|
||||
|
||||
EntityLinks entityLinks = mock(EntityLinks.class);
|
||||
when(entityLinks.linkToSingleResource(Matchers.<Class<?>> any(), anyObject())).thenReturn(new Link("/mock/1"));
|
||||
|
||||
return new PersistentEntityJackson2Module(new RepositoryResourceMappings(repositories(), persistentEntities()),
|
||||
persistentEntities(), config(), new UriToEntityConverter(persistentEntities(), defaultConversionService()));
|
||||
persistentEntities(), config(), new UriToEntityConverter(persistentEntities(), defaultConversionService()),
|
||||
entityLinks);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
Reference in New Issue
Block a user