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 863c30381..5c708654f 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 @@ -594,27 +594,53 @@ public class PersistentEntityJackson2Module extends SimpleModule { PersistentEntity entity = entities.getPersistentEntity(object.getClass()); - Link selfLink = getSelfLink(object, entity, new Links(existingLinks)); + Links links = new Links(existingLinks); + Link selfLink = createSelfLink(object, entity, links); + + if (selfLink == null) { + return links; + } + 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))); + List result = new ArrayList(existingLinks); result.addAll(handler.getLinks()); - return new Links(result); + return addSelfLinkIfNecessary(object, entity, result); } - private Link getSelfLink(Object object, PersistentEntity entity, Links existing) { + private Links addSelfLinkIfNecessary(Object object, PersistentEntity entity, List existing) { + + Links result = new Links(existing); + + if (result.hasLink(Link.REL_SELF)) { + return result; + } + + List list = new ArrayList(); + list.add(createSelfLink(object, entity, result)); + list.addAll(existing); + + return new Links(list); + } + + private Link createSelfLink(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(); + Object identifier = accessor.getIdentifier(); + + if (identifier == null) { + return null; + } + + return links.linkToSingleResource(entity.getType(), identifier).withSelfRel(); } } 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 da320a092..11fa91562 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 @@ -48,13 +48,15 @@ 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.mock.web.MockHttpServletRequest; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.util.UriTemplate; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.jayway.jsonpath.JsonPath; /** @@ -82,6 +84,8 @@ public class PersistentEntitySerializationTests { @Before public void setUp() { + RequestContextHolder.setRequestAttributes(new ServletWebRequest(new MockHttpServletRequest())); + this.linkDiscoverer = new HalLinkDiscoverer(); this.projectionFactory = new SpelAwareProxyProjectionFactory(); } @@ -234,9 +238,11 @@ public class PersistentEntitySerializationTests { @Test public void serializesLinksForExcerpts() throws Exception { - Person oliver = new Person("Oliver August", "Matthews"); - Person dave = new Person("Dave", "Matthews"); + dave.setId(1L); + + Person oliver = new Person("Oliver August", "Matthews"); + oliver.setId(2L); oliver.setFather(dave); UserExcerpt daveExcerpt = projectionFactory.createProjection(UserExcerpt.class, dave); @@ -248,9 +254,27 @@ public class PersistentEntitySerializationTests { 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())); } + + /** + * @see DATAREST-521 + */ + @Test + public void rendersAdditionalLinksRegisteredWithResource() throws Exception { + + Person dave = new Person("Dave", "Matthews"); + + PersistentEntityResource resource = PersistentEntityResource.// + build(dave, repositories.getPersistentEntity(Person.class)).// + withLink(new Link("/people/1")).// + withLink(new Link("/aditional", "processed")).// + build(); + + String result = mapper.writeValueAsString(resource); + + assertThat(JsonPath.read(result, "$_links.processed"), 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 7ed0f7a37..0cbe072e2 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,13 +15,12 @@ */ package org.springframework.data.rest.webmvc.json; -import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; +import java.util.Arrays; 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; @@ -38,13 +37,17 @@ import org.springframework.data.rest.webmvc.jpa.JpaRepositoryConfig; import org.springframework.data.rest.webmvc.jpa.Person; import org.springframework.data.rest.webmvc.jpa.PersonRepository; import org.springframework.data.rest.webmvc.mongodb.MongoDbRepositoryConfig; +import org.springframework.data.rest.webmvc.spi.BackendIdConverter; +import org.springframework.data.rest.webmvc.spi.BackendIdConverter.DefaultIdConverter; +import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables; +import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; 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; +import org.springframework.plugin.core.OrderAwarePluginRegistry; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -101,12 +104,13 @@ public class RepositoryTestsConfig { @Bean public Module persistentEntityModule() { - EntityLinks entityLinks = mock(EntityLinks.class); - when(entityLinks.linkToSingleResource(Matchers.> any(), anyObject())).thenReturn(new Link("/mock/1")); + RepositoryResourceMappings mappings = new RepositoryResourceMappings(repositories(), persistentEntities()); + EntityLinks entityLinks = new RepositoryEntityLinks(repositories(), mappings, config(), + mock(PagingAndSortingTemplateVariables.class), + OrderAwarePluginRegistry., BackendIdConverter> create(Arrays.asList(DefaultIdConverter.INSTANCE))); - return new PersistentEntityJackson2Module(new RepositoryResourceMappings(repositories(), persistentEntities()), - persistentEntities(), config(), new UriToEntityConverter(persistentEntities(), defaultConversionService()), - entityLinks); + return new PersistentEntityJackson2Module(mappings, persistentEntities(), config(), new UriToEntityConverter( + persistentEntities(), defaultConversionService()), entityLinks); } @Bean