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 37027ab..46fc5fe 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 @@ -1,3 +1,18 @@ +/* + * Copyright 2024-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 io.grpc.ServerBuilder; @@ -15,18 +30,19 @@ import org.springframework.grpc.server.ServerBuilderCustomizer; afterName = "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration") @ConditionalOnClass(value = { ObservationRegistry.class, ObservationGrpcServerInterceptor.class }) @ConditionalOnBean(ObservationRegistry.class) -@ConditionalOnProperty(value = "spring.grpc.server.observation.enabled", matchIfMissing = true) +@ConditionalOnProperty(name = "spring.grpc.server.observation.enabled", havingValue = "true", matchIfMissing = true) public class GrpcServerObservationAutoConfiguration { + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Bean - public ServerInterceptor observationGrpcServerInterceptor(ObservationRegistry observationRegistry) { + ServerInterceptor observationGrpcServerInterceptor(ObservationRegistry observationRegistry) { return new ObservationGrpcServerInterceptor(observationRegistry); } @Bean - > ServerBuilderCustomizer metricsInterceptor( + > ServerBuilderCustomizer observationGrpcServerInterceptorCustomizer( ServerInterceptor observationGrpcServerInterceptor) { return (serverBuilder) -> serverBuilder.intercept(observationGrpcServerInterceptor); } -} \ No newline at end of file +} diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index a7e2a86..41b6b0d 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-grpc-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ org.springframework.grpc.autoconfigure.server.GrpcServerFactoryAutoConfiguration org.springframework.grpc.autoconfigure.server.GrpcServerAutoConfiguration +org.springframework.grpc.autoconfigure.server.GrpcServerObservationAutoConfiguration org.springframework.grpc.autoconfigure.client.GrpcClientAutoConfiguration 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 2282beb..a974b2b 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 @@ -1,48 +1,98 @@ +/* + * Copyright 2024-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 io.grpc.ServerBuilder; -import io.grpc.ServerInterceptor; +import io.micrometer.core.instrument.binder.grpc.ObservationGrpcServerInterceptor; import io.micrometer.observation.ObservationRegistry; 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.ServerBuilderCustomizer; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +/** + * Tests for the {@link GrpcServerObservationAutoConfiguration}. + */ class GrpcServerObservationAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + private final ApplicationContextRunner baseContextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(GrpcServerObservationAutoConfiguration.class)); - @Test - void whenObservationRegistryNotProvided_thenObservationInterceptorNotConfigured() { - this.contextRunner.run(context -> { - assertThat(context).doesNotHaveBean(ServerBuilderCustomizer.class); - }); + private ApplicationContextRunner validContextRunner() { + return new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcServerObservationAutoConfiguration.class)) + .withBean("observationRegistry", ObservationRegistry.class, Mockito::mock); } @Test - void whenObservationInterceptorConfigured_thenServerBuilderCustomizerConfigured() { - this.contextRunner.withBean(ObservationRegistry.class, ObservationRegistry::create).run(context -> { + void whenObservationRegistryNotOnClasspathAutoConfigSkipped() { + this.validContextRunner() + .withClassLoader(new FilteredClassLoader(ObservationRegistry.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenObservationGrpcServerInterceptorNotOnClasspathAutoConfigSkipped() { + this.validContextRunner() + .withClassLoader(new FilteredClassLoader(ObservationGrpcServerInterceptor.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenObservationRegistryNotProvidedThenAutoConfigSkipped() { + this.baseContextRunner + .run(context -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenObservationPropertyEnabledThenAutoConfigNotSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.server.observation.enabled=true") + .run(context -> assertThat(context).hasSingleBean(GrpcServerObservationAutoConfiguration.class)); + } + + @Test + void whenObservationPropertyDisabledThenAutoConfigIsSkipped() { + this.validContextRunner() + .withPropertyValues("spring.grpc.server.observation.enabled=false") + .run(context -> assertThat(context).doesNotHaveBean(GrpcServerObservationAutoConfiguration.class)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + void whenAllConditionsAreMetThenInterceptorConfiguredAsExpected() { + this.validContextRunner().run((context) -> { + assertThat(context).hasSingleBean(ObservationGrpcServerInterceptor.class); assertThat(context).hasSingleBean(ServerBuilderCustomizer.class); - assertThat(context).hasSingleBean(ServerInterceptor.class); - ServerInterceptor interceptor = context.getBean(ServerInterceptor.class); - ServerBuilderCustomizer customizer = context.getBean(ServerBuilderCustomizer.class); - ServerBuilder builder = org.mockito.Mockito.mock(ServerBuilder.class); - customizer.customize(builder); - org.mockito.Mockito.verify(builder, org.mockito.Mockito.times(1)).intercept(interceptor); + // ensure the customizer in fact adds the interceptor to the builder + ObservationGrpcServerInterceptor serverInterceptor = context + .getBean(ObservationGrpcServerInterceptor.class); + ServerBuilder serverBuilder = mock(); + ServerBuilderCustomizer serverBuilderCustomizer = context.getBean(ServerBuilderCustomizer.class); + serverBuilderCustomizer.customize(serverBuilder); + verify(serverBuilder).intercept(serverInterceptor); }); } - @Test - void whenObservationPropertyDisabled_thenServerBuilderCustomizerNotConfigured() { - this.contextRunner.withPropertyValues("spring.grpc.server.observation.enabled=false") - .withBean(ObservationRegistry.class, ObservationRegistry::create) - .run(context -> { - assertThat(context).doesNotHaveBean(ServerBuilderCustomizer.class); - assertThat(context).doesNotHaveBean(ServerInterceptor.class); - }); - } - -} \ No newline at end of file +}