From 978abb1d0d3216f431784bd581967541d4445e85 Mon Sep 17 00:00:00 2001 From: sheheryar aamir Date: Thu, 14 Mar 2024 21:45:42 +0100 Subject: [PATCH 01/10] (fix): Google pubsub protocol schema setting - cloudsteam #651 --- .../annotation/GooglePubSubSchemaSetting.java | 35 ++++++ .../CloudStreamFunctionChannelsScanner.java | 18 ++- .../common/FunctionalChannelBeanBuilder.java | 20 +-- .../common/FunctionalChannelBeanData.java | 4 +- ...lsBindingSchemaSettingIntegrationTest.java | 118 ++++++++++++++++++ ...unctionChannelsScannerIntegrationTest.java | 8 +- .../FunctionalChannelBeanBuilderTest.java | 19 +-- 7 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java create mode 100644 springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java new file mode 100644 index 000000000..0666bee62 --- /dev/null +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.plugins.cloudstream.annotation; + +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD}) +@Inherited +public @interface GooglePubSubSchemaSetting { + /** + * Mapped to {@link GooglePubSubSchemaSettings#getEncoding()} + */ + String encoding() default ""; + + /** + * Mapped to {@link GooglePubSubSchemaSettings#getName()} + */ + String name() default ""; + + /** + * Mapped to {@link GooglePubSubSchemaSettings#getFirstRevisionId()} + */ + String firstRevisionId() default ""; + + /** + * Mapped to {@link GooglePubSubSchemaSettings#getLastRevisionId()} + */ + String lastRevisionId() default ""; +} diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java index 441f22bae..d6ef73b94 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java @@ -5,6 +5,8 @@ import io.github.springwolf.asyncapi.v3.bindings.EmptyChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.EmptyMessageBinding; import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; @@ -20,12 +22,14 @@ import io.github.springwolf.core.asyncapi.scanners.channels.ChannelMerger; import io.github.springwolf.core.configuration.docket.AsyncApiDocket; import io.github.springwolf.core.configuration.docket.AsyncApiDocketService; +import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanData; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.config.BindingServiceProperties; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; @@ -84,7 +88,7 @@ private ChannelObject buildChannel(FunctionalChannelBeanData beanData) { .build(); this.componentsService.registerMessage(message); - Map channelBinding = buildChannelBinding(); + Map channelBinding = buildChannelBinding(beanData.schemaSetting()); return ChannelObject.builder() .bindings(channelBinding) .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message))) @@ -96,8 +100,18 @@ private Map buildMessageBinding() { return Map.of(protocolName, new EmptyMessageBinding()); } - private Map buildChannelBinding() { + private Map buildChannelBinding(Annotation annotation) { String protocolName = getProtocolName(); + if (annotation instanceof GooglePubSubSchemaSetting schemaSetting) { + GooglePubSubChannelBinding googlePubSubChannelBinding = new GooglePubSubChannelBinding(); + GooglePubSubSchemaSettings googlePubSubSchemaSettings = new GooglePubSubSchemaSettings( + schemaSetting.encoding(), + schemaSetting.firstRevisionId(), + schemaSetting.lastRevisionId(), + schemaSetting.name()); + googlePubSubChannelBinding.setSchemaSettings(googlePubSubSchemaSettings); + return Map.of(protocolName, googlePubSubChannelBinding); + } return Map.of(protocolName, new EmptyChannelBinding()); } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java index 0ff74871a..3b2ae153c 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java @@ -2,8 +2,10 @@ package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; +import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import lombok.RequiredArgsConstructor; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.Arrays; @@ -22,35 +24,37 @@ public class FunctionalChannelBeanBuilder { public Set fromMethodBean(Method methodBean) { Class returnType = methodBean.getReturnType(); - + Annotation schemaSetting = methodBean.getAnnotation(GooglePubSubSchemaSetting.class); if (Consumer.class.isAssignableFrom(returnType)) { Class payloadType = getReturnTypeGenerics(methodBean).get(0); - return Set.of(ofConsumer(methodBean.getName(), payloadType)); + return Set.of(ofConsumer(methodBean.getName(), payloadType, schemaSetting)); } if (Supplier.class.isAssignableFrom(returnType)) { Class payloadType = getReturnTypeGenerics(methodBean).get(0); - return Set.of(ofSupplier(methodBean.getName(), payloadType)); + return Set.of(ofSupplier(methodBean.getName(), payloadType, schemaSetting)); } if (Function.class.isAssignableFrom(returnType)) { Class inputType = getReturnTypeGenerics(methodBean).get(0); Class outputType = getReturnTypeGenerics(methodBean).get(1); - return Set.of(ofConsumer(methodBean.getName(), inputType), ofSupplier(methodBean.getName(), outputType)); + return Set.of( + ofConsumer(methodBean.getName(), inputType, schemaSetting), + ofSupplier(methodBean.getName(), outputType, schemaSetting)); } return Collections.emptySet(); } - private static FunctionalChannelBeanData ofConsumer(String name, Class payloadType) { + private static FunctionalChannelBeanData ofConsumer(String name, Class payloadType, Annotation schemaSetting) { return new FunctionalChannelBeanData( - name, payloadType, FunctionalChannelBeanData.BeanType.CONSUMER, name + "-in-0"); + name, payloadType, FunctionalChannelBeanData.BeanType.CONSUMER, name + "-in-0", schemaSetting); } - private static FunctionalChannelBeanData ofSupplier(String name, Class payloadType) { + private static FunctionalChannelBeanData ofSupplier(String name, Class payloadType, Annotation schemaSetting) { return new FunctionalChannelBeanData( - name, payloadType, FunctionalChannelBeanData.BeanType.SUPPLIER, name + "-out-0"); + name, payloadType, FunctionalChannelBeanData.BeanType.SUPPLIER, name + "-out-0", schemaSetting); } private List> getReturnTypeGenerics(Method methodBean) { diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java index 578a3881f..7184ed76c 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java @@ -1,8 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common; +import java.lang.annotation.Annotation; + public record FunctionalChannelBeanData( - String beanName, Class payloadType, BeanType beanType, String cloudStreamBinding) { + String beanName, Class payloadType, BeanType beanType, String cloudStreamBinding, Annotation schemaSetting) { public enum BeanType { CONSUMER, diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java new file mode 100644 index 000000000..2a01eb9e7 --- /dev/null +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.channels; + +import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; +import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessagePayload; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; +import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; +import io.github.springwolf.core.asyncapi.components.DefaultComponentsService; +import io.github.springwolf.core.asyncapi.components.SwaggerSchemaUtil; +import io.github.springwolf.core.asyncapi.components.examples.SchemaWalkerProvider; +import io.github.springwolf.core.asyncapi.components.examples.walkers.DefaultSchemaWalker; +import io.github.springwolf.core.asyncapi.components.examples.walkers.json.ExampleJsonValueGenerator; +import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; +import io.github.springwolf.core.asyncapi.scanners.beans.DefaultBeanMethodsScanner; +import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner; +import io.github.springwolf.core.asyncapi.scanners.classes.spring.ConfigurationClassScanner; +import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; +import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; +import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; +import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; +import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.operations.CloudStreamFunctionOperationsScanner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cloud.stream.config.BindingProperties; +import org.springframework.cloud.stream.config.BindingServiceProperties; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration( + classes = { + ConfigurationClassScanner.class, + ComponentClassScanner.class, + DefaultBeanMethodsScanner.class, + DefaultComponentsService.class, + SwaggerSchemaUtil.class, + PayloadClassExtractor.class, + DefaultSchemaWalker.class, + SchemaWalkerProvider.class, + ExampleJsonValueGenerator.class, + DefaultAsyncApiDocketService.class, + CloudStreamFunctionChannelsScanner.class, + CloudStreamFunctionOperationsScanner.class, + FunctionalChannelBeanBuilder.class, + SpringwolfConfigProperties.class + }) +@TestPropertySource( + properties = { + "springwolf.enabled=true", + "springwolf.docket.info.title=Test", + "springwolf.docket.info.version=1.0.0", + "springwolf.docket.base-package=io.github.springwolf.plugins.cloudstream.asyncapi.scanners.channels", + "springwolf.docket.servers.googlepubsub.protocol=googlepubsub", + "springwolf.docket.servers.googlepubsub.host=kafka:9092", + }) +@EnableConfigurationProperties +@Import(CloudStreamFunctionChannelsScannerIntegrationTest.Configuration.class) +public class CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest { + + @MockBean + private BindingServiceProperties bindingServiceProperties; + + @Autowired + private CloudStreamFunctionChannelsScanner channelsScanner; + + private GooglePubSubSchemaSettings googlePubSubSchemaSettings = + new GooglePubSubSchemaSettings("BINARY", "", "", "project/test"); + private Map channelBindingGooglePubSub = Map.of( + "googlepubsub", new GooglePubSubChannelBinding(null, null, null, googlePubSubSchemaSettings, "0.2.0")); + + @Test + void testConsumerBinding() { + // Given a binding "spring.cloud.stream.bindings.testConsumer-in-0.destination=test-consumer-input-topic" + BindingProperties testConsumerInBinding = new BindingProperties(); + String topicName = "test-consumer-input-topic"; + testConsumerInBinding.setDestination(topicName); + when(bindingServiceProperties.getBindings()) + .thenReturn(Map.of("testPubSubConsumer-in-0", testConsumerInBinding)); + + // When scan is called + Map actualChannels = channelsScanner.scan(); + + // Then the returned channels contain a ChannelItem with the correct data + MessageObject message = MessageObject.builder() + .name(String.class.getName()) + .title(String.class.getSimpleName()) + .payload(MessagePayload.of(MultiFormatSchema.builder() + .schema(SchemaReference.fromSchema(String.class.getSimpleName())) + .build())) + .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))) + .bindings(Map.of("googlepubsub", new GooglePubSubMessageBinding())) + .build(); + + ChannelObject expectedChannel = ChannelObject.builder() + .bindings(channelBindingGooglePubSub) + .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message))) + .build(); + + assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); + } +} diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java index e679a8275..e7e0bed0f 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java @@ -29,6 +29,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; +import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.operations.CloudStreamFunctionOperationsScanner; import org.apache.kafka.streams.kstream.KStream; @@ -431,7 +432,6 @@ void testFunctionBindingWithSameTopicName() { @TestConfiguration public static class Configuration { - @Bean public Consumer testConsumer() { return System.out::println; @@ -451,5 +451,11 @@ public Function testFunction() { public Function, KStream> kStreamTestFunction() { return stream -> stream.mapValues(s -> 1); } + + @Bean + @GooglePubSubSchemaSetting(encoding = "BINARY", name = "project/test") + public Consumer testPubSubConsumer() { + return System.out::println; + } } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java index 69eadfc97..a63f760fb 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java @@ -50,8 +50,8 @@ void testConsumerBean() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly( - new FunctionalChannelBeanData("consumerBean", String.class, CONSUMER, "consumerBean-in-0")); + .containsExactly(new FunctionalChannelBeanData( + "consumerBean", String.class, CONSUMER, "consumerBean-in-0", null)); } @Bean @@ -70,7 +70,7 @@ void testSupplierBean() throws NoSuchMethodException { Assertions.assertThat(data) .containsExactly(new FunctionalChannelBeanData( - "supplierBean", String.class, SUPPLIER, "supplierBean-out-0")); + "supplierBean", String.class, SUPPLIER, "supplierBean-out-0", null)); } @Bean @@ -89,9 +89,10 @@ void testFunctionBean() throws NoSuchMethodException { Assertions.assertThat(data) .containsExactlyInAnyOrder( - new FunctionalChannelBeanData("functionBean", String.class, CONSUMER, "functionBean-in-0"), new FunctionalChannelBeanData( - "functionBean", Integer.class, SUPPLIER, "functionBean-out-0")); + "functionBean", String.class, CONSUMER, "functionBean-in-0", null), + new FunctionalChannelBeanData( + "functionBean", Integer.class, SUPPLIER, "functionBean-out-0", null)); } @Bean @@ -111,8 +112,8 @@ void testConsumerBeanWithGenericPayload() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly( - new FunctionalChannelBeanData(methodName, String.class, CONSUMER, methodName + "-in-0")); + .containsExactly(new FunctionalChannelBeanData( + methodName, String.class, CONSUMER, methodName + "-in-0", null)); } @Bean @@ -132,8 +133,8 @@ void testKafkaStreamsConsumerBean() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly( - new FunctionalChannelBeanData(methodName, String.class, CONSUMER, methodName + "-in-0")); + .containsExactly(new FunctionalChannelBeanData( + methodName, String.class, CONSUMER, methodName + "-in-0", null)); } @Bean From e54f737966caf9b766f58e78990830e8fda6056d Mon Sep 17 00:00:00 2001 From: sheheryar aamir Date: Mon, 18 Mar 2024 22:05:19 +0100 Subject: [PATCH 02/10] (fix): added Google pubsub protocol channel binding - cloudsteam #651 --- settings.gradle | 2 + .../build.gradle | 44 +++++++++++++++++ .../GooglePubSubAsyncChannelBinding.java | 31 ++++++++++++ .../GooglePubSubAsyncSchemaSetting.java | 7 ++- ...GooglePubsubAsyncMessageStoragePolicy.java | 19 ++++++++ ...fGooglePubSubBindingAutoConfiguration.java | 17 +++++++ .../GooglePubSubChannelBindingProcessor.java | 47 +++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...oglePubSubChannelBindingProcessorTest.java | 43 +++++++++++++++++ .../channels/ChannelBindingProcessor.java | 17 +++++++ .../channels/ProcessedChannelBinding.java | 11 +++++ .../common/utils/AsyncAnnotationUtil.java | 13 +++++ .../TestChannelBindingProcessor.java | 44 +++++++++++++++++ .../common/utils/AsyncAnnotationUtilTest.java | 20 ++++++++ .../build.gradle | 1 + .../CloudStreamFunctionChannelsScanner.java | 27 +++++------ .../common/FunctionalChannelBeanBuilder.java | 19 +++----- .../common/FunctionalChannelBeanData.java | 4 +- .../CloudStreamFunctionOperationsScanner.java | 3 +- ...pringwolfCloudStreamAutoConfiguration.java | 9 +++- ...lsBindingSchemaSettingIntegrationTest.java | 21 ++++++++- ...unctionChannelsScannerIntegrationTest.java | 7 --- .../FunctionalChannelBeanBuilderTest.java | 22 ++++----- 23 files changed, 374 insertions(+), 55 deletions(-) create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/build.gradle create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java rename springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java => springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncSchemaSetting.java (85%) create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java create mode 100644 springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java create mode 100644 springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java create mode 100644 springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java diff --git a/settings.gradle b/settings.gradle index 9df2245df..b637d2838 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,6 +17,7 @@ include( 'springwolf-examples:springwolf-sqs-example', 'springwolf-bindings:springwolf-sns-binding', 'springwolf-bindings:springwolf-sqs-binding', + 'springwolf-bindings:springwolf-googlepubsub-binding', 'springwolf-ui', 'springwolf-add-ons:springwolf-common-model-converters', 'springwolf-add-ons:springwolf-generic-binding', @@ -29,3 +30,4 @@ project(':springwolf-plugins:springwolf-jms-plugin').name = 'springwolf-jms' project(':springwolf-plugins:springwolf-kafka-plugin').name = 'springwolf-kafka' project(':springwolf-plugins:springwolf-sns-plugin').name = 'springwolf-sns' project(':springwolf-plugins:springwolf-sqs-plugin').name = 'springwolf-sqs' + diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle b/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle new file mode 100644 index 000000000..1cbacc478 --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java-library' + + id 'org.springframework.boot' + id 'io.spring.dependency-management' + id 'ca.cutterslade.analyze' +} + +dependencies { + api project(":springwolf-asyncapi") + api project(":springwolf-core") + + implementation "org.springframework:spring-context" + implementation "org.springframework:spring-core" + implementation "org.springframework.boot:spring-boot-autoconfigure" + + + testImplementation "org.assertj:assertj-core:${assertjCoreVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" + + testRuntimeOnly "org.junit.jupiter:junit-jupiter:${junitJupiterVersion}" +} + +jar { + enabled = true + archiveClassifier = '' +} +bootJar.enabled = false + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + pom { + name = 'springwolf-googlepubsub-binding' + description = 'Automated JSON API documentation for Google PubSub Bindings' + } + } + } +} diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java new file mode 100644 index 000000000..da79b8471 --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.bindings.googlepubsub.annotations; + +import io.github.springwolf.core.asyncapi.annotations.AsyncOperationBinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@code @GooglePubSubAsyncChannelBinding} is a method-level annotation. + * It configures the channel binding for the Google pubsub protocol. + * @see io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@AsyncOperationBinding +@Inherited +public @interface GooglePubSubAsyncChannelBinding { + String type() default "googlepubsub"; + + String messageRetentionDuration() default ""; + + GooglePubsubAsyncMessageStoragePolicy messageStoragePolicy(); + + GooglePubSubAsyncSchemaSetting schemaSettings(); + + String bindingVersion() default "0.2.0"; +} diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncSchemaSetting.java similarity index 85% rename from springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java rename to springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncSchemaSetting.java index 0666bee62..a83d2a778 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/annotation/GooglePubSubSchemaSetting.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncSchemaSetting.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.plugins.cloudstream.annotation; +package io.github.springwolf.bindings.googlepubsub.annotations; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; @@ -9,10 +9,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * @see GooglePubSubSchemaSettings + */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) @Inherited -public @interface GooglePubSubSchemaSetting { +public @interface GooglePubSubAsyncSchemaSetting { /** * Mapped to {@link GooglePubSubSchemaSettings#getEncoding()} */ diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java new file mode 100644 index 000000000..af0cb29ef --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.bindings.googlepubsub.annotations; + +import io.github.springwolf.core.asyncapi.annotations.AsyncOperationBinding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@AsyncOperationBinding +@Inherited +public @interface GooglePubsubAsyncMessageStoragePolicy { + + String[] allowedPersistenceRegions = null; +} diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java new file mode 100644 index 000000000..3daf4c286 --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.bindings.googlepubsub.configuration; + +import io.github.springwolf.bindings.googlepubsub.scanners.channels.GooglePubSubChannelBindingProcessor; +import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; + +@AutoConfiguration +@ConditionalOnProperty(name = SpringwolfConfigConstants.SPRINGWOLF_ENABLED, havingValue = "true", matchIfMissing = true) +public class SpringwolfGooglePubSubBindingAutoConfiguration { + @Bean + public GooglePubSubChannelBindingProcessor googlePubSubChannelBindingProcessor() { + return new GooglePubSubChannelBindingProcessor(); + } +} diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java new file mode 100644 index 000000000..9c8d920a4 --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.bindings.googlepubsub.scanners.channels; + +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; +import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import org.springframework.context.EmbeddedValueResolverAware; +import org.springframework.util.StringValueResolver; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Optional; + +public class GooglePubSubChannelBindingProcessor implements ChannelBindingProcessor, EmbeddedValueResolverAware { + private StringValueResolver resolver; + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.resolver = resolver; + } + + @Override + public Optional process(Method method) { + return Arrays.stream(method.getAnnotations()) + .filter(GooglePubSubAsyncChannelBinding.class::isInstance) + .map(GooglePubSubAsyncChannelBinding.class::cast) + .findAny() + .map(this::mapToChannelBinding); + } + + private ProcessedChannelBinding mapToChannelBinding(GooglePubSubAsyncChannelBinding bindingAnnotation) { + return new ProcessedChannelBinding( + bindingAnnotation.type(), + new GooglePubSubChannelBinding( + null, + null, + null, + new GooglePubSubSchemaSettings( + bindingAnnotation.schemaSettings().encoding(), + bindingAnnotation.schemaSettings().firstRevisionId(), + bindingAnnotation.schemaSettings().lastRevisionId(), + bindingAnnotation.schemaSettings().name()), + bindingAnnotation.bindingVersion())); + } +} diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..6badfd59b --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration \ No newline at end of file diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java new file mode 100644 index 000000000..6725aa1ce --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubsubAsyncMessageStoragePolicy; +import io.github.springwolf.bindings.googlepubsub.scanners.channels.GooglePubSubChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GooglePubSubChannelBindingProcessorTest { + private final GooglePubSubChannelBindingProcessor processor = new GooglePubSubChannelBindingProcessor(); + + @Test + void processTest() throws NoSuchMethodException { + Method method = GooglePubSubChannelBindingProcessorTest.class.getMethod("methodWithAnnotation"); + + ProcessedChannelBinding binding = processor.process(method).get(); + + assertThat(binding.getType()).isEqualTo("googlepubsub"); + assertThat(binding.getBinding()).isEqualTo(new GooglePubSubChannelBinding()); + } + + @Test + void processWithoutAnnotationTest() throws NoSuchMethodException { + Method method = GooglePubSubChannelBindingProcessorTest.class.getMethod("methodWithoutAnnotation"); + + Optional binding = processor.process(method); + + assertThat(binding).isNotPresent(); + } + + @GooglePubSubAsyncChannelBinding( + messageStoragePolicy = @GooglePubsubAsyncMessageStoragePolicy, + schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) + public void methodWithAnnotation() {} + + public void methodWithoutAnnotation() {} +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java new file mode 100644 index 000000000..a6ec14dbd --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.channels; + +import java.lang.reflect.Method; +import java.util.Optional; + +public interface ChannelBindingProcessor { + + /** + * Process the methods annotated with Channel Binding Annotation + * for protocol specific channelBinding annotations, method parameters, etc + * + * @param method The method being annotated + * @return A message binding, if found + */ + Optional process(Method method); +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java new file mode 100644 index 000000000..d5f6ddfca --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.channels; + +import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; +import lombok.Data; + +@Data +public class ProcessedChannelBinding { + private final String type; + private final ChannelBinding binding; +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java index 1a99e322d..160d3cb1b 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.scanners.common.utils; +import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; @@ -12,6 +13,8 @@ import io.github.springwolf.core.asyncapi.scanners.bindings.messages.ProcessedMessageBinding; import io.github.springwolf.core.asyncapi.scanners.bindings.operations.OperationBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.operations.ProcessedOperationBinding; +import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -136,4 +139,14 @@ public static void processAsyncMessageAnnotation( public static List getServers(AsyncOperation op, StringValueResolver resolver) { return Arrays.stream(op.servers()).map(resolver::resolveStringValue).toList(); } + + public static Map processChannelBindingFromAnnotation( + Method method, List channelBindingProcessors) { + return channelBindingProcessors.stream() + .map(channelBindingProcessor -> channelBindingProcessor.process(method)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toMap( + ProcessedChannelBinding::getType, ProcessedChannelBinding::getBinding, (e1, e2) -> e1)); + } } diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java new file mode 100644 index 000000000..d0d43607e --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.bindings.processor; + +import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.EmptyChannelBinding; +import io.github.springwolf.core.asyncapi.scanners.bindings.BindingProcessorPriority; +import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import org.springframework.core.annotation.Order; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Optional; + +@Order(value = BindingProcessorPriority.MANUAL_DEFINED) +public class TestChannelBindingProcessor implements ChannelBindingProcessor { + + public static final String TYPE = "testType"; + public static final ChannelBinding BINDING = new EmptyChannelBinding(); + + @Override + public Optional process(Method method) { + return Arrays.stream(method.getAnnotations()) + .filter(annotation -> annotation instanceof TestChannelBindingProcessor.TestChannelBinding) + .map(annotation -> (TestChannelBindingProcessor.TestChannelBinding) annotation) + .findAny() + .map(this::mapToChannelBinding); + } + + private ProcessedChannelBinding mapToChannelBinding( + TestChannelBindingProcessor.TestChannelBinding bindingAnnotation) { + return new ProcessedChannelBinding(TYPE, BINDING); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) + @Inherited + public @interface TestChannelBinding {} +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtilTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtilTest.java index 5e643a3bf..bbbb726f3 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtilTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtilTest.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.scanners.common.utils; +import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; @@ -9,6 +10,7 @@ import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestAbstractOperationBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestMessageBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.processor.TestOperationBindingProcessor; import org.assertj.core.util.Maps; @@ -178,6 +180,24 @@ void getServers() throws NoSuchMethodException { assertEquals(List.of("server1"), servers); } + @Test + void processChannelBindingFromAnnotation() throws NoSuchMethodException { + // given + Method m = ClassWithChannelBindingProcessor.class.getDeclaredMethod("methodWithAnnotation", String.class); + + // when + Map bindings = AsyncAnnotationUtil.processChannelBindingFromAnnotation( + m, Collections.singletonList(new TestChannelBindingProcessor())); + + // then + assertEquals(Maps.newHashMap(TestChannelBindingProcessor.TYPE, TestChannelBindingProcessor.BINDING), bindings); + } + + private static class ClassWithChannelBindingProcessor { + @TestChannelBindingProcessor.TestChannelBinding() + private void methodWithAnnotation(String payload) {} + } + private static class ClassWithOperationBindingProcessor { @AsyncListener( operation = diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle index a36e98644..277483d8d 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle @@ -19,6 +19,7 @@ dependencyManagement { dependencies { api project(":springwolf-asyncapi") api project(":springwolf-core") + testImplementation project(":springwolf-bindings:springwolf-googlepubsub-binding") implementation "org.slf4j:slf4j-api:${slf4jApiVersion}" diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java index d6ef73b94..69170898c 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java @@ -5,8 +5,6 @@ import io.github.springwolf.asyncapi.v3.bindings.EmptyChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.EmptyMessageBinding; import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; @@ -19,18 +17,19 @@ import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; import io.github.springwolf.core.asyncapi.scanners.ChannelsScanner; import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner; +import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.channels.ChannelMerger; +import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil; import io.github.springwolf.core.configuration.docket.AsyncApiDocket; import io.github.springwolf.core.configuration.docket.AsyncApiDocketService; -import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanData; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.stream.config.BindingServiceProperties; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.List; import java.util.Map; import java.util.Set; @@ -43,6 +42,7 @@ public class CloudStreamFunctionChannelsScanner implements ChannelsScanner { private final ComponentsService componentsService; private final BindingServiceProperties cloudStreamBindingsProperties; private final FunctionalChannelBeanBuilder functionalChannelBeanBuilder; + protected final List channelBindingProcessors; @Override public Map scan() { @@ -88,7 +88,7 @@ private ChannelObject buildChannel(FunctionalChannelBeanData beanData) { .build(); this.componentsService.registerMessage(message); - Map channelBinding = buildChannelBinding(beanData.schemaSetting()); + Map channelBinding = buildChannelBinding(beanData.method()); return ChannelObject.builder() .bindings(channelBinding) .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message))) @@ -100,18 +100,13 @@ private Map buildMessageBinding() { return Map.of(protocolName, new EmptyMessageBinding()); } - private Map buildChannelBinding(Annotation annotation) { - String protocolName = getProtocolName(); - if (annotation instanceof GooglePubSubSchemaSetting schemaSetting) { - GooglePubSubChannelBinding googlePubSubChannelBinding = new GooglePubSubChannelBinding(); - GooglePubSubSchemaSettings googlePubSubSchemaSettings = new GooglePubSubSchemaSettings( - schemaSetting.encoding(), - schemaSetting.firstRevisionId(), - schemaSetting.lastRevisionId(), - schemaSetting.name()); - googlePubSubChannelBinding.setSchemaSettings(googlePubSubSchemaSettings); - return Map.of(protocolName, googlePubSubChannelBinding); + private Map buildChannelBinding(Method method) { + Map channelBindingMap = + AsyncAnnotationUtil.processChannelBindingFromAnnotation(method, channelBindingProcessors); + if (!channelBindingMap.isEmpty()) { + return channelBindingMap; } + String protocolName = getProtocolName(); return Map.of(protocolName, new EmptyChannelBinding()); } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java index 3b2ae153c..35c2375c1 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilder.java @@ -2,10 +2,8 @@ package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; -import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import lombok.RequiredArgsConstructor; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.Arrays; @@ -24,37 +22,34 @@ public class FunctionalChannelBeanBuilder { public Set fromMethodBean(Method methodBean) { Class returnType = methodBean.getReturnType(); - Annotation schemaSetting = methodBean.getAnnotation(GooglePubSubSchemaSetting.class); if (Consumer.class.isAssignableFrom(returnType)) { Class payloadType = getReturnTypeGenerics(methodBean).get(0); - return Set.of(ofConsumer(methodBean.getName(), payloadType, schemaSetting)); + return Set.of(ofConsumer(methodBean, payloadType)); } if (Supplier.class.isAssignableFrom(returnType)) { Class payloadType = getReturnTypeGenerics(methodBean).get(0); - return Set.of(ofSupplier(methodBean.getName(), payloadType, schemaSetting)); + return Set.of(ofSupplier(methodBean, payloadType)); } if (Function.class.isAssignableFrom(returnType)) { Class inputType = getReturnTypeGenerics(methodBean).get(0); Class outputType = getReturnTypeGenerics(methodBean).get(1); - return Set.of( - ofConsumer(methodBean.getName(), inputType, schemaSetting), - ofSupplier(methodBean.getName(), outputType, schemaSetting)); + return Set.of(ofConsumer(methodBean, inputType), ofSupplier(methodBean, outputType)); } return Collections.emptySet(); } - private static FunctionalChannelBeanData ofConsumer(String name, Class payloadType, Annotation schemaSetting) { + private static FunctionalChannelBeanData ofConsumer(Method method, Class payloadType) { return new FunctionalChannelBeanData( - name, payloadType, FunctionalChannelBeanData.BeanType.CONSUMER, name + "-in-0", schemaSetting); + method, payloadType, FunctionalChannelBeanData.BeanType.CONSUMER, method.getName() + "-in-0"); } - private static FunctionalChannelBeanData ofSupplier(String name, Class payloadType, Annotation schemaSetting) { + private static FunctionalChannelBeanData ofSupplier(Method method, Class payloadType) { return new FunctionalChannelBeanData( - name, payloadType, FunctionalChannelBeanData.BeanType.SUPPLIER, name + "-out-0", schemaSetting); + method, payloadType, FunctionalChannelBeanData.BeanType.SUPPLIER, method.getName() + "-out-0"); } private List> getReturnTypeGenerics(Method methodBean) { diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java index 7184ed76c..4d7a42be4 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common; -import java.lang.annotation.Annotation; +import java.lang.reflect.Method; public record FunctionalChannelBeanData( - String beanName, Class payloadType, BeanType beanType, String cloudStreamBinding, Annotation schemaSetting) { + Method method, Class payloadType, BeanType beanType, String cloudStreamBinding) { public enum BeanType { CONSUMER, diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java index a73d89672..080d263c2 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java @@ -124,6 +124,7 @@ private String buildOperationId(FunctionalChannelBeanData beanData, String chann String operationName = beanData.beanType() == FunctionalChannelBeanData.BeanType.CONSUMER ? "publish" : "subscribe"; - return String.format("%s_%s_%s", channelName, operationName, beanData.beanName()); + return String.format( + "%s_%s_%s", channelName, operationName, beanData.method().getName()); } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java index 77ed475b8..89d924d48 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java @@ -3,6 +3,7 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService; import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner; +import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; import io.github.springwolf.core.configuration.docket.AsyncApiDocketService; import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants; @@ -14,6 +15,8 @@ import org.springframework.cloud.stream.config.BindingServiceProperties; import org.springframework.context.annotation.Bean; +import java.util.List; + /** * Autoconfiguration for the springwolf cloudstream plugin. */ @@ -27,13 +30,15 @@ public CloudStreamFunctionChannelsScanner cloudStreamFunctionChannelsScanner( BeanMethodsScanner beanMethodsScanner, ComponentsService componentsService, BindingServiceProperties cloudstreamBindingServiceProperties, - FunctionalChannelBeanBuilder functionalChannelBeanBuilder) { + FunctionalChannelBeanBuilder functionalChannelBeanBuilder, + List channelBindingProcessors) { return new CloudStreamFunctionChannelsScanner( asyncApiDocketService, beanMethodsScanner, componentsService, cloudstreamBindingServiceProperties, - functionalChannelBeanBuilder); + functionalChannelBeanBuilder, + channelBindingProcessors); } @Bean diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java index 2a01eb9e7..31da228bf 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java @@ -12,6 +12,10 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubsubAsyncMessageStoragePolicy; +import io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration; import io.github.springwolf.core.asyncapi.components.DefaultComponentsService; import io.github.springwolf.core.asyncapi.components.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.components.examples.SchemaWalkerProvider; @@ -30,15 +34,18 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cloud.stream.config.BindingProperties; import org.springframework.cloud.stream.config.BindingServiceProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Map; +import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @@ -46,6 +53,7 @@ @ExtendWith(SpringExtension.class) @ContextConfiguration( classes = { + SpringwolfGooglePubSubBindingAutoConfiguration.class, ConfigurationClassScanner.class, ComponentClassScanner.class, DefaultBeanMethodsScanner.class, @@ -71,7 +79,7 @@ "springwolf.docket.servers.googlepubsub.host=kafka:9092", }) @EnableConfigurationProperties -@Import(CloudStreamFunctionChannelsScannerIntegrationTest.Configuration.class) +@Import(CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.Configuration.class) public class CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest { @MockBean @@ -115,4 +123,15 @@ void testConsumerBinding() { assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); } + + @TestConfiguration + public static class Configuration { + @Bean + @GooglePubSubAsyncChannelBinding( + messageStoragePolicy = @GooglePubsubAsyncMessageStoragePolicy(), + schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) + public Consumer testPubSubConsumer() { + return System.out::println; + } + } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java index e7e0bed0f..9005d07f1 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java @@ -29,7 +29,6 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; -import io.github.springwolf.plugins.cloudstream.annotation.GooglePubSubSchemaSetting; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.operations.CloudStreamFunctionOperationsScanner; import org.apache.kafka.streams.kstream.KStream; @@ -451,11 +450,5 @@ public Function testFunction() { public Function, KStream> kStreamTestFunction() { return stream -> stream.mapValues(s -> 1); } - - @Bean - @GooglePubSubSchemaSetting(encoding = "BINARY", name = "project/test") - public Consumer testPubSubConsumer() { - return System.out::println; - } } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java index a63f760fb..599a2135d 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanBuilderTest.java @@ -50,8 +50,8 @@ void testConsumerBean() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly(new FunctionalChannelBeanData( - "consumerBean", String.class, CONSUMER, "consumerBean-in-0", null)); + .containsExactly( + new FunctionalChannelBeanData(method, String.class, CONSUMER, "consumerBean-in-0")); } @Bean @@ -69,8 +69,8 @@ void testSupplierBean() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly(new FunctionalChannelBeanData( - "supplierBean", String.class, SUPPLIER, "supplierBean-out-0", null)); + .containsExactly( + new FunctionalChannelBeanData(method, String.class, SUPPLIER, "supplierBean-out-0")); } @Bean @@ -89,10 +89,8 @@ void testFunctionBean() throws NoSuchMethodException { Assertions.assertThat(data) .containsExactlyInAnyOrder( - new FunctionalChannelBeanData( - "functionBean", String.class, CONSUMER, "functionBean-in-0", null), - new FunctionalChannelBeanData( - "functionBean", Integer.class, SUPPLIER, "functionBean-out-0", null)); + new FunctionalChannelBeanData(method, String.class, CONSUMER, "functionBean-in-0"), + new FunctionalChannelBeanData(method, Integer.class, SUPPLIER, "functionBean-out-0")); } @Bean @@ -112,8 +110,8 @@ void testConsumerBeanWithGenericPayload() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly(new FunctionalChannelBeanData( - methodName, String.class, CONSUMER, methodName + "-in-0", null)); + .containsExactly( + new FunctionalChannelBeanData(method, String.class, CONSUMER, methodName + "-in-0")); } @Bean @@ -133,8 +131,8 @@ void testKafkaStreamsConsumerBean() throws NoSuchMethodException { Set data = functionalChannelBeanBuilder.fromMethodBean(method); Assertions.assertThat(data) - .containsExactly(new FunctionalChannelBeanData( - methodName, String.class, CONSUMER, methodName + "-in-0", null)); + .containsExactly( + new FunctionalChannelBeanData(method, String.class, CONSUMER, methodName + "-in-0")); } @Bean From e2bc1178b98a2a1276449b63434b4dbbbd75229f Mon Sep 17 00:00:00 2001 From: sheheryar aamir Date: Tue, 19 Mar 2024 12:45:03 +0100 Subject: [PATCH 03/10] (fix): fix annotation and test for google pubsub binding --- .../GooglePubSubAsyncChannelBinding.java | 5 +---- ...GooglePubsubAsyncMessageStoragePolicy.java | 6 +----- .../GooglePubSubChannelBindingProcessor.java | 21 ++++++++++++------- ...oglePubSubChannelBindingProcessorTest.java | 13 +++++++++--- ...lsBindingSchemaSettingIntegrationTest.java | 11 +++++++--- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java index da79b8471..bf7543a1f 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubSubAsyncChannelBinding.java @@ -1,8 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.bindings.googlepubsub.annotations; -import io.github.springwolf.core.asyncapi.annotations.AsyncOperationBinding; - import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; @@ -16,14 +14,13 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -@AsyncOperationBinding @Inherited public @interface GooglePubSubAsyncChannelBinding { String type() default "googlepubsub"; String messageRetentionDuration() default ""; - GooglePubsubAsyncMessageStoragePolicy messageStoragePolicy(); + GooglePubsubAsyncMessageStoragePolicy messageStoragePolicy() default @GooglePubsubAsyncMessageStoragePolicy; GooglePubSubAsyncSchemaSetting schemaSettings(); diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java index af0cb29ef..6e2e69597 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/annotations/GooglePubsubAsyncMessageStoragePolicy.java @@ -1,8 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.bindings.googlepubsub.annotations; -import io.github.springwolf.core.asyncapi.annotations.AsyncOperationBinding; - import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; @@ -11,9 +9,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -@AsyncOperationBinding @Inherited public @interface GooglePubsubAsyncMessageStoragePolicy { - - String[] allowedPersistenceRegions = null; + String[] allowedPersistenceRegions() default {}; } diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java index 9c8d920a4..4e005b7a5 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java @@ -2,6 +2,7 @@ package io.github.springwolf.bindings.googlepubsub.scanners.channels; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageStoragePolicy; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; @@ -31,17 +32,23 @@ public Optional process(Method method) { } private ProcessedChannelBinding mapToChannelBinding(GooglePubSubAsyncChannelBinding bindingAnnotation) { + + GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = new GooglePubSubMessageStoragePolicy( + Arrays.stream(bindingAnnotation.messageStoragePolicy().allowedPersistenceRegions()) + .toList()); + GooglePubSubSchemaSettings googlePubSubSchemaSettings = new GooglePubSubSchemaSettings( + bindingAnnotation.schemaSettings().encoding(), + bindingAnnotation.schemaSettings().firstRevisionId(), + bindingAnnotation.schemaSettings().lastRevisionId(), + bindingAnnotation.schemaSettings().name()); + return new ProcessedChannelBinding( bindingAnnotation.type(), new GooglePubSubChannelBinding( null, - null, - null, - new GooglePubSubSchemaSettings( - bindingAnnotation.schemaSettings().encoding(), - bindingAnnotation.schemaSettings().firstRevisionId(), - bindingAnnotation.schemaSettings().lastRevisionId(), - bindingAnnotation.schemaSettings().name()), + bindingAnnotation.messageRetentionDuration(), + googlePubsubMessageStoragePolicy, + googlePubSubSchemaSettings, bindingAnnotation.bindingVersion())); } } diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java index 6725aa1ce..3671447be 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java @@ -1,13 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageStoragePolicy; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; -import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubsubAsyncMessageStoragePolicy; import io.github.springwolf.bindings.googlepubsub.scanners.channels.GooglePubSubChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; +import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -22,7 +24,13 @@ void processTest() throws NoSuchMethodException { ProcessedChannelBinding binding = processor.process(method).get(); assertThat(binding.getType()).isEqualTo("googlepubsub"); - assertThat(binding.getBinding()).isEqualTo(new GooglePubSubChannelBinding()); + GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = + new GooglePubSubMessageStoragePolicy(List.of()); + GooglePubSubSchemaSettings googlePubSubSchemaSettings = + new GooglePubSubSchemaSettings("BINARY", "", "", "project/test"); + assertThat(binding.getBinding()) + .isEqualTo(new GooglePubSubChannelBinding( + null, "", googlePubsubMessageStoragePolicy, googlePubSubSchemaSettings, "0.2.0")); } @Test @@ -35,7 +43,6 @@ void processWithoutAnnotationTest() throws NoSuchMethodException { } @GooglePubSubAsyncChannelBinding( - messageStoragePolicy = @GooglePubsubAsyncMessageStoragePolicy, schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) public void methodWithAnnotation() {} diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java index 31da228bf..7f877dc0b 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java @@ -4,6 +4,7 @@ import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageBinding; +import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageStoragePolicy; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; @@ -14,7 +15,6 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; -import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubsubAsyncMessageStoragePolicy; import io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration; import io.github.springwolf.core.asyncapi.components.DefaultComponentsService; import io.github.springwolf.core.asyncapi.components.SwaggerSchemaUtil; @@ -44,6 +44,7 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -90,8 +91,13 @@ public class CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest { private GooglePubSubSchemaSettings googlePubSubSchemaSettings = new GooglePubSubSchemaSettings("BINARY", "", "", "project/test"); + + private GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = + new GooglePubSubMessageStoragePolicy(List.of()); private Map channelBindingGooglePubSub = Map.of( - "googlepubsub", new GooglePubSubChannelBinding(null, null, null, googlePubSubSchemaSettings, "0.2.0")); + "googlepubsub", + new GooglePubSubChannelBinding( + null, "", googlePubsubMessageStoragePolicy, googlePubSubSchemaSettings, "0.2.0")); @Test void testConsumerBinding() { @@ -128,7 +134,6 @@ void testConsumerBinding() { public static class Configuration { @Bean @GooglePubSubAsyncChannelBinding( - messageStoragePolicy = @GooglePubsubAsyncMessageStoragePolicy(), schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) public Consumer testPubSubConsumer() { return System.out::println; From 2d8435d5362efb961ea27c26a7bbdf145a64f040 Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 14:53:57 +0100 Subject: [PATCH 04/10] feat(google-pubsub-binding): update pipeline --- .github/workflows/springwolf-bindings.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/springwolf-bindings.yml b/.github/workflows/springwolf-bindings.yml index e250466cd..5a9b79380 100644 --- a/.github/workflows/springwolf-bindings.yml +++ b/.github/workflows/springwolf-bindings.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - binding: [ "sns", "sqs" ] + binding: [ "googlepubsub", "sns", "sqs" ] env: binding: springwolf-bindings/springwolf-${{ matrix.binding }}-binding From 7a7cff171254190f81c2d571bf9bd5bd0354d70b Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 14:54:20 +0100 Subject: [PATCH 05/10] fix(google-pubsub-binding): fix auto configuration --- ....springframework.boot.autoconfigure.AutoConfiguration.imports | 1 - 1 file changed, 1 deletion(-) delete mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 6badfd59b..000000000 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF.spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration \ No newline at end of file From 2375eef125b2ef971336098a0ba18c888ca9868b Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 14:55:45 +0100 Subject: [PATCH 06/10] feat(google-pubsub-binding): update cloud-stream example --- .../build.gradle | 2 + .../CloudstreamConfiguration.java | 10 +++ .../dtos/GooglePubSubPayloadDto.java | 21 +++++ .../src/main/resources/application.properties | 1 + .../cloudstream/ApiIntegrationTest.java | 2 +- .../src/test/resources/asyncapi.json | 80 ++++++++++++++++++- 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/dtos/GooglePubSubPayloadDto.java diff --git a/springwolf-examples/springwolf-cloud-stream-example/build.gradle b/springwolf-examples/springwolf-cloud-stream-example/build.gradle index b8ad253e5..09a9d0698 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/build.gradle +++ b/springwolf-examples/springwolf-cloud-stream-example/build.gradle @@ -23,6 +23,8 @@ dependencies { runtimeOnly project(":springwolf-plugins:springwolf-cloud-stream") annotationProcessor project(":springwolf-plugins:springwolf-cloud-stream") + implementation project(":springwolf-bindings:springwolf-googlepubsub-binding") + runtimeOnly project(":springwolf-ui") runtimeOnly "org.springframework.boot:spring-boot-starter-web" runtimeOnly "org.springframework.boot:spring-boot-starter-actuator" diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/configuration/CloudstreamConfiguration.java b/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/configuration/CloudstreamConfiguration.java index bd4225402..2e8fd5c06 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/configuration/CloudstreamConfiguration.java +++ b/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/configuration/CloudstreamConfiguration.java @@ -1,8 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.examples.cloudstream.configuration; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; import io.github.springwolf.examples.cloudstream.dtos.AnotherPayloadDto; import io.github.springwolf.examples.cloudstream.dtos.ExamplePayloadDto; +import io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.streams.kstream.KStream; import org.springframework.context.annotation.Bean; @@ -25,4 +28,11 @@ public Function, KStream consumerMethod() { return input -> log.info("Received new message in another-topic: {}", input.toString()); } + + @GooglePubSubAsyncChannelBinding( + schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) + @Bean + public Consumer googlePubSubConsumerMethod() { + return input -> log.info("Received new message in google-pubsub-topic: {}", input.toString()); + } } diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/dtos/GooglePubSubPayloadDto.java b/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/dtos/GooglePubSubPayloadDto.java new file mode 100644 index 000000000..1ef31c309 --- /dev/null +++ b/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/springwolf/examples/cloudstream/dtos/GooglePubSubPayloadDto.java @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.examples.cloudstream.dtos; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Schema(description = "Google pubsub payload model") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GooglePubSubPayloadDto { + @Schema(description = "Some string field", example = "some string value", requiredMode = REQUIRED) + private String someString; + + @Schema(description = "Some long field", example = "5") + private long someLong; +} diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/main/resources/application.properties b/springwolf-examples/springwolf-cloud-stream-example/src/main/resources/application.properties index 4b150d9ea..ca03fd0b5 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/src/main/resources/application.properties +++ b/springwolf-examples/springwolf-cloud-stream-example/src/main/resources/application.properties @@ -10,6 +10,7 @@ spring.kafka.bootstrap-servers=${BOOTSTRAP_SERVER:localhost:29092} spring.cloud.stream.bindings.process-in-0.destination=example-topic spring.cloud.stream.bindings.process-out-0.destination=another-topic spring.cloud.stream.bindings.consumerMethod-in-0.destination=another-topic +spring.cloud.stream.bindings.googlePubSubConsumerMethod-in-0.destination=google-pubsub-topic ######### diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/test/java/io/github/springwolf/examples/cloudstream/ApiIntegrationTest.java b/springwolf-examples/springwolf-cloud-stream-example/src/test/java/io/github/springwolf/examples/cloudstream/ApiIntegrationTest.java index b3e383023..397f79848 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/src/test/java/io/github/springwolf/examples/cloudstream/ApiIntegrationTest.java +++ b/springwolf-examples/springwolf-cloud-stream-example/src/test/java/io/github/springwolf/examples/cloudstream/ApiIntegrationTest.java @@ -39,7 +39,7 @@ void asyncApiResourceArtifactTest() throws IOException { Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); InputStream s = this.getClass().getResourceAsStream("/asyncapi.json"); - String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8); + String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); assertEquals(expected, actualPatched); } diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json b/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json index c81b34014..11b5fab74 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json +++ b/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json @@ -41,6 +41,23 @@ "bindings": { "kafka": { } } + }, + "google-pubsub-topic": { + "messages": { + "io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto" + } + }, + "bindings": { + "googlepubsub": { + "messageStoragePolicy": { }, + "schemaSettings": { + "encoding": "BINARY", + "name": "project/test" + }, + "bindingVersion": "0.2.0" + } + } } }, "components": { @@ -118,6 +135,36 @@ "someString" ] }, + "GooglePubSubPayloadDto": { + "type": "object", + "properties": { + "someLong": { + "type": "integer", + "description": "Some long field", + "format": "int64", + "examples": [ + 5 + ] + }, + "someString": { + "type": "string", + "description": "Some string field", + "examples": [ + "some string value" + ] + } + }, + "description": "Google pubsub payload model", + "examples": [ + { + "someLong": 5, + "someString": "some string value" + } + ], + "required": [ + "someString" + ] + }, "HeadersNotDocumented": { "type": "object", "properties": { }, @@ -158,6 +205,22 @@ "bindings": { "kafka": { } } + }, + "io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/GooglePubSubPayloadDto" + } + }, + "name": "io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto", + "title": "GooglePubSubPayloadDto", + "bindings": { + "kafka": { } + } } } }, @@ -206,6 +269,21 @@ "$ref": "#/channels/example-topic/messages/io.github.springwolf.examples.cloudstream.dtos.ExamplePayloadDto" } ] + }, + "google-pubsub-topic_publish_googlePubSubConsumerMethod": { + "action": "receive", + "channel": { + "$ref": "#/channels/google-pubsub-topic" + }, + "description": "Auto-generated description", + "bindings": { + "kafka": { } + }, + "messages": [ + { + "$ref": "#/channels/google-pubsub-topic/messages/io.github.springwolf.examples.cloudstream.dtos.GooglePubSubPayloadDto" + } + ] } } -} \ No newline at end of file +} From 9ad37fd5a3d0ceaf6ecc5545866b72b4a0942f6a Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 14:56:00 +0100 Subject: [PATCH 07/10] chore(google-pubsub-binding): fix auto configuration --- .../SpringwolfGooglePubSubBindingAutoConfiguration.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java index 3daf4c286..ebec0c490 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/configuration/SpringwolfGooglePubSubBindingAutoConfiguration.java @@ -2,15 +2,24 @@ package io.github.springwolf.bindings.googlepubsub.configuration; import io.github.springwolf.bindings.googlepubsub.scanners.channels.GooglePubSubChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.BindingProcessorPriority; import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; +/** + * Autoconfiguration for the springwolf Google PubSub Binding. + */ @AutoConfiguration @ConditionalOnProperty(name = SpringwolfConfigConstants.SPRINGWOLF_ENABLED, havingValue = "true", matchIfMissing = true) public class SpringwolfGooglePubSubBindingAutoConfiguration { + @Bean + @Order(value = BindingProcessorPriority.PROTOCOL_BINDING) + @ConditionalOnMissingBean public GooglePubSubChannelBindingProcessor googlePubSubChannelBindingProcessor() { return new GooglePubSubChannelBindingProcessor(); } From 8128ebab7574290c64e517d2f0a2f6adeec95cf7 Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 14:56:28 +0100 Subject: [PATCH 08/10] refactor(google-pubsub-binding): move bindings to dedicated package --- .../GooglePubSubChannelBindingProcessor.java | 59 +++++--- ...oglePubSubChannelBindingProcessorTest.java | 29 +++- .../channels/ChannelBindingProcessor.java | 2 +- .../channels/ProcessedChannelBinding.java | 2 +- .../common/utils/AsyncAnnotationUtil.java | 4 +- .../TestChannelBindingProcessor.java | 4 +- .../build.gradle | 1 - .../CloudStreamFunctionChannelsScanner.java | 4 +- .../CloudStreamFunctionOperationsScanner.java | 2 + ...pringwolfCloudStreamAutoConfiguration.java | 2 +- ...lsBindingSchemaSettingIntegrationTest.java | 142 ------------------ 11 files changed, 74 insertions(+), 177 deletions(-) rename springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/{ => bindings}/channels/ChannelBindingProcessor.java (86%) rename springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/{ => bindings}/channels/ProcessedChannelBinding.java (77%) delete mode 100644 springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java index 4e005b7a5..7c0d0f064 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java @@ -5,8 +5,9 @@ import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageStoragePolicy; import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; -import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; -import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ProcessedChannelBinding; +import io.micrometer.common.util.StringUtils; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.util.StringValueResolver; @@ -33,22 +34,42 @@ public Optional process(Method method) { private ProcessedChannelBinding mapToChannelBinding(GooglePubSubAsyncChannelBinding bindingAnnotation) { - GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = new GooglePubSubMessageStoragePolicy( - Arrays.stream(bindingAnnotation.messageStoragePolicy().allowedPersistenceRegions()) - .toList()); - GooglePubSubSchemaSettings googlePubSubSchemaSettings = new GooglePubSubSchemaSettings( - bindingAnnotation.schemaSettings().encoding(), - bindingAnnotation.schemaSettings().firstRevisionId(), - bindingAnnotation.schemaSettings().lastRevisionId(), - bindingAnnotation.schemaSettings().name()); - - return new ProcessedChannelBinding( - bindingAnnotation.type(), - new GooglePubSubChannelBinding( - null, - bindingAnnotation.messageRetentionDuration(), - googlePubsubMessageStoragePolicy, - googlePubSubSchemaSettings, - bindingAnnotation.bindingVersion())); + GooglePubSubMessageStoragePolicy.GooglePubSubMessageStoragePolicyBuilder policyBuilder = + GooglePubSubMessageStoragePolicy.builder(); + if (bindingAnnotation.messageStoragePolicy().allowedPersistenceRegions().length > 0) { + policyBuilder.allowedPersistenceRegions( + Arrays.stream(bindingAnnotation.messageStoragePolicy().allowedPersistenceRegions()) + .toList()); + } + + GooglePubSubSchemaSettings.GooglePubSubSchemaSettingsBuilder schemaSettingsBuilder = + GooglePubSubSchemaSettings.builder(); + if (StringUtils.isNotBlank(bindingAnnotation.schemaSettings().encoding())) { + schemaSettingsBuilder.encoding(bindingAnnotation.schemaSettings().encoding()); + } + if (StringUtils.isNotBlank(bindingAnnotation.schemaSettings().firstRevisionId())) { + schemaSettingsBuilder.firstRevisionId( + bindingAnnotation.schemaSettings().firstRevisionId()); + } + if (StringUtils.isNotBlank(bindingAnnotation.schemaSettings().lastRevisionId())) { + schemaSettingsBuilder.lastRevisionId( + bindingAnnotation.schemaSettings().lastRevisionId()); + } + if (StringUtils.isNotBlank(bindingAnnotation.schemaSettings().name())) { + schemaSettingsBuilder.name(bindingAnnotation.schemaSettings().name()); + } + + GooglePubSubChannelBinding.GooglePubSubChannelBindingBuilder bindingBuilder = + GooglePubSubChannelBinding.builder() + .messageStoragePolicy(policyBuilder.build()) + .schemaSettings(schemaSettingsBuilder.build()); + if (StringUtils.isNotBlank(bindingAnnotation.messageRetentionDuration())) { + bindingBuilder.messageRetentionDuration(bindingAnnotation.messageRetentionDuration()); + } + if (StringUtils.isNotBlank(bindingAnnotation.bindingVersion())) { + bindingBuilder.bindingVersion(bindingAnnotation.bindingVersion()); + } + + return new ProcessedChannelBinding(bindingAnnotation.type(), bindingBuilder.build()); } } diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java index 3671447be..436de699d 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/test/java/GooglePubSubChannelBindingProcessorTest.java @@ -4,8 +4,9 @@ import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; +import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubsubAsyncMessageStoragePolicy; import io.github.springwolf.bindings.googlepubsub.scanners.channels.GooglePubSubChannelBindingProcessor; -import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ProcessedChannelBinding; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; @@ -19,31 +20,45 @@ public class GooglePubSubChannelBindingProcessorTest { @Test void processTest() throws NoSuchMethodException { + // given Method method = GooglePubSubChannelBindingProcessorTest.class.getMethod("methodWithAnnotation"); + // when ProcessedChannelBinding binding = processor.process(method).get(); + // then assertThat(binding.getType()).isEqualTo("googlepubsub"); - GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = - new GooglePubSubMessageStoragePolicy(List.of()); - GooglePubSubSchemaSettings googlePubSubSchemaSettings = - new GooglePubSubSchemaSettings("BINARY", "", "", "project/test"); assertThat(binding.getBinding()) .isEqualTo(new GooglePubSubChannelBinding( - null, "", googlePubsubMessageStoragePolicy, googlePubSubSchemaSettings, "0.2.0")); + null, + "messageRetentionDuration", + new GooglePubSubMessageStoragePolicy(List.of("region1", "region2")), + new GooglePubSubSchemaSettings("BINARY", "firstRevisionId", "lastRevisionId", "project/test"), + "0.2.0")); } @Test void processWithoutAnnotationTest() throws NoSuchMethodException { + // given Method method = GooglePubSubChannelBindingProcessorTest.class.getMethod("methodWithoutAnnotation"); + // when Optional binding = processor.process(method); + // then assertThat(binding).isNotPresent(); } @GooglePubSubAsyncChannelBinding( - schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) + messageRetentionDuration = "messageRetentionDuration", + messageStoragePolicy = + @GooglePubsubAsyncMessageStoragePolicy(allowedPersistenceRegions = {"region1", "region2"}), + schemaSettings = + @GooglePubSubAsyncSchemaSetting( + encoding = "BINARY", + firstRevisionId = "firstRevisionId", + lastRevisionId = "lastRevisionId", + name = "project/test")) public void methodWithAnnotation() {} public void methodWithoutAnnotation() {} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ChannelBindingProcessor.java similarity index 86% rename from springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java rename to springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ChannelBindingProcessor.java index a6ec14dbd..a79658a13 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ChannelBindingProcessor.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ChannelBindingProcessor.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.core.asyncapi.scanners.channels; +package io.github.springwolf.core.asyncapi.scanners.bindings.channels; import java.lang.reflect.Method; import java.util.Optional; diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ProcessedChannelBinding.java similarity index 77% rename from springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java rename to springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ProcessedChannelBinding.java index d5f6ddfca..7976f1edc 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/ProcessedChannelBinding.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/channels/ProcessedChannelBinding.java @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.core.asyncapi.scanners.channels; +package io.github.springwolf.core.asyncapi.scanners.bindings.channels; import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import lombok.Data; diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java index 160d3cb1b..0e9782b16 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java @@ -9,12 +9,12 @@ import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaderSchema; import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ProcessedChannelBinding; import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.messages.ProcessedMessageBinding; import io.github.springwolf.core.asyncapi.scanners.bindings.operations.OperationBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.operations.ProcessedOperationBinding; -import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; -import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java index d0d43607e..c38c7f412 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/bindings/processor/TestChannelBindingProcessor.java @@ -4,8 +4,8 @@ import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.EmptyChannelBinding; import io.github.springwolf.core.asyncapi.scanners.bindings.BindingProcessorPriority; -import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; -import io.github.springwolf.core.asyncapi.scanners.channels.ProcessedChannelBinding; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ProcessedChannelBinding; import org.springframework.core.annotation.Order; import java.lang.annotation.ElementType; diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle index 277483d8d..a36e98644 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle @@ -19,7 +19,6 @@ dependencyManagement { dependencies { api project(":springwolf-asyncapi") api project(":springwolf-core") - testImplementation project(":springwolf-bindings:springwolf-googlepubsub-binding") implementation "org.slf4j:slf4j-api:${slf4jApiVersion}" diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java index 69170898c..d2c9e7773 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java @@ -17,7 +17,7 @@ import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; import io.github.springwolf.core.asyncapi.scanners.ChannelsScanner; import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner; -import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.channels.ChannelMerger; import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil; import io.github.springwolf.core.configuration.docket.AsyncApiDocket; @@ -96,6 +96,7 @@ private ChannelObject buildChannel(FunctionalChannelBeanData beanData) { } private Map buildMessageBinding() { + // FIXME: handle messageBindings from annotations as for the channel String protocolName = getProtocolName(); return Map.of(protocolName, new EmptyMessageBinding()); } @@ -106,6 +107,7 @@ private Map buildChannelBinding(Method method) { if (!channelBindingMap.isEmpty()) { return channelBindingMap; } + String protocolName = getProtocolName(); return Map.of(protocolName, new EmptyChannelBinding()); } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java index 080d263c2..db401c7f3 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java @@ -96,11 +96,13 @@ private Operation buildOperation(FunctionalChannelBeanData beanData, String chan } private Map buildMessageBinding() { + // FIXME: handle messageBindings from annotations as for the channel String protocolName = getProtocolName(); return Map.of(protocolName, new EmptyMessageBinding()); } private Map buildOperationBinding() { + // FIXME: handle operationBindings from annotations as for the channel String protocolName = getProtocolName(); return Map.of(protocolName, new EmptyOperationBinding()); } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java index 89d924d48..412e368ff 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/configuration/SpringwolfCloudStreamAutoConfiguration.java @@ -3,7 +3,7 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService; import io.github.springwolf.core.asyncapi.scanners.beans.BeanMethodsScanner; -import io.github.springwolf.core.asyncapi.scanners.channels.ChannelBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; import io.github.springwolf.core.configuration.docket.AsyncApiDocketService; import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants; diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java deleted file mode 100644 index 7f877dc0b..000000000 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.java +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.channels; - -import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubChannelBinding; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageBinding; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubMessageStoragePolicy; -import io.github.springwolf.asyncapi.v3.bindings.googlepubsub.GooglePubSubSchemaSettings; -import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessagePayload; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; -import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; -import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; -import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; -import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncSchemaSetting; -import io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration; -import io.github.springwolf.core.asyncapi.components.DefaultComponentsService; -import io.github.springwolf.core.asyncapi.components.SwaggerSchemaUtil; -import io.github.springwolf.core.asyncapi.components.examples.SchemaWalkerProvider; -import io.github.springwolf.core.asyncapi.components.examples.walkers.DefaultSchemaWalker; -import io.github.springwolf.core.asyncapi.components.examples.walkers.json.ExampleJsonValueGenerator; -import io.github.springwolf.core.asyncapi.components.headers.AsyncHeaders; -import io.github.springwolf.core.asyncapi.scanners.beans.DefaultBeanMethodsScanner; -import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClassScanner; -import io.github.springwolf.core.asyncapi.scanners.classes.spring.ConfigurationClassScanner; -import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; -import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; -import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; -import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; -import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.operations.CloudStreamFunctionOperationsScanner; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.cloud.stream.config.BindingProperties; -import org.springframework.cloud.stream.config.BindingServiceProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - -@ExtendWith(SpringExtension.class) -@ContextConfiguration( - classes = { - SpringwolfGooglePubSubBindingAutoConfiguration.class, - ConfigurationClassScanner.class, - ComponentClassScanner.class, - DefaultBeanMethodsScanner.class, - DefaultComponentsService.class, - SwaggerSchemaUtil.class, - PayloadClassExtractor.class, - DefaultSchemaWalker.class, - SchemaWalkerProvider.class, - ExampleJsonValueGenerator.class, - DefaultAsyncApiDocketService.class, - CloudStreamFunctionChannelsScanner.class, - CloudStreamFunctionOperationsScanner.class, - FunctionalChannelBeanBuilder.class, - SpringwolfConfigProperties.class - }) -@TestPropertySource( - properties = { - "springwolf.enabled=true", - "springwolf.docket.info.title=Test", - "springwolf.docket.info.version=1.0.0", - "springwolf.docket.base-package=io.github.springwolf.plugins.cloudstream.asyncapi.scanners.channels", - "springwolf.docket.servers.googlepubsub.protocol=googlepubsub", - "springwolf.docket.servers.googlepubsub.host=kafka:9092", - }) -@EnableConfigurationProperties -@Import(CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest.Configuration.class) -public class CloudStreamFunctionChannelsBindingSchemaSettingIntegrationTest { - - @MockBean - private BindingServiceProperties bindingServiceProperties; - - @Autowired - private CloudStreamFunctionChannelsScanner channelsScanner; - - private GooglePubSubSchemaSettings googlePubSubSchemaSettings = - new GooglePubSubSchemaSettings("BINARY", "", "", "project/test"); - - private GooglePubSubMessageStoragePolicy googlePubsubMessageStoragePolicy = - new GooglePubSubMessageStoragePolicy(List.of()); - private Map channelBindingGooglePubSub = Map.of( - "googlepubsub", - new GooglePubSubChannelBinding( - null, "", googlePubsubMessageStoragePolicy, googlePubSubSchemaSettings, "0.2.0")); - - @Test - void testConsumerBinding() { - // Given a binding "spring.cloud.stream.bindings.testConsumer-in-0.destination=test-consumer-input-topic" - BindingProperties testConsumerInBinding = new BindingProperties(); - String topicName = "test-consumer-input-topic"; - testConsumerInBinding.setDestination(topicName); - when(bindingServiceProperties.getBindings()) - .thenReturn(Map.of("testPubSubConsumer-in-0", testConsumerInBinding)); - - // When scan is called - Map actualChannels = channelsScanner.scan(); - - // Then the returned channels contain a ChannelItem with the correct data - MessageObject message = MessageObject.builder() - .name(String.class.getName()) - .title(String.class.getSimpleName()) - .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaReference.fromSchema(String.class.getSimpleName())) - .build())) - .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))) - .bindings(Map.of("googlepubsub", new GooglePubSubMessageBinding())) - .build(); - - ChannelObject expectedChannel = ChannelObject.builder() - .bindings(channelBindingGooglePubSub) - .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message))) - .build(); - - assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); - } - - @TestConfiguration - public static class Configuration { - @Bean - @GooglePubSubAsyncChannelBinding( - schemaSettings = @GooglePubSubAsyncSchemaSetting(encoding = "BINARY", name = "project/test")) - public Consumer testPubSubConsumer() { - return System.out::println; - } - } -} From 39fa9fb9cba3d5eb183c26133d9b1a677b65830f Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 15:06:11 +0100 Subject: [PATCH 09/10] deps(google-pubsub-binding): use apache StringUtils --- .../springwolf-googlepubsub-binding/build.gradle | 1 + .../scanners/channels/GooglePubSubChannelBindingProcessor.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle b/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle index 1cbacc478..283f7b7fb 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle +++ b/springwolf-bindings/springwolf-googlepubsub-binding/build.gradle @@ -14,6 +14,7 @@ dependencies { implementation "org.springframework:spring-core" implementation "org.springframework.boot:spring-boot-autoconfigure" + implementation "org.apache.commons:commons-lang3:${commonsLang3Version}" testImplementation "org.assertj:assertj-core:${assertjCoreVersion}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java index 7c0d0f064..56642a571 100644 --- a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/java/io/github/springwolf/bindings/googlepubsub/scanners/channels/GooglePubSubChannelBindingProcessor.java @@ -7,7 +7,7 @@ import io.github.springwolf.bindings.googlepubsub.annotations.GooglePubSubAsyncChannelBinding; import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ChannelBindingProcessor; import io.github.springwolf.core.asyncapi.scanners.bindings.channels.ProcessedChannelBinding; -import io.micrometer.common.util.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.util.StringValueResolver; From 4ef9ed30600cb9f1f7a37fab7f7b374c338dcb6a Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 22 Mar 2024 15:09:49 +0100 Subject: [PATCH 10/10] feat(google-pubsub-binding): add missing autoconfiguration --- ....springframework.boot.autoconfigure.AutoConfiguration.imports | 1 + 1 file changed, 1 insertion(+) create mode 100644 springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..0444f7ce0 --- /dev/null +++ b/springwolf-bindings/springwolf-googlepubsub-binding/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +io.github.springwolf.bindings.googlepubsub.configuration.SpringwolfGooglePubSubBindingAutoConfiguration