diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.java index 306122116..ad1811f78 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.java @@ -20,6 +20,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.http.converter.HttpMessageConverter; @@ -132,4 +133,15 @@ public interface RepositoryRestConfigurer { * @param objectMapper The {@literal ObjectMapper} to be used by the system. */ default void configureJacksonObjectMapper(ObjectMapper objectMapper) {} + + /** + * Customize the {@link AuditableBeanWrapperFactory} to be used. + * + * @param factory will never be {@literal null}. + * @return must not be {@literal null}. + * @since 3.5 + */ + default AuditableBeanWrapperFactory customizeAuditableBeanWrapperFactory(AuditableBeanWrapperFactory factory) { + return factory; + } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurerDelegate.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurerDelegate.java index 7c4000dff..1be406fb0 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurerDelegate.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurerDelegate.java @@ -18,6 +18,7 @@ package org.springframework.data.rest.webmvc.config; import java.util.List; import org.springframework.core.convert.support.ConfigurableConversionService; +import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.http.converter.HttpMessageConverter; @@ -121,4 +122,18 @@ class RepositoryRestConfigurerDelegate implements RepositoryRestConfigurer { configurer.configureValidatingRepositoryEventListener(validatingListener); } } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureAuditableBeanWrapperFactory(org.springframework.data.auditing.AuditableBeanWrapperFactory) + */ + @Override + public AuditableBeanWrapperFactory customizeAuditableBeanWrapperFactory(AuditableBeanWrapperFactory factory) { + + for (RepositoryRestConfigurer configurer : delegates) { + factory = configurer.customizeAuditableBeanWrapperFactory(factory); + } + + return factory; + } } 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 f0b931184..9caf6fc3a 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 @@ -169,6 +169,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon ObjectProvider invoker; ObjectProvider resolver; ObjectProvider geoModule; + ObjectProvider auditingBeanWrapperFactoryProvider; ConversionService defaultConversionService; @@ -855,7 +856,10 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Bean public AuditableBeanWrapperFactory auditableBeanWrapperFactory(PersistentEntities persistentEntities) { - return new MappingAuditableBeanWrapperFactory(persistentEntities); + + AuditableBeanWrapperFactory factory = new MappingAuditableBeanWrapperFactory(persistentEntities); + + return configurerDelegate.get().customizeAuditableBeanWrapperFactory(factory); } @Bean diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java index 0013e697d..690a9c043 100755 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvConfigurationIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.rest.webmvc.config; import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import static org.springframework.data.rest.webmvc.util.AssertionUtils.*; import java.util.Collection; @@ -37,6 +38,7 @@ import org.springframework.context.annotation.Import; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.core.convert.ConversionService; +import org.springframework.data.auditing.AuditableBeanWrapperFactory; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.geo.Distance; @@ -211,6 +213,14 @@ public class RepositoryRestMvConfigurationIntegrationTests { .containsSequence(userConfig.otherConfigurer(), userConfig.configurer()); } + @Test // #2040 + public void appliesAuditingBeanWrapperFactoryCustomizer() { + + AuditableBeanWrapperFactory factory = context.getBean(AuditableBeanWrapperFactory.class); + + assertThat(factory).isEqualTo(ExtendingConfiguration.auditableBeanWrapperFactory); + } + private static ObjectMapper getObjectMapper() { AbstractJackson2HttpMessageConverter converter = context.getBean("halJacksonHttpMessageConverter", @@ -222,6 +232,8 @@ public class RepositoryRestMvConfigurationIntegrationTests { @Import(RepositoryRestMvcConfiguration.class) static class ExtendingConfiguration { + static AuditableBeanWrapperFactory auditableBeanWrapperFactory = mock(AuditableBeanWrapperFactory.class); + @Bean DefaultLinkRelationProvider relProvider() { return new DefaultLinkRelationProvider(); @@ -251,6 +263,19 @@ public class RepositoryRestMvConfigurationIntegrationTests { RepositoryRestConfigurer otherConfigurer() { return RepositoryRestConfigurer.withConfig(__ -> {}); } + + @Bean + @Order(300) // #2040 + RepositoryRestConfigurer auditingBeanWrapperCustomizer() { + + return new RepositoryRestConfigurer() { + + @Override + public AuditableBeanWrapperFactory customizeAuditableBeanWrapperFactory(AuditableBeanWrapperFactory factory) { + return auditableBeanWrapperFactory; + } + }; + } } @Configuration