Skip to content

Commit 2688d55

Browse files
azizabahCharles Bazeleylorenzsimonlorenz-scalable
authored
feat: create new annotation for classes that allows you to have multiple channels within (#76)
* allow annotation to target function also. * add support for channel on functions instead of just classes. * split into a separate processor with new annotation to handle finding the correct classes. * match previous white space * name updates per PR comments. * fix annotation location * add AsyncApiComponent population of channel map behavior to match Channel behavior. added value attribute to AsyncApiComponent for parity. * chore: move domain (#77) * feat: Implement message model annotations (#43) * feat - Implement message model annotations * chore - Remove unused dependencies * feat: Process message and schema annotations (#44) * feat - Implement Message annotation processing * feat - Merge annotation components * feat - Add schema annotation processor * refactor - Make annotation processor context dynamic test - Add annotation provider integration test * chore - ktlint format * refactor - Refactor dependency management --------- Co-authored-by: lorenzsimon <[email protected]> * feat: Channel annotation processing (#45) * feat - Add Channel and Operation annotations * feat - Add Channel processing * refactor - Annotation keys to values * chore - Fix confusing test values * refactor: Annotation mapping (#47) * refactor - Annotation mapping improvements * refactor - Add option for inline messages and schemas * refactor - Use classname for channel component keys if autogenerated * fix - Typo * test - Fix Schemas test * feat: Add Kotlin module to model resolver (#48) feat - Add Kotlin module to model resolver * feat: Bind channels to annotation components (#49) * refactor - Context providers * feat - Bind channels to annotation components * refactor - Annotation components binding * chore - Format * chore: Add Spring Boot example application (#63) * chore: Add Spring Boot example application * fix: Java version * fix: Test * chore: Bump dependencies (#65) * chore: Bump dependencies * chore: Bump dependencies * chore: Set Java version in GH actions * fix: Autoconfig migration * fix: Migrate Jakarta * chore: Refactor data objects (#67) * chore: Revert * release: 3.0.3 * pre-release: 3.0.4 * feat: Add Ktor integration (#72) * docs: Update Spring support * pre-release: 3.0.4 (#70) * docs: Link Spring Boot example * Add Ktor integration * Fix content type * Fix test * Fix test * Add Script extension * Add Script extension * Refactor * Add plugin integration test * Add docs for ktor integration * Move to new domain namespace * Fix kts example usage * Move domain * Bump dependencies --------- Co-authored-by: Lorenz Simon <[email protected]> * chore: Update repositories * 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. * Update CODEOWNERS * chore: Update pom.xml * Update CODEOWNERS * changes per PR requests. * updates to handle merge conflicts. --------- Co-authored-by: Charles Bazeley <[email protected]> Co-authored-by: Lorenz Simon <[email protected]> Co-authored-by: Lorenz Simon <[email protected]>
1 parent e304489 commit 2688d55

File tree

13 files changed

+259
-10
lines changed

13 files changed

+259
-10
lines changed

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# Default owners
2-
* @lorenzsimon @sueskind @gimlet2
2+
* @lorenzsimon @gimlet2 @asyncapi-bot-eve
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.asyncapi.kotlinasyncapi.annotation
2+
3+
@Target(AnnotationTarget.CLASS)
4+
@AsyncApiAnnotation
5+
annotation class AsyncApiComponent

kotlin-asyncapi-annotation/src/main/kotlin/com/asyncapi/kotlinasyncapi/annotation/channel/Channel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import com.asyncapi.kotlinasyncapi.annotation.AsyncApiAnnotation
44

55
@Target(
66
AnnotationTarget.CLASS,
7-
AnnotationTarget.ANNOTATION_CLASS
7+
AnnotationTarget.ANNOTATION_CLASS,
8+
AnnotationTarget.FUNCTION
89
)
910
@AsyncApiAnnotation
1011
annotation class Channel(

kotlin-asyncapi-context/src/main/kotlin/com/asyncapi/kotlinasyncapi/context/annotation/AnnotationProvider.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.asyncapi.kotlinasyncapi.context.annotation
22

33
import com.asyncapi.kotlinasyncapi.annotation.AsyncApiAnnotation
4+
import com.asyncapi.kotlinasyncapi.annotation.AsyncApiComponent
45
import com.asyncapi.kotlinasyncapi.annotation.Schema
56
import com.asyncapi.kotlinasyncapi.annotation.channel.Channel
67
import com.asyncapi.kotlinasyncapi.annotation.channel.Message
@@ -31,7 +32,9 @@ class AnnotationProvider(
3132
private val scanner: AnnotationScanner,
3233
private val messageProcessor: AnnotationProcessor<Message, KClass<*>>,
3334
private val schemaProcessor: AnnotationProcessor<Schema, KClass<*>>,
34-
private val channelProcessor: AnnotationProcessor<Channel, KClass<*>>
35+
private val channelProcessor: AnnotationProcessor<Channel, KClass<*>>,
36+
private val asyncApiComponentProcessor: AnnotationProcessor<AsyncApiComponent, KClass<*>>
37+
3538
) : AsyncApiContextProvider {
3639

3740
private val componentToChannelMapping = mutableMapOf<String, String>()
@@ -70,7 +73,8 @@ class AnnotationProvider(
7073
listOfNotNull(
7174
clazz.findAnnotation<Message>()?.let { clazz to it },
7275
clazz.findAnnotation<Schema>()?.let { clazz to it },
73-
clazz.findAnnotation<Channel>()?.let { clazz to it }
76+
clazz.findAnnotation<Channel>()?.let { clazz to it },
77+
clazz.findAnnotation<AsyncApiComponent>()?.let { clazz to it}
7478
)
7579
}
7680
.mapNotNull { (clazz, annotation) ->
@@ -81,6 +85,11 @@ class AnnotationProvider(
8185
componentToChannelMapping[clazz.java.simpleName] =
8286
annotation.value.takeIf { it.isNotEmpty() } ?: clazz.java.simpleName
8387
}
88+
is AsyncApiComponent -> asyncApiComponentProcessor.process(annotation, clazz).also { processedComponents ->
89+
processedComponents.channels?.forEach { (channelName, _) ->
90+
componentToChannelMapping[channelName] = channelName
91+
}
92+
}
8493
else -> null
8594
}
8695
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.asyncapi.kotlinasyncapi.context.annotation.processor
2+
3+
import com.asyncapi.kotlinasyncapi.annotation.AsyncApiComponent
4+
import com.asyncapi.kotlinasyncapi.annotation.channel.Channel
5+
import com.asyncapi.kotlinasyncapi.annotation.channel.Publish
6+
import com.asyncapi.kotlinasyncapi.annotation.channel.Subscribe
7+
import com.asyncapi.kotlinasyncapi.model.component.Components
8+
import kotlin.reflect.KClass
9+
import kotlin.reflect.full.findAnnotation
10+
import kotlin.reflect.full.functions
11+
import kotlin.reflect.full.hasAnnotation
12+
13+
class AsyncApiComponentProcessor : AnnotationProcessor<AsyncApiComponent, KClass<*>> {
14+
override fun process(annotation: AsyncApiComponent, context: KClass<*>): Components {
15+
return Components().apply {
16+
channels {
17+
context.functions.filter { it.hasAnnotation<Channel>() }.forEach { currentFunction ->
18+
var currentAnnotation = currentFunction.findAnnotation<Channel>()!!
19+
currentAnnotation.toChannel()
20+
.apply {
21+
subscribe = subscribe ?: currentFunction.findAnnotation<Subscribe>()?.toOperation()
22+
publish = publish ?: currentFunction.findAnnotation<Publish>()?.toOperation()
23+
}
24+
.also {
25+
put(currentAnnotation.value, it)
26+
}
27+
}
28+
}
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.asyncapi.kotlinasyncapi.context.annotation.processor
2+
3+
import org.junit.jupiter.api.Test
4+
import com.asyncapi.kotlinasyncapi.annotation.AsyncApiComponent
5+
import com.asyncapi.kotlinasyncapi.annotation.channel.Channel
6+
import com.asyncapi.kotlinasyncapi.annotation.channel.Message
7+
import com.asyncapi.kotlinasyncapi.annotation.channel.Parameter
8+
import com.asyncapi.kotlinasyncapi.annotation.channel.SecurityRequirement
9+
import com.asyncapi.kotlinasyncapi.annotation.channel.Subscribe
10+
import com.asyncapi.kotlinasyncapi.context.TestUtils.assertJsonEquals
11+
import com.asyncapi.kotlinasyncapi.context.TestUtils.json
12+
import kotlin.reflect.full.findAnnotation
13+
14+
internal class AsyncApiComponentProcessorTest {
15+
16+
private val processor = AsyncApiComponentProcessor()
17+
18+
@Test
19+
fun `should process async api component annotation on class`() {
20+
val payload = TestChannelFunction::class
21+
val annotation = payload.findAnnotation<AsyncApiComponent>()!!
22+
23+
val expected = json("annotation/async_api_component.json")
24+
val actual = json(processor.process(annotation, payload))
25+
26+
assertJsonEquals(expected, actual)
27+
}
28+
29+
30+
@AsyncApiComponent
31+
class TestChannelFunction {
32+
@Channel(
33+
value = "some/{parameter}/channel",
34+
description = "testDescription",
35+
servers = ["dev"],
36+
parameters = [
37+
Parameter(
38+
value = "parameter",
39+
description = "testDescription"
40+
)
41+
]
42+
)
43+
@Subscribe(
44+
operationId = "testOperationId",
45+
security = [
46+
SecurityRequirement(
47+
key = "petstore_auth",
48+
values = ["write:pets", "read:pets"]
49+
)
50+
],
51+
message = Message(TestSubscribeMessage::class)
52+
)
53+
fun testSubscribe() {}
54+
}
55+
56+
@Message
57+
data class TestSubscribeMessage(
58+
val id: Int = 0,
59+
val name: String,
60+
val isTest: Boolean
61+
)
62+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"channels" : {
3+
"some/{parameter}/channel" : {
4+
"description" : "testDescription",
5+
"servers" : [ "dev" ],
6+
"subscribe" : {
7+
"operationId" : "testOperationId",
8+
"security" : [ {
9+
"petstore_auth" : [ "write:pets", "read:pets" ]
10+
} ],
11+
"message" : {
12+
"$ref" : "#/components/messages/TestSubscribeMessage"
13+
}
14+
},
15+
"parameters" : {
16+
"parameter" : {
17+
"description" : "testDescription"
18+
}
19+
}
20+
}
21+
}
22+
}

kotlin-asyncapi-ktor/src/main/kotlin/com/asyncapi/kotlinasyncapi/ktor/AsyncApiModule.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.asyncapi.kotlinasyncapi.context.PackageInfoProvider
1414
import com.asyncapi.kotlinasyncapi.context.ResourceProvider
1515
import com.asyncapi.kotlinasyncapi.context.annotation.AnnotationProvider
1616
import com.asyncapi.kotlinasyncapi.context.annotation.DefaultAnnotationScanner
17+
import com.asyncapi.kotlinasyncapi.context.annotation.processor.AsyncApiComponentProcessor
1718
import com.asyncapi.kotlinasyncapi.context.annotation.processor.ChannelProcessor
1819
import com.asyncapi.kotlinasyncapi.context.annotation.processor.MessageProcessor
1920
import com.asyncapi.kotlinasyncapi.context.annotation.processor.SchemaProcessor
@@ -52,6 +53,8 @@ class AsyncApiModule(
5253

5354
private val channelProcessor = ChannelProcessor()
5455

56+
private val asyncApiComponentProcessor = AsyncApiComponentProcessor()
57+
5558
private val annotationScanner = DefaultAnnotationScanner()
5659

5760
private val annotationProvider = with(configuration) {
@@ -62,6 +65,7 @@ class AsyncApiModule(
6265
messageProcessor = messageProcessor,
6366
schemaProcessor = schemaProcessor,
6467
channelProcessor = channelProcessor,
68+
asyncApiComponentProcessor = asyncApiComponentProcessor
6569
)
6670
}
6771

kotlin-asyncapi-spring-web/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<dependency>
3333
<groupId>org.springframework.boot</groupId>
3434
<artifactId>spring-boot-autoconfigure</artifactId>
35-
<version>[2.6.4,2.7.17], [3.2.0,)</version>
35+
<version>[2.6.4,2.7.17], [3.2.0,3.3.5]</version>
3636
</dependency>
3737
<dependency>
3838
<groupId>org.jetbrains.kotlin</groupId>

kotlin-asyncapi-spring-web/src/main/kotlin/com/asyncapi/kotlinasyncapi/springweb/AsyncApiAutoConfiguration.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.asyncapi.kotlinasyncapi.springweb
22

3+
import com.asyncapi.kotlinasyncapi.annotation.AsyncApiComponent
34
import kotlin.reflect.KClass
45
import kotlin.script.experimental.host.toScriptSource
56
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
@@ -13,6 +14,7 @@ import com.asyncapi.kotlinasyncapi.context.annotation.AnnotationProvider
1314
import com.asyncapi.kotlinasyncapi.context.annotation.AnnotationScanner
1415
import com.asyncapi.kotlinasyncapi.context.annotation.DefaultAnnotationScanner
1516
import com.asyncapi.kotlinasyncapi.context.annotation.processor.AnnotationProcessor
17+
import com.asyncapi.kotlinasyncapi.context.annotation.processor.AsyncApiComponentProcessor
1618
import com.asyncapi.kotlinasyncapi.context.annotation.processor.ChannelProcessor
1719
import com.asyncapi.kotlinasyncapi.context.annotation.processor.MessageProcessor
1820
import com.asyncapi.kotlinasyncapi.context.annotation.processor.SchemaProcessor
@@ -102,6 +104,10 @@ internal open class AsyncApiAnnotationAutoConfiguration {
102104
open fun channelProcessor() =
103105
ChannelProcessor()
104106

107+
@Bean
108+
open fun asyncApiComponentProcessor() =
109+
AsyncApiComponentProcessor()
110+
105111
@Bean
106112
open fun annotationScanner() =
107113
DefaultAnnotationScanner()
@@ -112,14 +118,16 @@ internal open class AsyncApiAnnotationAutoConfiguration {
112118
scanner: AnnotationScanner,
113119
messageProcessor: AnnotationProcessor<Message, KClass<*>>,
114120
schemaProcessor: AnnotationProcessor<Schema, KClass<*>>,
115-
channelProcessor: AnnotationProcessor<Channel, KClass<*>>
121+
channelClassProcessor: AnnotationProcessor<Channel, KClass<*>>,
122+
asyncApiComponentProcessor: AnnotationProcessor<AsyncApiComponent, KClass<*>>
116123
) = packageFromContext(context)?.let {
117124
AnnotationProvider(
118125
applicationPackage = it,
119126
scanner = scanner,
120127
messageProcessor = messageProcessor,
121128
schemaProcessor = schemaProcessor,
122-
channelProcessor = channelProcessor,
129+
channelProcessor = channelClassProcessor,
130+
asyncApiComponentProcessor = asyncApiComponentProcessor,
123131
)
124132
}
125133

0 commit comments

Comments
 (0)