Skip to content

Commit

Permalink
DATAREST-1256 - Upgraded to Spring HATEOAS 0.25.0.BUILD-SNAPSHOT.
Browse files Browse the repository at this point in the history
Adapted RepositoryRestMvcConfiguration to now create a shared fallback ObjectMapper and avoid to register it as Spring Bean. Adapted test cases that previously were relying on such a bean being present.

Adapted test cases to verify on the correct ALPS document structure (see spring-projects/spring-hateoas#665).
  • Loading branch information
odrotbohm committed Jul 2, 2018
1 parent 06f775a commit 954416a
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 49 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<springdata.solr>3.0.9.BUILD-SNAPSHOT</springdata.solr>
<springdata.cassandra>2.0.9.BUILD-SNAPSHOT</springdata.cassandra>
<springdata.keyvalue>2.0.9.BUILD-SNAPSHOT</springdata.keyvalue>
<spring-hateoas>0.25.0.BUILD-SNAPSHOT</spring-hateoas>

<hibernate.version>4.3.10.Final</hibernate.version>
<jsonpath>1.1.0</jsonpath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static class Config extends RepositoryRestConfigurerAdapter {

@Bean
public LinkDiscoverer alpsLinkDiscoverer() {
return new JsonPathLinkDiscoverer("$.descriptors[?(@.name == '%s')].href",
return new JsonPathLinkDiscoverer("$.descriptor[?(@.name == '%s')].href",
MediaType.valueOf("application/alps+json"));
}

Expand Down Expand Up @@ -101,7 +101,7 @@ public void exposesAlpsCollectionResources() throws Exception {

client.follow(peopleLink, RestMediaTypes.ALPS_JSON)//
.andExpect(jsonPath("$.alps.version").value("1.0"))//
.andExpect(jsonPath("$.alps.descriptors[*].name", hasItems("people", "person")));
.andExpect(jsonPath("$.alps.descriptor[*].name", hasItems("people", "person")));
}

@Test // DATAREST-638
Expand All @@ -112,7 +112,7 @@ public void verifyThatAlpsIsDefaultProfileFormat() throws Exception {

client.follow(peopleLink)//
.andExpect(jsonPath("$.alps.version").value("1.0"))//
.andExpect(jsonPath("$.alps.descriptors[*].name", hasItems("people", "person")));
.andExpect(jsonPath("$.alps.descriptor[*].name", hasItems("people", "person")));

}

Expand All @@ -124,11 +124,11 @@ public void verifyThatAttributesIgnoredDontAppearInAlps() throws Exception {

client.follow(itemsLink, RestMediaTypes.ALPS_JSON)//
// Exposes standard property
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", hasItems("name")))
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", hasItems("name")))
// Does not expose explicitly @JsonIgnored property
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", not(hasItems("owner"))))
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", not(hasItems("owner"))))
// Does not expose properties pointing to non exposed types
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", not(hasItems("manager", "curator"))));
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", not(hasItems("manager", "curator"))));
}

@Test // DATAREST-494
Expand All @@ -140,7 +140,7 @@ public void linksToJsonSchemaFromRepresentationDescriptor() throws Exception {
assertThat(itemsLink).isNotNull();

String result = client.follow(itemsLink, RestMediaTypes.ALPS_JSON).andReturn().getResponse().getContentAsString();
String href = JsonPath.<JSONArray> read(result, "$.alps.descriptors[?(@.id == 'item-representation')].href").get(0)
String href = JsonPath.<JSONArray> read(result, "$.alps.descriptor[?(@.id == 'item-representation')].href").get(0)
.toString();

assertThat(href, endsWith("/profile/items"));
Expand All @@ -153,8 +153,8 @@ public void referenceToAssociatedEntityDesciptorPointsToRepresentationDescriptor
Link usersLink = client.discoverUnique(profileLink, "people", MediaType.ALL);

String jsonPath = "$.alps."; // Root
jsonPath += "descriptors[?(@.id == 'person-representation')]."; // Representation descriptor
jsonPath += "descriptors[?(@.name == 'father')]."; // First father descriptor
jsonPath += "descriptor[?(@.id == 'person-representation')]."; // Representation descriptor
jsonPath += "descriptor[?(@.name == 'father')]."; // First father descriptor
jsonPath += "rt"; // Return type

String result = client.follow(usersLink, RestMediaTypes.ALPS_JSON).andReturn().getResponse().getContentAsString();
Expand All @@ -171,7 +171,7 @@ public void onlyExposesIdAttributesWhenExposedInTheConfiguration() throws Except

client.follow(itemsLink, RestMediaTypes.ALPS_JSON)//
// Exposes identifier if configured to
.andExpect(jsonPath("$.alps.descriptors[*].descriptors[*].name", hasItems("id", "name")));
.andExpect(jsonPath("$.alps.descriptor[*].descriptor[*].name", hasItems("id", "name")));
}

@Test // DATAREST-683
Expand All @@ -186,7 +186,7 @@ public void enumValueListingsAreTranslatedIfEnabled() throws Exception {

String value = JsonPath
.<JSONArray> read(result,
"$.alps.descriptors[?(@.id == 'person-representation')].descriptors[?(@.name == 'gender')].doc.value")
"$.alps.descriptor[?(@.id == 'person-representation')].descriptor[?(@.name == 'gender')].doc.value")
.get(0).toString();

assertThat(value).isEqualTo("Male, Female, Undefined");
Expand All @@ -202,7 +202,7 @@ public void alpsCanHandleGroovyDomainObjects() throws Exception {

String name = JsonPath
.<JSONArray> read(result,
"$.alps.descriptors[?(@.id == 'simulatedGroovyDomainClass-representation')].descriptors[0].name")
"$.alps.descriptor[?(@.id == 'simulatedGroovyDomainClass-representation')].descriptor[0].name")
.get(0).toString();

assertThat(name).isEqualTo("name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
import javax.validation.constraints.NotNull;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
Expand All @@ -41,9 +39,16 @@
import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
Expand All @@ -69,11 +74,18 @@ static class Config {
@Autowired ApplicationContext beanFactory;
@Autowired JpaMetamodelMappingContext mappingContext;
@Autowired AirportRepository repository;
@Autowired @Qualifier("halObjectMapper") ObjectMapper mapper;

ObjectMapper mapper;

@Before
public void setUp() {
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

this.mapper = beanFactory //
.getBean("halJacksonHttpMessageConverter", AbstractJackson2HttpMessageConverter.class) //
.getObjectMapper() //
.copy();

this.mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}

@Test // DATAREST-262
Expand All @@ -87,9 +99,13 @@ public void deserializesNestedAssociation() throws Exception {
}

@Test // DATAREST-262
@Ignore
public void serializesLinksToNestedAssociations() throws Exception {

MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.ACCEPT, MediaTypes.HAL_JSON_VALUE);
RequestAttributes attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);

Airport first = new Airport();
first.id = 1L;

Expand All @@ -112,9 +128,9 @@ public void serializesLinksToNestedAssociations() throws Exception {

String result = mapper.writeValueAsString(resource);

assertThat(JsonPath.<Object> read(result, "$_links.self")).isNotNull();
assertThat(JsonPath.<Object> read(result, "$_links.airport")).isNotNull();
assertThat(JsonPath.<Object> read(result, "$_links.originOrDestinationAirport")).isNotNull();
assertThat(JsonPath.<Object> read(result, "$._links.self")).isNotNull();
assertThat(JsonPath.<Object> read(result, "$._links.originOrDestinationAirport")).isNotNull();
assertThat(JsonPath.<Object> read(result, "$.orgOrDstFlightPart._links.airport")).isNotNull();
}

public interface AircraftMovementRepository extends CrudRepository<AircraftMovement, Long> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
Expand All @@ -33,6 +34,7 @@
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.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -50,18 +52,22 @@
public class Jackson2DatatypeHelperIntegrationTests {

@Autowired PersistentEntities entities;
@Autowired ObjectMapper objectMapper;
@Autowired ApplicationContext context;

@Autowired PersonRepository people;
@Autowired OrderRepository orders;
@Autowired EntityManager em;

ObjectMapper objectMapper;

Order order;

@Before
public void setUp() {

this.order = orders.save(new Order(people.save(new Person("Dave", "Matthews"))));
this.objectMapper = context.getBean("halJacksonHttpMessageConverter", AbstractJackson2HttpMessageConverter.class)
.getObjectMapper();

// Reset JPA to make sure the query returns a result with proxy references
em.flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.junit.Test;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.rest.core.support.DefaultSelfLinkProvider;
import org.springframework.data.rest.core.support.EntityLookup;
Expand All @@ -39,8 +38,6 @@
import org.springframework.hateoas.Links;
import org.springframework.test.context.ContextConfiguration;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Integration tests for {@link PersistentEntityResourceAssembler}.
*
Expand All @@ -51,7 +48,6 @@ public class PersistentEntityResourceAssemblerIntegrationTests extends AbstractC

@Autowired PersistentEntities entities;
@Autowired EntityLinks entityLinks;
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
@Autowired Associations associations;

@Test // DATAREST-609
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ public class PersistentEntityToJsonSchemaConverterUnitTests {
@Autowired @Qualifier("resourceDescriptionMessageSourceAccessor") MessageSourceAccessor accessor;
@Autowired RepositoryRestConfiguration configuration;
@Autowired PersistentEntities entities;
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
@Autowired Associations associations;

ObjectMapper objectMapper = new ObjectMapper();

@Configuration
@Import(RepositoryRestMvcConfiguration.class)
static class TestConfiguration extends RepositoryRestConfigurerAdapter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
Expand Down Expand Up @@ -117,6 +119,7 @@
import org.springframework.data.rest.webmvc.support.PagingAndSortingTemplateVariables;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.data.util.AnnotatedTypeScanner;
import org.springframework.data.util.Lazy;
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
import org.springframework.data.web.HateoasSortHandlerMethodArgumentResolver;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
Expand All @@ -132,6 +135,7 @@
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.hateoas.core.EvoInflectorRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.hateoas.hal.HalConfiguration;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator;
import org.springframework.hateoas.mvc.ResourceProcessorInvoker;
Expand Down Expand Up @@ -189,15 +193,32 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
@Autowired(required = false) List<RepositoryRestConfigurer> configurers = Collections.emptyList();
@Autowired(required = false) List<EntityLookup<?>> lookups = Collections.emptyList();

@Autowired(required = false) RelProvider relProvider;
@Autowired(required = false) CurieProvider curieProvider;
@Autowired Optional<RelProvider> relProvider;
@Autowired Optional<CurieProvider> curieProvider;
@Autowired Optional<HalConfiguration> halConfiguration;
@Autowired ObjectProvider<ObjectMapper> objectMapper;

private final Lazy<ObjectMapper> mapper;

private RepositoryRestConfigurerDelegate configurerDelegate;
private ClassLoader beanClassLoader;

public RepositoryRestMvcConfiguration(ApplicationContext context,
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {

super(context, conversionService);

this.mapper = Lazy.of(() -> {

Jdk8Module jdk8Module = new Jdk8Module();
jdk8Module.configureAbsentsAsNulls(true);

ObjectMapper mapper = basicObjectMapper();
mapper.registerModule(persistentEntityJackson2Module());
mapper.registerModule(jdk8Module);

return mapper;
});
}

/*
Expand Down Expand Up @@ -461,17 +482,8 @@ public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() {
*
* @return
*/
@Bean
public ObjectMapper objectMapper() {

Jdk8Module jdk8Module = new Jdk8Module();
jdk8Module.configureAbsentsAsNulls(true);

ObjectMapper mapper = basicObjectMapper();
mapper.registerModule(persistentEntityJackson2Module());
mapper.registerModule(jdk8Module);

return mapper;
return mapper.get();
}

/**
Expand Down Expand Up @@ -529,13 +541,13 @@ public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageC
return converter;
}

@Bean
public ObjectMapper halObjectMapper() {

RelProvider defaultedRelProvider = this.relProvider != null ? this.relProvider : new EvoInflectorRelProvider();
RelProvider defaultedRelProvider = this.relProvider.orElseGet(() -> new EvoInflectorRelProvider());
HalConfiguration halConfiguration = this.halConfiguration.orElseGet(() -> new HalConfiguration());

HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider, curieProvider,
resourceDescriptionMessageSourceAccessor(), applicationContext.getAutowireCapableBeanFactory());
HalHandlerInstantiator instantiator = new HalHandlerInstantiator(defaultedRelProvider, curieProvider.orElse(null),
resourceDescriptionMessageSourceAccessor(), halConfiguration);

ObjectMapper mapper = basicObjectMapper();
mapper.registerModule(persistentEntityJackson2Module());
Expand Down Expand Up @@ -849,7 +861,8 @@ protected List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() {

protected ObjectMapper basicObjectMapper() {

ObjectMapper objectMapper = new ObjectMapper();
ObjectMapper mapper = this.objectMapper.getIfAvailable();
ObjectMapper objectMapper = mapper == null ? new ObjectMapper() : mapper.copy();

objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Expand Down
Loading

0 comments on commit 954416a

Please sign in to comment.