From 7a428bd65cfd0a7b74d3fd095656dbab79baae35 Mon Sep 17 00:00:00 2001 From: Charles Bazeley Date: Tue, 17 Dec 2024 11:08:53 -0500 Subject: [PATCH] change how the map is populated for channels found via AsyncApiComponent and for Channel annotations to handle the fact that not all of them will be named based on their class and that will only happen if they target a class instead of a function. --- .../annotation/AsyncApiComponent.kt | 4 +- .../context/annotation/AnnotationProvider.kt | 16 ++++-- .../processor/AsyncApiComponentProcessor.kt | 5 +- .../annotation/async_api_component.json | 2 +- .../AsyncApiControllerIntegrationTest.kt | 50 ++++++++++++++++ ..._api_component_annotation_integration.json | 57 +++++++++++++++++++ 6 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 kotlin-asyncapi-spring-web/src/test/resources/async_api_component_annotation_integration.json diff --git a/kotlin-asyncapi-annotation/src/main/kotlin/org/openfolder/kotlinasyncapi/annotation/AsyncApiComponent.kt b/kotlin-asyncapi-annotation/src/main/kotlin/org/openfolder/kotlinasyncapi/annotation/AsyncApiComponent.kt index 6f7e17f..a04b8a6 100644 --- a/kotlin-asyncapi-annotation/src/main/kotlin/org/openfolder/kotlinasyncapi/annotation/AsyncApiComponent.kt +++ b/kotlin-asyncapi-annotation/src/main/kotlin/org/openfolder/kotlinasyncapi/annotation/AsyncApiComponent.kt @@ -2,6 +2,4 @@ package org.openfolder.kotlinasyncapi.annotation @Target(AnnotationTarget.CLASS) @AsyncApiAnnotation -annotation class AsyncApiComponent( - val value: String = "", -) \ No newline at end of file +annotation class AsyncApiComponent \ No newline at end of file diff --git a/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/AnnotationProvider.kt b/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/AnnotationProvider.kt index 2b6dcc2..cdcd438 100644 --- a/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/AnnotationProvider.kt +++ b/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/AnnotationProvider.kt @@ -72,7 +72,8 @@ class AnnotationProvider( listOfNotNull( clazz.findAnnotation()?.let { clazz to it }, clazz.findAnnotation()?.let { clazz to it }, - clazz.findAnnotation()?.let { clazz to it } + clazz.findAnnotation()?.let { clazz to it }, + clazz.findAnnotation()?.let { clazz to it} ) } .mapNotNull { (clazz, annotation) -> @@ -80,12 +81,15 @@ class AnnotationProvider( is Message -> messageProcessor.process(annotation, clazz) is Schema -> schemaProcessor.process(annotation, clazz) is Channel -> channelProcessor.process(annotation, clazz).also { - componentToChannelMapping[clazz.java.simpleName] = - annotation.value.takeIf { it.isNotEmpty() } ?: clazz.java.simpleName + if (clazz.annotations.any { it is Channel }) { + componentToChannelMapping[clazz.java.simpleName] = + annotation.value.takeIf { it.isNotEmpty() } ?: clazz.java.simpleName + } } - is AsyncApiComponent -> asyncApiComponentProcessor.process(annotation, clazz).also { - componentToChannelMapping[clazz.java.simpleName] = - annotation.value.takeIf { it.isNotEmpty() } ?: clazz.java.simpleName + is AsyncApiComponent -> asyncApiComponentProcessor.process(annotation, clazz).also { processedComponents -> + processedComponents.channels?.forEach { (channelName, _) -> + componentToChannelMapping[channelName] = channelName + } } else -> null } diff --git a/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/processor/AsyncApiComponentProcessor.kt b/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/processor/AsyncApiComponentProcessor.kt index 79f7b50..9aae918 100644 --- a/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/processor/AsyncApiComponentProcessor.kt +++ b/kotlin-asyncapi-context/src/main/kotlin/org/openfolder/kotlinasyncapi/context/annotation/processor/AsyncApiComponentProcessor.kt @@ -15,13 +15,14 @@ class AsyncApiComponentProcessor : AnnotationProcessor() }.forEach { currentFunction -> - currentFunction.findAnnotation()!!.toChannel() + var currentAnnotation = currentFunction.findAnnotation()!! + currentAnnotation.toChannel() .apply { subscribe = subscribe ?: currentFunction.findAnnotation()?.toOperation() publish = publish ?: currentFunction.findAnnotation()?.toOperation() } .also { - put(currentFunction.name, it) + put(currentAnnotation.value, it) } } } diff --git a/kotlin-asyncapi-context/src/test/resources/annotation/async_api_component.json b/kotlin-asyncapi-context/src/test/resources/annotation/async_api_component.json index c1e86bc..a9edb0d 100644 --- a/kotlin-asyncapi-context/src/test/resources/annotation/async_api_component.json +++ b/kotlin-asyncapi-context/src/test/resources/annotation/async_api_component.json @@ -1,6 +1,6 @@ { "channels" : { - "testSubscribe" : { + "some/{parameter}/channel" : { "description" : "testDescription", "servers" : [ "dev" ], "subscribe" : { diff --git a/kotlin-asyncapi-spring-web/src/test/kotlin/org/openfolder/kotlinasyncapi/springweb/controller/AsyncApiControllerIntegrationTest.kt b/kotlin-asyncapi-spring-web/src/test/kotlin/org/openfolder/kotlinasyncapi/springweb/controller/AsyncApiControllerIntegrationTest.kt index 8bcb2d3..1ea1c10 100644 --- a/kotlin-asyncapi-spring-web/src/test/kotlin/org/openfolder/kotlinasyncapi/springweb/controller/AsyncApiControllerIntegrationTest.kt +++ b/kotlin-asyncapi-spring-web/src/test/kotlin/org/openfolder/kotlinasyncapi/springweb/controller/AsyncApiControllerIntegrationTest.kt @@ -1,6 +1,7 @@ package org.openfolder.kotlinasyncapi.springweb.controller import org.junit.jupiter.api.Test +import org.openfolder.kotlinasyncapi.annotation.AsyncApiComponent import org.openfolder.kotlinasyncapi.annotation.channel.Channel import org.openfolder.kotlinasyncapi.annotation.channel.Message import org.openfolder.kotlinasyncapi.annotation.channel.Publish @@ -212,3 +213,52 @@ internal class AsyncApiControllerAnnotationIntegrationTest { val optionalValue: Boolean? ) } + +@SpringBootTest +@AutoConfigureMockMvc +internal class AsyncApiComponentAnnotationControllerIntegrationTest { + + @Autowired + lateinit var mockMvc: MockMvc + + @Test + fun `should return AsyncApi document`() { + val expected = TestUtils.json("async_api_component_annotation_integration.json") + + mockMvc.perform(get("/docs/asyncapi")) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful) + .andExpect(content().json(expected)) + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @EnableAsyncApi + open class TestConfig { + + @Bean + open fun asyncApiExtension() = + AsyncApiExtension.builder { + info { + title("testTitle") + version("testVersion") + } + } + } + + @AsyncApiComponent + class TestChannel { + + @Channel("my/channel") + @Publish( + description = "testDescription", + message = Message(TestMessage::class) + ) + fun testOperation() {} + } + + @Message + data class TestMessage( + val value: String, + val optionalValue: Boolean? + ) +} \ No newline at end of file diff --git a/kotlin-asyncapi-spring-web/src/test/resources/async_api_component_annotation_integration.json b/kotlin-asyncapi-spring-web/src/test/resources/async_api_component_annotation_integration.json new file mode 100644 index 0000000..519b246 --- /dev/null +++ b/kotlin-asyncapi-spring-web/src/test/resources/async_api_component_annotation_integration.json @@ -0,0 +1,57 @@ +{ + "asyncapi": "2.4.0", + "info": { + "title": "testTitle", + "version": "testVersion" + }, + "channels": { + "my/channel": { + "$ref": "#/components/channels/TestChannel" + } + }, + "components": { + "schemas": { + "TestMessage": { + "required": [ + "value" + ], + "type": "object", + "properties": { + "value": { + "type": "string", + "exampleSetFlag": false, + "types": [ + "string" + ] + }, + "optionalValue": { + "type": "boolean", + "exampleSetFlag": false, + "types": [ + "boolean" + ] + } + }, + "exampleSetFlag": false + } + }, + "channels": { + "my/channel": { + "publish": { + "description": "testDescription", + "message": { + "$ref": "#/components/messages/TestMessage" + } + } + } + }, + "messages": { + "TestMessage": { + "payload": { + "$ref": "#/components/schemas/TestMessage" + }, + "schemaFormat": "application/schema+json;version=draft-07" + } + } + } +}