From bba665cef4622fd9b81f7215e845914020959a19 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 16 Apr 2015 09:15:48 +0200 Subject: [PATCH] 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. --- .../rest/webmvc/PersistentEntityResource.java | 21 +- .../PersistentEntityResourceAssembler.java | 20 +- .../RepositoryRestMvcConfiguration.java | 4 +- .../json/PersistentEntityJackson2Module.java | 268 ++++++++++++++++-- .../data/rest/webmvc/jpa/UserExcerpt.java | 25 ++ .../PersistentEntitySerializationTests.java | 39 ++- .../webmvc/json/RepositoryTestsConfig.java | 13 +- 7 files changed, 326 insertions(+), 64 deletions(-) create mode 100644 spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/UserExcerpt.java diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java index 45569df50..934490201 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResource.java @@ -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 { private final PersistentEntity entity; private final Resources 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 { * @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 links, - boolean renderAllAssociations, Resources embeddeds) { + Resources embeddeds) { super(content, links); @@ -64,7 +62,6 @@ public class PersistentEntityResource extends Resource { this.entity = entity; this.embeddeds = embeddeds == null ? NO_EMBEDDEDS : embeddeds; - this.enforceAssociationLinks = renderAllAssociations; } /** @@ -118,7 +115,6 @@ public class PersistentEntityResource extends Resource { private final List links = new ArrayList(); private Resources 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 { 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 { * @return */ public PersistentEntityResource build() { - return new PersistentEntityResource(entity, content, links, renderAllAssociationLinks, embeddeds); + return new PersistentEntityResource(entity, content, links, embeddeds); } } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java index 3e147fcff..b5892e951 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/PersistentEntityResourceAssembler.java @@ -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 getExcerptResource(Object entity) { - return new Resource(projector.projectExcerpt(entity), getSelfLinkFor(entity)); - } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index a34973109..0d9cd73be 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -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()); } /** diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java index 0550f6c7c..863c30381 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityJackson2Module.java @@ -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 { - 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 links = new ArrayList(); - 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 resourceToRender = new Resource(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 { + + 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 unwrappingSerializer(NameTransformer unwrapper) { + this.unwrapping = true; + return this; + } + } + + static class ProjectionResource extends Resource { + + ProjectionResource(TargetAware projection, Iterable 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 { + + 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 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. 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 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 result = new ArrayList(); + 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}. diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/UserExcerpt.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/UserExcerpt.java new file mode 100644 index 000000000..592488589 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/UserExcerpt.java @@ -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(); +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java index d9e14c6c2..d9a526a8c 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntitySerializationTests.java @@ -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())); + } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java index 1d792d93e..7ed0f7a37 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java @@ -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.> 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