From c9fa66a9097a5bd00c3b4dc574e0183a49c4be67 Mon Sep 17 00:00:00 2001 From: Hemant Vyas Date: Sat, 18 Nov 2023 17:48:16 +0530 Subject: [PATCH] feat: Allow annotation GrpcClient for target type 'annotation' Add testcases with usage of GrpcClientWrapper meta-annotation. --- .../boot/grpc/client/inject/GrpcClient.java | 2 +- .../metaannotation/GrpcClientWrapper.java | 33 +++ .../inject/GrpcClientMetaAnnotationTest.java | 200 ++++++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/metaannotation/GrpcClientWrapper.java create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientMetaAnnotationTest.java diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClient.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClient.java index 9a7bd4855..539b0fd3f 100644 --- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClient.java +++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClient.java @@ -66,7 +66,7 @@ * * @see GrpcClientBean Add as bean to the {@link ApplicationContext}. */ -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/metaannotation/GrpcClientWrapper.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/metaannotation/GrpcClientWrapper.java new file mode 100644 index 000000000..527be48dc --- /dev/null +++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/metaannotation/GrpcClientWrapper.java @@ -0,0 +1,33 @@ +package net.devh.boot.grpc.client.inject.metaannotation; + + +import io.grpc.ClientInterceptor; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import net.devh.boot.grpc.client.inject.GrpcClient; +import org.springframework.core.annotation.AliasFor; + +@GrpcClient(value = "") +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface GrpcClientWrapper { + + @AliasFor(annotation = GrpcClient.class, attribute = "value") + String value(); + + @AliasFor(annotation = GrpcClient.class, attribute = "interceptors") + Class[] interceptors() default {}; + + @AliasFor(annotation = GrpcClient.class, attribute = "interceptorNames") + String[] interceptorNames() default {}; + + @AliasFor(annotation = GrpcClient.class, attribute = "sortInterceptors") + boolean sortInterceptors() default false; + + String extraParamString(); + + boolean extraParamBoolean() default false; + +} diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientMetaAnnotationTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientMetaAnnotationTest.java new file mode 100644 index 000000000..43ea63cfb --- /dev/null +++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientMetaAnnotationTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2016-2021 Michael Zhang + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.devh.boot.grpc.test.inject; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.grpc.stub.AbstractStub; +import java.lang.reflect.Field; +import javax.annotation.PostConstruct; +import net.devh.boot.grpc.client.inject.metaannotation.GrpcClientWrapper; +import net.devh.boot.grpc.client.stubfactory.StandardJavaGrpcStubFactory; +import net.devh.boot.grpc.client.stubfactory.StubFactory; +import net.devh.boot.grpc.test.config.BaseAutoConfiguration; +import net.devh.boot.grpc.test.config.InProcessConfiguration; +import net.devh.boot.grpc.test.config.ServiceConfiguration; +import net.devh.boot.grpc.test.inject.CustomGrpc.ConstructorAccessibleStub; +import net.devh.boot.grpc.test.inject.CustomGrpc.CustomAccessibleStub; +import net.devh.boot.grpc.test.inject.CustomGrpc.FactoryMethodAccessibleStub; +import net.devh.boot.grpc.test.inject.GrpcClientInjectionTest.TestConfig; +import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceBlockingStub; +import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceFutureStub; +import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceStub; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +/** + * A test checking that the client injection works. + * + * @author Daniel Theuke (daniel.theuke@heuboe.de) + */ +@SpringBootTest +@SpringJUnitConfig(classes = {TestConfig.class, InProcessConfiguration.class, + ServiceConfiguration.class, + BaseAutoConfiguration.class}) +@DirtiesContext +class GrpcClientMetaAnnotationTest { + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + TestServiceStub stub; + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + TestServiceBlockingStub blockingStub; + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + TestServiceFutureStub futureStub; + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + ConstructorAccessibleStub constructorStub; + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + FactoryMethodAccessibleStub factoryMethodStub; + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + CustomAccessibleStub customStub; + + TestServiceStub stubSetted; + TestServiceBlockingStub blockingStubSetted; + TestServiceFutureStub futureStubSetted; + ConstructorAccessibleStub constructorStubSetted; + FactoryMethodAccessibleStub factoryMethodStubSetted; + CustomAccessibleStub customStubSetted; + + @PostConstruct + public void init() { + // Test injection + assertNotNull(this.stub, "stub"); + assertNotNull(this.blockingStub, "blockingStub"); + assertNotNull(this.futureStub, "futureStub"); + assertNotNull(this.constructorStub, "constructorStub"); + assertNotNull(this.factoryMethodStub, "factoryMethodStub"); + assertNotNull(this.customStub, "customStub"); + + assertAnnotationExtraParams("testExtraParamString", true); + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final TestServiceStub stub) { + assertNotNull(stub, "stub"); + this.stubSetted = stub; + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final TestServiceBlockingStub stub) { + assertNotNull(stub, "stub"); + this.blockingStubSetted = stub; + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final TestServiceFutureStub stub) { + assertNotNull(stub, "stub"); + this.futureStubSetted = stub; + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final ConstructorAccessibleStub stub) { + assertNotNull(stub, "stub"); + this.constructorStubSetted = stub; + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final FactoryMethodAccessibleStub stub) { + assertNotNull(stub, "stub"); + this.factoryMethodStubSetted = stub; + } + + @GrpcClientWrapper(value = "test", extraParamString = "testExtraParamString", extraParamBoolean = true) + void inject(final CustomAccessibleStub stub) { + assertNotNull(stub, "stub"); + this.customStubSetted = stub; + } + + @Test + void testAllSet() { + // Field injection + assertNotNull(this.stub, "stub"); + assertNotNull(this.blockingStub, "blockingStub"); + assertNotNull(this.futureStub, "futureStub"); + assertNotNull(this.constructorStub, "constructorStub"); + assertNotNull(this.factoryMethodStub, "factoryMethodStub"); + assertNotNull(this.customStub, "customStub"); + // Setter injection + assertNotNull(this.stubSetted, "stubSetted"); + assertNotNull(this.blockingStubSetted, "blockingStubSetted"); + assertNotNull(this.futureStubSetted, "futureStubSetted"); + assertNotNull(this.constructorStubSetted, "constructorStubSetted"); + assertNotNull(this.factoryMethodStubSetted, "factoryMethodStubSetted"); + assertNotNull(this.customStubSetted, "customStubSetted"); + } + + @Test + void AnnotationExtraParamsNegativeTest() { + + AssertionFailedError exceptionA = assertThrows( + AssertionFailedError.class, + () -> assertAnnotationExtraParams("INCORRECT_VALUE", true), + "failed" + ); + + AssertionFailedError exceptionB = assertThrows( + AssertionFailedError.class, + () -> assertAnnotationExtraParams("testExtraParamString", false), + "failed" + ); + + assertNotNull(exceptionA); + assertNotNull(exceptionB); + } + + private void assertAnnotationExtraParams(String extraParamStringValue, + boolean extraParamBooleanValue) { + final Field[] fields = this.getClass().getDeclaredFields(); + for (final Field field : fields) { + if (field.isAnnotationPresent(GrpcClientWrapper.class)) { + final GrpcClientWrapper annotation = field.getAnnotation(GrpcClientWrapper.class); + assertEquals(extraParamStringValue, annotation.extraParamString()); + assertEquals(extraParamBooleanValue, annotation.extraParamBoolean()); + } + } + } + + @TestConfiguration + public static class TestConfig { + + @Bean + StubFactory customStubFactory() { + return new StandardJavaGrpcStubFactory() { + + @Override + public boolean isApplicable(final Class> stubType) { + return CustomStub.class.isAssignableFrom(stubType); + } + + @Override + protected String getFactoryMethodName() { + return "custom"; + } + + }; + } + + } + +}