/*
* Copyright 2013-2017 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.config;
import static org.assertj.core.api.Assertions.*;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.RepositoryLinksResource;
import org.springframework.data.rest.webmvc.RestMediaTypes;
import org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter;
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
import org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.hateoas.LinkDiscoverers;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.core.DefaultRelProvider;
import org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
/**
* Integration tests for basic application bootstrapping (general configuration related checks).
*
* @author Oliver Gierke
*/
public class RepositoryRestMvConfigurationIntegrationTests {
static AbstractApplicationContext context;
@BeforeClass
public static void setUp() {
context = new AnnotationConfigApplicationContext(ExtendingConfiguration.class);
}
@AfterClass
public static void tearDown() {
if (context != null) {
context.close();
}
}
@Test // DATAREST-210
public void assertEnableHypermediaSupportWorkingCorrectly() {
assertThat(context.getBean("entityLinksPluginRegistry")).isNotNull();
assertThat(context.getBean(LinkDiscoverers.class)).isNotNull();
}
@Test
public void assertBeansBeingSetUp() throws Exception {
context.getBean(PageableHandlerMethodArgumentResolver.class);
// Verify HAL setup
context.getBean("halJacksonHttpMessageConverter", HttpMessageConverter.class);
ObjectMapper mapper = context.getBean("halObjectMapper", ObjectMapper.class);
mapper.writeValueAsString(new RepositoryLinksResource());
}
@Test // DATAREST-271
public void assetConsidersPaginationCustomization() {
HateoasPageableHandlerMethodArgumentResolver resolver = context
.getBean(HateoasPageableHandlerMethodArgumentResolver.class);
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
resolver.enhance(builder, null, PageRequest.of(0, 9000, Direction.ASC, "firstname"));
MultiValueMap<String, String> params = builder.build().getQueryParams();
assertThat(params.containsKey("myPage")).isTrue();
assertThat(params.containsKey("mySort")).isTrue();
assertThat(params.get("mySize")).hasSize(1);
assertThat(params.get("mySize").get(0)).isEqualTo("7000");
}
@Test // DATAREST-336
public void objectMapperRendersDatesInIsoByDefault() throws Exception {
Sample sample = new Sample();
sample.date = new Date();
ObjectMapper mapper = context.getBean("objectMapper", ObjectMapper.class);
DateFormatter formatter = new DateFormatter();
formatter.setPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Object result = JsonPath.read(mapper.writeValueAsString(sample), "$.date");
assertThat(result).isInstanceOf(String.class);
assertThat(result).isEqualTo(formatter.print(sample.date, Locale.US));
}
@Test(expected = NoSuchBeanDefinitionException.class) // DATAREST-362
public void doesNotExposePersistentEntityJackson2ModuleAsBean() {
context.getBean(PersistentEntityJackson2Module.class);
}
@Test // DATAREST-362
public void registeredHttpMessageConvertersAreTypeConstrained() {
Collection<MappingJackson2HttpMessageConverter> converters = context
.getBeansOfType(MappingJackson2HttpMessageConverter.class).values();
converters.forEach(converter -> {
assertThat(converter).isInstanceOfAny(TypeConstrainedMappingJackson2HttpMessageConverter.class,
AlpsJsonHttpMessageConverter.class);
});
}
@Test // DATAREST-424
public void halHttpMethodConverterIsRegisteredBeforeTheGeneralOne() {
CollectingComponent component = context.getBean(CollectingComponent.class);
List<HttpMessageConverter<?>> converters = component.converters;
assertThat(converters.get(0).getSupportedMediaTypes()).contains(MediaTypes.HAL_JSON);
assertThat(converters.get(1).getSupportedMediaTypes()).contains(RestMediaTypes.SCHEMA_JSON);
}
@Test // DATAREST-424
public void halHttpMethodConverterIsRegisteredAfterTheGeneralOneIfHalIsDisabledAsDefaultMediaType() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(NonHalConfiguration.class);
CollectingComponent component = context.getBean(CollectingComponent.class);
context.close();
List<HttpMessageConverter<?>> converters = component.converters;
assertThat(converters.get(0).getSupportedMediaTypes()).contains(RestMediaTypes.SCHEMA_JSON);
assertThat(converters.get(1).getSupportedMediaTypes()).contains(MediaTypes.HAL_JSON);
}
@Test // DATAREST-431, DATACMNS-626
public void hasConvertersForPointAndDistance() {
ConversionService service = context.getBean("defaultConversionService", ConversionService.class);
assertThat(service.canConvert(String.class, Point.class)).isTrue();
assertThat(service.canConvert(Point.class, String.class)).isTrue();
assertThat(service.canConvert(String.class, Distance.class)).isTrue();
assertThat(service.canConvert(Distance.class, String.class)).isTrue();
}
@Test // DATAREST-686
public void defaultsEncodingForMessageSourceToUtfEight() {
MessageSourceAccessor accessor = context.getBean("resourceDescriptionMessageSourceAccessor",
MessageSourceAccessor.class);
Object messageSource = ReflectionTestUtils.getField(accessor, "messageSource");
assertThat((String) ReflectionTestUtils.getField(messageSource, "defaultEncoding")).isEqualTo("UTF-8");
}
@Configuration
@Import(RepositoryRestMvcConfiguration.class)
static class ExtendingConfiguration extends RepositoryRestConfigurerAdapter {
@Bean
public DefaultRelProvider relProvider() {
return new DefaultRelProvider();
}
@Bean
public CollectingComponent collectingComponent() {
return new CollectingComponent();
}
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultPageSize(45);
config.setMaxPageSize(7000);
config.setPageParamName("myPage");
config.setLimitParamName("mySize");
config.setSortParamName("mySort");
}
}
@Configuration
@Import(RepositoryRestMvcConfiguration.class)
static class NonHalConfiguration extends RepositoryRestConfigurerAdapter {
@Bean
public CollectingComponent collectingComponent() {
return new CollectingComponent();
}
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.useHalAsDefaultJsonMediaType(false);
}
}
static class Sample {
public Date date;
}
static class CollectingComponent {
List<HttpMessageConverter<?>> converters;
@Autowired
public void setConverters(List<HttpMessageConverter<?>> converters) {
this.converters = converters;
}
}
}