From 9f62237dd343ec9ee645f8a00c1a4ad10da58540 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Fri, 3 Jul 2020 14:53:59 +0200 Subject: [PATCH] DATAREST-1543 - Deprecate RepositoryRestConfiguration.getCorsRegistry(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slightly tweaked the configuration model to rather handle the CorsRegistry via RepositoryRestConfigurer.configureRepositoryRestConfiguration(…) rather than RepositoryRestConfiguration itself. That allows moving of Spring WebMVC as a dependency in the core module. The original methods exposing access to the CorsRegistry are now still available in deprecated form to not break existing clients. Follow-up ticket: DATAREST-1542. --- .../core/config/RepositoryCorsRegistry.java | 4 ++ .../config/RepositoryRestConfiguration.java | 10 ++- .../RepositoryRestConfigurationUnitTests.java | 17 ----- .../rest/webmvc/jpa/CorsIntegrationTests.java | 4 +- .../webmvc/config/CorsConfigurationAware.java | 37 ++++++++++ .../config/RepositoryRestConfigurer.java | 43 +++++++++++- .../RepositoryRestConfigurerDelegate.java | 15 ++++ .../RepositoryRestMvcConfiguration.java | 16 +++-- .../WebMvcRepositoryRestConfiguration.java | 70 +++++++++++++++++++ 9 files changed, 187 insertions(+), 29 deletions(-) create mode 100644 spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/CorsConfigurationAware.java create mode 100644 spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/WebMvcRepositoryRestConfiguration.java diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryCorsRegistry.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryCorsRegistry.java index 5ab3c61c2..1fcdeed78 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryCorsRegistry.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryCorsRegistry.java @@ -26,7 +26,11 @@ import org.springframework.web.servlet.config.annotation.CorsRegistry; * * @author Mark Paluch * @since 2.6 + * @deprecated since 3.4. Rather implement + * {@code RepositoryRestConfigurer.configureRepositoryRestConfiguration(RepositoryRestConfiguration, CorsRegistry)} + * instead to get access to the registry. */ +@Deprecated public class RepositoryCorsRegistry extends CorsRegistry { /* (non-Javadoc) diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java index 5ce09c895..893d5f7f0 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/config/RepositoryRestConfiguration.java @@ -69,13 +69,13 @@ public class RepositoryRestConfiguration { private ResourceMappingConfiguration repoMappings = new ResourceMappingConfiguration(); private RepositoryDetectionStrategy repositoryDetectionStrategy = RepositoryDetectionStrategies.DEFAULT; private boolean exposeRepositoryMethodsByDefault = true; + private RepositoryCorsRegistry corsRegistry = new RepositoryCorsRegistry(); /** * The {@link RelProvider} to be used to calculate the link relation defaults for repositories. */ private @Getter @Setter @NonNull LinkRelationProvider relProvider = new EvoInflectorLinkRelationProvider(); - private final RepositoryCorsRegistry corsRegistry = new RepositoryCorsRegistry(); private final ProjectionDefinitionConfiguration projectionConfiguration; private final MetadataConfiguration metadataConfiguration; private final EntityLookupConfiguration entityLookupConfiguration; @@ -627,11 +627,19 @@ public class RepositoryRestConfiguration { * @since 2.6 * @see RepositoryCorsRegistry * @see CorsRegistration + * @deprecated since 3.4. Rather implement + * {@code RepositoryRestConfigurer.configureRepositoryRestConfiguration(RepositoryRestConfiguration, CorsRegistry)} + * instead to get access to the registry. */ + @Deprecated public RepositoryCorsRegistry getCorsRegistry() { return corsRegistry; } + protected void setCorsRegistry(RepositoryCorsRegistry corsRegistry) { + this.corsRegistry = corsRegistry; + } + /** * Returns the {@link EntityLookupRegistrar} to create custom {@link EntityLookup} instances registered in the * configuration. diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/RepositoryRestConfigurationUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/RepositoryRestConfigurationUnitTests.java index 1762d10fc..2f025b01a 100755 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/RepositoryRestConfigurationUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/RepositoryRestConfigurationUnitTests.java @@ -18,19 +18,15 @@ package org.springframework.data.rest.core; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.util.Map; - import org.junit.Before; import org.junit.Test; import org.springframework.data.rest.core.config.EnumTranslationConfiguration; import org.springframework.data.rest.core.config.MetadataConfiguration; import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration; -import org.springframework.data.rest.core.config.RepositoryCorsRegistry; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.domain.Profile; import org.springframework.data.rest.core.domain.ProfileRepository; import org.springframework.http.MediaType; -import org.springframework.web.cors.CorsConfiguration; /** * Unit tests for {@link RepositoryRestConfiguration}. @@ -119,19 +115,6 @@ public class RepositoryRestConfigurationUnitTests { assertThat(configuration.isLookupType(Profile.class)).isTrue(); } - @Test // DATAREST-573 - public void configuresCorsProcessing() { - - RepositoryCorsRegistry registry = configuration.getCorsRegistry(); - registry.addMapping("/hello").maxAge(1234); - - Map corsConfigurations = registry.getCorsConfigurations(); - assertThat(corsConfigurations).containsKey("/hello"); - - CorsConfiguration corsConfiguration = corsConfigurations.get("/hello"); - assertThat(corsConfiguration.getMaxAge()).isEqualTo(1234L); - } - @Test // DATAREST-1076 public void rejectsNullRelProvider() { assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> configuration.setRelProvider(null)); diff --git a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/CorsIntegrationTests.java b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/CorsIntegrationTests.java index b93de4c9d..046213930 100755 --- a/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/CorsIntegrationTests.java +++ b/spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/jpa/CorsIntegrationTests.java @@ -51,9 +51,9 @@ public class CorsIntegrationTests extends AbstractWebIntegrationTests { @Bean RepositoryRestConfigurer repositoryRestConfigurer() { - return RepositoryRestConfigurer.withConfig(config -> { + return RepositoryRestConfigurer.withConfig((config, cors) -> { - config.getCorsRegistry().addMapping("/books/**") // + cors.addMapping("/books/**") // .allowedMethods("GET", "PUT", "POST") // .allowedOrigins("http://far.far.example"); }); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/CorsConfigurationAware.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/CorsConfigurationAware.java new file mode 100644 index 000000000..dc3463761 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/CorsConfigurationAware.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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 + * + * https://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 java.util.Map; + +import org.springframework.web.cors.CorsConfiguration; + +/** + * Components that are aware of CORS configuration. + * + * @author Oliver Drotbohm + * @since 3.4 + * @soundtrack Elen - Blind über Rot (Blind über Rot) + */ +public interface CorsConfigurationAware { + + /** + * Return the registered {@link CorsConfiguration} objects, keyed by path pattern. + * + * @return will never be {@literal null}. + */ + Map getCorsConfigurations(); +} 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 d63ccdad5..2afcb662f 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 @@ -16,6 +16,7 @@ package org.springframework.data.rest.webmvc.config; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Consumer; import org.springframework.core.convert.support.ConfigurableConversionService; @@ -23,6 +24,7 @@ import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; +import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; import com.fasterxml.jackson.databind.ObjectMapper; @@ -39,7 +41,7 @@ public interface RepositoryRestConfigurer { /** * Convenience method to easily create simple {@link RepositoryRestConfigurer} instances that solely want to tweak the * {@link RepositoryRestConfiguration}. - * + * * @param consumer must not be {@literal null}. * @return * @since 3.1 @@ -50,7 +52,7 @@ public interface RepositoryRestConfigurer { return new RepositoryRestConfigurer() { - /* + /* * (non-Javadoc) * @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureRepositoryRestConfiguration(org.springframework.data.rest.core.config.RepositoryRestConfiguration) */ @@ -61,13 +63,50 @@ public interface RepositoryRestConfigurer { }; } + /** + * Convenience method to easily create simple {@link RepositoryRestConfigurer} instances that solely want to tweak the + * {@link RepositoryRestConfiguration}. + * + * @param consumer must not be {@literal null}. + * @return + * @since 3.4 + */ + static RepositoryRestConfigurer withConfig(BiConsumer consumer) { + + Assert.notNull(consumer, "Consumer must not be null!"); + + return new RepositoryRestConfigurer() { + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureRepositoryRestConfiguration(org.springframework.data.rest.core.config.RepositoryRestConfiguration, org.springframework.web.servlet.config.annotation.CorsRegistry) + */ + @Override + public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) { + consumer.accept(config, cors); + } + }; + } + /** * Override this method to add additional configuration. * * @param config Main configuration bean. + * @deprecated since 3.4, implement + * {@link #configureRepositoryRestConfiguration(RepositoryRestConfiguration, CorsRegistry)} instead. */ + @Deprecated default void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {} + /** + * Override this method to add additional configuration. + * + * @param config Main configuration bean. + * @param cors CORS configuration. + * @since 3.4 + */ + default void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {} + /** * Override this method to add your own converters. * 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 8a9fb0699..5f665497c 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 @@ -22,6 +22,7 @@ import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; +import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; import com.fasterxml.jackson.databind.ObjectMapper; @@ -61,6 +62,20 @@ class RepositoryRestConfigurerDelegate implements RepositoryRestConfigurer { } } + /* + * (non-Javadoc) + * @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureRepositoryRestConfiguration(org.springframework.data.rest.core.config.RepositoryRestConfiguration, org.springframework.web.servlet.config.annotation.CorsRegistry) + */ + @Override + public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) { + + configureRepositoryRestConfiguration(config); + + for (RepositoryRestConfigurer configurer : delegates) { + configurer.configureRepositoryRestConfiguration(config, cors); + } + } + /* * (non-Javadoc) * @see org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer#configureConversionService(org.springframework.core.convert.support.ConfigurableConversionService) 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 e9a694b0f..9916b3337 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 @@ -59,6 +59,7 @@ import org.springframework.data.rest.core.UriToEntityConverter; import org.springframework.data.rest.core.config.MetadataConfiguration; import org.springframework.data.rest.core.config.Projection; import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration; +import org.springframework.data.rest.core.config.RepositoryCorsRegistry; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.event.AnnotatedEventHandlerInvoker; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; @@ -278,7 +279,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon * Main configuration for the REST exporter. */ @Bean - public RepositoryRestConfiguration repositoryRestConfiguration() { + public T repositoryRestConfiguration() { ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration(); @@ -287,11 +288,13 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon configuration.addProjection(projection); } - RepositoryRestConfiguration config = new RepositoryRestConfiguration(configuration, metadataConfiguration(), - enumTranslator()); - configurerDelegate.configureRepositoryRestConfiguration(config); + RepositoryCorsRegistry registry = new RepositoryCorsRegistry(); - return config; + WebMvcRepositoryRestConfiguration config = new WebMvcRepositoryRestConfiguration(configuration, + metadataConfiguration(), enumTranslator(), registry); + configurerDelegate.configureRepositoryRestConfiguration(config, registry); + + return (T) config; } @Bean @@ -542,8 +545,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon @Bean public AbstractHandlerMapping restHandlerMapping() { - Map corsConfigurations = repositoryRestConfiguration().getCorsRegistry() - .getCorsConfigurations(); + Map corsConfigurations = repositoryRestConfiguration().getCorsConfigurations(); RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), repositoryRestConfiguration(), repositories()); diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/WebMvcRepositoryRestConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/WebMvcRepositoryRestConfiguration.java new file mode 100644 index 000000000..66be8c048 --- /dev/null +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/WebMvcRepositoryRestConfiguration.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 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 + * + * https://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 java.util.Map; + +import org.springframework.data.rest.core.config.EnumTranslationConfiguration; +import org.springframework.data.rest.core.config.MetadataConfiguration; +import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration; +import org.springframework.data.rest.core.config.RepositoryCorsRegistry; +import org.springframework.data.rest.core.config.RepositoryRestConfiguration; +import org.springframework.util.Assert; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; + +/** + * Internal variant of {@link RepositoryRestConfiguration} to also expose the {@link CorsRegistry} via + * {@link CorsConfigurationAware}. + * + * @author Oliver Drotbohm + * @since 3.4 + * @soundtrack Elen - Andre Arcaden (Blind über Rot) + */ +class WebMvcRepositoryRestConfiguration extends RepositoryRestConfiguration implements CorsConfigurationAware { + + /** + * Creates a new {@link WebMvcRepositoryRestConfiguration}. + * + * @param projectionConfiguration must not be {@literal null}. + * @param metadataConfiguration must not be {@literal null}. + * @param enumTranslationConfiguration must not be {@literal null}. + * @param registry must not be {@literal null}. + */ + public WebMvcRepositoryRestConfiguration(ProjectionDefinitionConfiguration projectionConfiguration, // + MetadataConfiguration metadataConfiguration, // + EnumTranslationConfiguration enumTranslationConfiguration, // + RepositoryCorsRegistry registry) { + + super(projectionConfiguration, metadataConfiguration, enumTranslationConfiguration); + + Assert.notNull(registry, "CorsRegistry must not be null!"); + + this.registry = registry; + setCorsRegistry(registry); + } + + private final RepositoryCorsRegistry registry; + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.webmvc.config.CorsConfigurationAware#getCorsConfigurations() + */ + @Override + public Map getCorsConfigurations() { + return registry.getCorsConfigurations(); + } +}