From b04b6ca1acc9cec9660d5fd87c83a1b9352a716f Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Tue, 18 Feb 2025 18:11:28 -0600 Subject: [PATCH] Add ConditionalOnGrpcServerEnabled flag This adds a coarse-grained conditional guard that will disable the server autoconfiguration if `spring.grpc.server.enabled` is set to false or `BindableService` class is not available on the classpath. Signed-off-by: Chris Bono --- .../modules/ROOT/partials/_configprops.adoc | 3 +- .../ConditionalOnGrpcServerEnabled.java | 43 +++++++ .../server/GrpcServerAutoConfiguration.java | 7 +- .../GrpcServerFactoryAutoConfiguration.java | 2 +- ...rpcServerObservationAutoConfiguration.java | 1 + ...GrpcServerReflectionAutoConfiguration.java | 1 + ...GrpcExceptionHandlerAutoConfiguration.java | 6 +- .../GrpcServerHealthAutoConfiguration.java | 2 + .../GrpcSecurityAutoConfiguration.java | 4 +- ...itional-spring-configuration-metadata.json | 6 + .../GrpcServerAutoConfigurationTests.java | 19 +++ ...rverObservationAutoConfigurationTests.java | 27 +++- ...erverReflectionAutoConfigurationTests.java | 19 +++ ...xceptionHandlerAutoConfigurationTests.java | 121 ++++++++++++++++++ ...rpcServerHealthAutoConfigurationTests.java | 20 +++ .../GrpcSecurityAutoConfigurationTests.java | 80 ++++++++++++ 16 files changed, 346 insertions(+), 15 deletions(-) create mode 100644 spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/ConditionalOnGrpcServerEnabled.java create mode 100644 spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfigurationTests.java create mode 100644 spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfigurationTests.java diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc index 7d22345..534518f 100644 --- a/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc +++ b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc @@ -21,6 +21,7 @@ |spring.grpc.client.enabled | `+++true+++` | Whether to enable client autoconfiguration. |spring.grpc.client.observations.enabled | `+++true+++` | Whether to enable Observations on the client. |spring.grpc.server.address | | The address to bind to. could be a host:port combination or a pseudo URL like static://host:port. Can not be set if host or port are set independently. +|spring.grpc.server.enabled | `+++true+++` | Whether to enable server autoconfiguration. |spring.grpc.server.exception-handling.enabled | `+++true+++` | Whether to enable user-defined global exception handling on the gRPC server. |spring.grpc.server.health.actuator.enabled | `+++true+++` | Whether to adapt Actuator health indicators into gRPC health checks. |spring.grpc.server.health.actuator.health-indicator-paths | | List of Actuator health indicator paths to adapt into gRPC health checks. @@ -49,4 +50,4 @@ |spring.grpc.server.ssl.enabled | | Whether to enable SSL support. Enabled automatically if "bundle" is provided unless specified otherwise. |spring.grpc.server.ssl.secure | `+++true+++` | Flag to indicate that client authentication is secure (i.e. certificates are checked). Do not set this to false in production. -|=== \ No newline at end of file +|=== diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/ConditionalOnGrpcServerEnabled.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/ConditionalOnGrpcServerEnabled.java new file mode 100644 index 0000000..036902d --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/ConditionalOnGrpcServerEnabled.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023-2024 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.grpc.autoconfigure.server; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Conditional; + +import io.grpc.BindableService; + +/** + * {@link Conditional @Conditional} that only matches when the + * {@code io.grpc.BindableService} class is on the classpath and the + * {@code spring.grpc.server.enabled} property is not explicitly set to {@code false}. + * + * @author Freeman + * @author Chris Bono + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnClass(BindableService.class) +@ConditionalOnProperty(prefix = "spring.grpc.server", name = "enabled", matchIfMissing = true) +public @interface ConditionalOnGrpcServerEnabled { + +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java index 29abff2..b06324f 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java @@ -17,10 +17,8 @@ package org.springframework.grpc.autoconfigure.server; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; @@ -50,9 +48,8 @@ import io.grpc.ServerBuilder; * @author David Syer * @author Chris Bono */ -@AutoConfiguration -@AutoConfigureAfter(GrpcServerFactoryAutoConfiguration.class) -@ConditionalOnClass(BindableService.class) +@AutoConfiguration(after = GrpcServerFactoryAutoConfiguration.class) +@ConditionalOnGrpcServerEnabled @ConditionalOnBean(BindableService.class) @EnableConfigurationProperties(GrpcServerProperties.class) @Import({ GrpcCodecConfiguration.class }) diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java index 81e55d5..540e575 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java @@ -54,7 +54,7 @@ import io.grpc.servlet.jakarta.ServletServerBuilder; */ @AutoConfiguration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) -@ConditionalOnClass(BindableService.class) +@ConditionalOnGrpcServerEnabled @ConditionalOnBean(BindableService.class) public class GrpcServerFactoryAutoConfiguration { diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java index a59e573..5ae4a9c 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfiguration.java @@ -27,6 +27,7 @@ import io.micrometer.observation.ObservationRegistry; @AutoConfiguration( afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration") +@ConditionalOnGrpcServerEnabled @ConditionalOnClass({ ObservationRegistry.class, ObservationGrpcServerInterceptor.class }) @ConditionalOnBean(ObservationRegistry.class) @ConditionalOnProperty(name = "spring.grpc.server.observation.enabled", havingValue = "true", matchIfMissing = true) diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfiguration.java index dae7acf..6a82eb0 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfiguration.java @@ -19,6 +19,7 @@ import io.grpc.protobuf.services.ProtoReflectionServiceV1; * @author Haris Zujo */ @AutoConfiguration(before = GrpcServerFactoryAutoConfiguration.class) +@ConditionalOnGrpcServerEnabled @ConditionalOnClass(ProtoReflectionService.class) @ConditionalOnProperty(name = "spring.grpc.server.reflection.enabled", havingValue = "true", matchIfMissing = true) public class GrpcServerReflectionAutoConfiguration { diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfiguration.java index b3ccbc2..233995f 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfiguration.java @@ -15,8 +15,6 @@ */ package org.springframework.grpc.autoconfigure.server.exception; -import java.util.stream.Collectors; - import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -24,6 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.grpc.autoconfigure.server.ConditionalOnGrpcServerEnabled; import org.springframework.grpc.server.GlobalServerInterceptor; import org.springframework.grpc.server.exception.CompositeGrpcExceptionHandler; import org.springframework.grpc.server.exception.GrpcExceptionHandler; @@ -32,6 +31,7 @@ import org.springframework.grpc.server.exception.GrpcExceptionHandlerInterceptor import io.grpc.Grpc; @AutoConfiguration +@ConditionalOnGrpcServerEnabled @ConditionalOnClass(Grpc.class) @ConditionalOnBean(GrpcExceptionHandler.class) @ConditionalOnMissingBean(GrpcExceptionHandlerInterceptor.class) @@ -44,7 +44,7 @@ public class GrpcExceptionHandlerAutoConfiguration { public GrpcExceptionHandlerInterceptor globalExceptionHandlerInterceptor( ObjectProvider exceptionHandler) { return new GrpcExceptionHandlerInterceptor(new CompositeGrpcExceptionHandler( - exceptionHandler.orderedStream().collect(Collectors.toList()).toArray(new GrpcExceptionHandler[0]))); + exceptionHandler.orderedStream().toArray(GrpcExceptionHandler[]::new))); } } diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfiguration.java index f9e9dfb..43ccd15 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.grpc.autoconfigure.server.ConditionalOnGrpcServerEnabled; import org.springframework.grpc.autoconfigure.server.GrpcServerFactoryAutoConfiguration; import org.springframework.grpc.autoconfigure.server.GrpcServerProperties; import org.springframework.scheduling.annotation.EnableScheduling; @@ -57,6 +58,7 @@ import io.grpc.protobuf.services.HealthStatusManager; * @author Chris Bono */ @AutoConfiguration(before = GrpcServerFactoryAutoConfiguration.class) +@ConditionalOnGrpcServerEnabled @ConditionalOnClass(HealthStatusManager.class) @ConditionalOnProperty(name = "spring.grpc.server.health.enabled", havingValue = "true", matchIfMissing = true) public class GrpcServerHealthAutoConfiguration { diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfiguration.java index dced3b0..c7b94f5 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfiguration.java @@ -23,6 +23,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.grpc.autoconfigure.server.ConditionalOnGrpcServerEnabled; import org.springframework.grpc.autoconfigure.server.GrpcServerFactoryAutoConfiguration; import org.springframework.grpc.autoconfigure.server.exception.GrpcExceptionHandlerAutoConfiguration; import org.springframework.grpc.server.GlobalServerInterceptor; @@ -40,6 +41,7 @@ import io.grpc.ServerBuilder; import io.grpc.internal.GrpcUtil; @ConditionalOnClass(ObjectPostProcessor.class) +@ConditionalOnGrpcServerEnabled @AutoConfiguration(before = GrpcExceptionHandlerAutoConfiguration.class, after = SecurityAutoConfiguration.class) public class GrpcSecurityAutoConfiguration { @@ -84,4 +86,4 @@ public class GrpcSecurityAutoConfiguration { } -} \ No newline at end of file +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 6ad3d98..773f888 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -5,6 +5,12 @@ "name": "spring.grpc.server.port", "defaultValue": "9090" }, + { + "name": "spring.grpc.server.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable server autoconfiguration.", + "defaultValue": true + }, { "name": "spring.grpc.server.reflection.enabled", "type": "java.lang.Boolean", diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java index 3d597f5..7925430 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java @@ -104,6 +104,25 @@ class GrpcServerAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerAutoConfiguration.class)); } + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner().run((context) -> assertThat(context).hasSingleBean(GrpcServerAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcServerAutoConfiguration.class)); + } + @Test void whenHasUserDefinedServerLifecycleDoesNotAutoConfigureBean() { GrpcServerLifecycle customServerLifecycle = mock(GrpcServerLifecycle.class); diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java index c54e362..5ffcfad 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerObservationAutoConfigurationTests.java @@ -78,13 +78,32 @@ class GrpcServerObservationAutoConfigurationTests { .run(context -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); } + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.validContextRunner() + .run((context) -> assertThat(context).hasSingleBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcServerObservationAutoConfiguration.class)); + } + @Test void whenAllConditionsAreMetThenInterceptorConfiguredAsExpected() { - this.validContextRunner().run((context) -> { - assertThat(context).hasSingleBean(ObservationGrpcServerInterceptor.class) + this.validContextRunner() + .run((context) -> assertThat(context).hasSingleBean(ObservationGrpcServerInterceptor.class) .has(new Condition<>(beans -> beans.getBeansWithAnnotation(GlobalServerInterceptor.class).size() == 1, - "One global interceptor expected")); - }); + "One global interceptor expected"))); } } diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfigurationTests.java index 67e3bba..88fe3a5 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerReflectionAutoConfigurationTests.java @@ -36,4 +36,23 @@ public class GrpcServerReflectionAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(BindableService.class)); } + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(BindableService.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner().run((context) -> assertThat(context).hasSingleBean(BindableService.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(BindableService.class)); + } + } diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfigurationTests.java new file mode 100644 index 0000000..892b12d --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/exception/GrpcExceptionHandlerAutoConfigurationTests.java @@ -0,0 +1,121 @@ +/* + * Copyright 2023-2024 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.grpc.autoconfigure.server.exception; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.grpc.server.exception.GrpcExceptionHandler; +import org.springframework.grpc.server.exception.GrpcExceptionHandlerInterceptor; +import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; + +import io.grpc.Grpc; + +/** + * Tests for {@link GrpcExceptionHandlerAutoConfiguration}. + * + * @author Chris Bono + */ +class GrpcExceptionHandlerAutoConfigurationTests { + + private ApplicationContextRunner contextRunner() { + // NOTE: we use noop server lifecycle to avoid startup + return new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcExceptionHandlerAutoConfiguration.class)) + .withBean("noopServerLifecycle", GrpcServerLifecycle.class, Mockito::mock) + .withBean("mockGrpcExceptionHandler", GrpcExceptionHandler.class, Mockito::mock); + } + + @Test + void whenGrpcNotOnClasspathAutoConfigurationIsSkipped() { + this.contextRunner() + .withClassLoader(new FilteredClassLoader(Grpc.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenNoGrpcExceptionHandlerRegisteredAutoConfigurationIsSkipped() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcExceptionHandlerAutoConfiguration.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenExceptionHandlerPropertyNotSetExceptionHandlerIsAutoConfigured() { + this.contextRunner() + .run((context) -> assertThat(context).hasSingleBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenExceptionHandlerPropertyIsTrueExceptionHandlerIsAutoConfigured() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.exception-handler.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenExceptionHandlerPropertyIsFalseAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.exception-handler.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .run((context) -> assertThat(context).hasSingleBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcExceptionHandlerAutoConfiguration.class)); + } + + @Test + void whenHasUserDefinedGrpcExceptionHandlerInterceptorDoesNotAutoConfigureBean() { + GrpcExceptionHandlerInterceptor customInterceptor = Mockito.mock(); + this.contextRunner() + .withBean("customInterceptor", GrpcExceptionHandlerInterceptor.class, () -> customInterceptor) + .run((context) -> assertThat(context).getBean(GrpcExceptionHandlerInterceptor.class) + .isSameAs(customInterceptor)); + } + + @Test + void exceptionHandlerInterceptorAutoConfiguredAsExpected() { + this.contextRunner() + .run((context) -> assertThat(context).getBean(GrpcExceptionHandlerInterceptor.class) + .extracting("exceptionHandler.exceptionHandlers", + InstanceOfAssertFactories.array(GrpcExceptionHandler[].class)) + .containsExactly(context.getBean(GrpcExceptionHandler.class))); + } + +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java index 2fab869..9ae704c 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java @@ -85,6 +85,26 @@ class GrpcServerHealthAutoConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerHealthAutoConfiguration.class)); } + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerHealthAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .run((context) -> assertThat(context).hasSingleBean(GrpcServerHealthAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcServerHealthAutoConfiguration.class)); + } + @Test void healthIsAutoConfiguredBeforeGrpcServerFactory() { BindableService service = mock(); diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfigurationTests.java new file mode 100644 index 0000000..8595179 --- /dev/null +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcSecurityAutoConfigurationTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2023-2024 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.grpc.autoconfigure.server.security; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.grpc.autoconfigure.server.GrpcServerAutoConfiguration; +import org.springframework.grpc.server.exception.GrpcExceptionHandler; +import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; +import org.springframework.grpc.server.security.SecurityGrpcExceptionHandler; +import org.springframework.security.config.ObjectPostProcessor; + +/** + * Tests for {@link GrpcServerAutoConfiguration}. + * + * @author Chris Bono + */ +class GrpcSecurityAutoConfigurationTests { + + private ApplicationContextRunner contextRunner() { + // NOTE: we use noop server lifecycle to avoid startup + return new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcSecurityAutoConfiguration.class)) + .withBean("noopServerLifecycle", GrpcServerLifecycle.class, Mockito::mock); + } + + @Test + void whenObjectPostProcessorNotOnClasspathAutoConfigurationIsSkipped() { + this.contextRunner() + .withClassLoader(new FilteredClassLoader(ObjectPostProcessor.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcSecurityAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetFalseThenAutoConfigurationIsSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(GrpcSecurityAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertyNotSetThenAutoConfigurationIsNotSkipped() { + this.contextRunner().run((context) -> assertThat(context).hasSingleBean(GrpcSecurityAutoConfiguration.class)); + } + + @Test + void whenServerEnabledPropertySetTrueThenAutoConfigurationIsNotSkipped() { + this.contextRunner() + .withPropertyValues("spring.grpc.server.enabled=true") + .run((context) -> assertThat(context).hasSingleBean(GrpcSecurityAutoConfiguration.class)); + } + + @Test + void grpcSecurityAutoConfiguredAsExpected() { + this.contextRunner() + .run((context) -> assertThat(context).getBean(GrpcExceptionHandler.class) + .isInstanceOf(SecurityGrpcExceptionHandler.class)); + } + +}