Skip to content

Commit

Permalink
Merge branch 'cbaz/new-build' into azizabah/add-channel
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Bazeley committed Dec 10, 2024
2 parents cf25fe0 + 7bb9a5b commit c45fba1
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import org.openfolder.kotlinasyncapi.model.server.ReferencableServerVariablesMap
import org.openfolder.kotlinasyncapi.model.server.ReferencableServersMap
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions

class AnnotationProvider(
private val applicationPackage: Package? = null,
private val classLoader: ClassLoader? = null,
private val scanner: AnnotationScanner,
private val messageProcessor: AnnotationProcessor<Message, KClass<*>>,
private val schemaProcessor: AnnotationProcessor<Schema, KClass<*>>,
private val channelProcessor: AnnotationProcessor<Channel, KClass<*>>
private val channelProcessor: AnnotationProcessor<Channel, Any>
) : AsyncApiContextProvider {

private val componentToChannelMapping = mutableMapOf<String, String>()
Expand Down Expand Up @@ -67,19 +68,31 @@ class AnnotationProvider(

annotatedClasses
.flatMap { clazz ->
listOfNotNull(
val classAnnotations = listOfNotNull(
clazz.findAnnotation<Message>()?.let { clazz to it },
clazz.findAnnotation<Schema>()?.let { clazz to it },
clazz.findAnnotation<Channel>()?.let { clazz to it }
)

val functionAnnotations = clazz.functions.flatMap { function ->
listOfNotNull(
function.findAnnotation<Message>()?.let { function to it },
function.findAnnotation<Schema>()?.let { function to it },
function.findAnnotation<Channel>()?.let { function to it }
)
}

classAnnotations + functionAnnotations
}
.mapNotNull { (clazz, annotation) ->
.mapNotNull { (element, annotation) ->
when (annotation) {
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
is Message -> if (element is KClass<*>) messageProcessor.process(annotation, element) else null
is Schema -> if (element is KClass<*>) schemaProcessor.process(annotation, element) else null
is Channel -> channelProcessor.process(annotation, element).also {
if (element is KClass<*>) {
componentToChannelMapping[element.java.simpleName] =
annotation.value.takeIf { it.isNotEmpty() } ?: element.java.simpleName
}
}
else -> null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,38 @@ import org.openfolder.kotlinasyncapi.annotation.channel.Publish
import org.openfolder.kotlinasyncapi.annotation.channel.Subscribe
import org.openfolder.kotlinasyncapi.model.component.Components
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions
import kotlin.reflect.full.hasAnnotation

class ChannelProcessor : AnnotationProcessor<Channel, KClass<*>> {
override fun process(annotation: Channel, context: KClass<*>): Components {
class ChannelProcessor : AnnotationProcessor<Channel, Any> {
override fun process(annotation: Channel, context: Any): Components {
return Components().apply {
channels {
annotation.toChannel()
.apply {
subscribe = subscribe ?: context.findSubscribeOperation()?.toOperation()
publish = publish ?: context.findPublishOperation()?.toOperation()
when (context) {
is KClass<*> -> {
annotation.toChannel()
.apply {
subscribe = subscribe ?: context.findSubscribeOperation()?.toOperation()
publish = publish ?: context.findPublishOperation()?.toOperation()
}
.also {
put(context.java.simpleName, it)
}
}
.also {
put(context.java.simpleName, it)
is KFunction<*> -> {
annotation.toChannel()
.apply {
subscribe = subscribe ?: context.findAnnotation<Subscribe>()?.toOperation()
publish = publish ?: context.findAnnotation<Publish>()?.toOperation()
}
.also {
put(context.name, it)
}
}
else -> throw IllegalArgumentException("Unsupported context type: ${context::class}")
}
}
}
}
Expand All @@ -30,4 +46,4 @@ class ChannelProcessor : AnnotationProcessor<Channel, KClass<*>> {

private fun KClass<*>.findPublishOperation(): Publish? =
functions.firstOrNull { it.hasAnnotation<Publish>() }?.findAnnotation()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import org.openfolder.kotlinasyncapi.annotation.channel.Subscribe
import org.openfolder.kotlinasyncapi.context.TestUtils.assertJsonEquals
import org.openfolder.kotlinasyncapi.context.TestUtils.json
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions

internal class ChannelProcessorTest {

private val processor = ChannelProcessor()

@Test
fun `should process channel annotation`() {
fun `should process channel annotation on class`() {
val payload = TestChannel::class
val annotation = payload.findAnnotation<Channel>()!!

Expand Down Expand Up @@ -65,6 +66,43 @@ internal class ChannelProcessorTest {
fun testPublish() {}
}

@Test
fun `should process channel annotation on function`() {
val payload = TestChannelFunction::class
val annotation = payload.functions.flatMap { it.annotations }.filterIsInstance<Channel>().firstOrNull()!!

val expected = json("annotation/channel_component_function.json")
val actual = json(processor.process(annotation, payload))

assertJsonEquals(expected, actual)
}


class TestChannelFunction {
@Channel(
value = "some/{parameter}/channel",
description = "testDescription",
servers = ["dev"],
parameters = [
Parameter(
value = "parameter",
description = "testDescription"
)
]
)
@Subscribe(
operationId = "testOperationId",
security = [
SecurityRequirement(
key = "petstore_auth",
values = ["write:pets", "read:pets"]
)
],
message = Message(TestSubscribeMessage::class)
)
fun testSubscribe() {}
}

@Message
data class TestSubscribeMessage(
val id: Int = 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"channels" : {
"TestChannelFunction" : {
"description" : "testDescription",
"servers" : [ "dev" ],
"subscribe" : {
"operationId" : "testOperationId",
"security" : [ {
"petstore_auth" : [ "write:pets", "read:pets" ]
} ],
"message" : {
"$ref" : "#/components/messages/TestSubscribeMessage"
}
},
"parameters" : {
"parameter" : {
"description" : "testDescription"
}
}
}
}
}
2 changes: 1 addition & 1 deletion kotlin-asyncapi-spring-web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>[2.6.4,2.7.17], [3.2.0,)</version>
<version>[2.6.4,2.7.17], [3.2.0,3.3.5]</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import org.openfolder.kotlinasyncapi.context.annotation.AnnotationProvider
import org.openfolder.kotlinasyncapi.context.annotation.AnnotationScanner
import org.openfolder.kotlinasyncapi.context.annotation.DefaultAnnotationScanner
import org.openfolder.kotlinasyncapi.context.annotation.processor.AnnotationProcessor
import org.openfolder.kotlinasyncapi.context.annotation.processor.ChannelProcessor
import org.openfolder.kotlinasyncapi.context.annotation.processor.MessageProcessor
import org.openfolder.kotlinasyncapi.context.annotation.processor.SchemaProcessor
import org.openfolder.kotlinasyncapi.context.annotation.processor.ChannelProcessor
import org.openfolder.kotlinasyncapi.context.service.AsyncApiExtension
import org.openfolder.kotlinasyncapi.context.service.AsyncApiSerializer
import org.openfolder.kotlinasyncapi.context.service.AsyncApiService
Expand Down Expand Up @@ -112,7 +112,7 @@ internal open class AsyncApiAnnotationAutoConfiguration {
scanner: AnnotationScanner,
messageProcessor: AnnotationProcessor<Message, KClass<*>>,
schemaProcessor: AnnotationProcessor<Schema, KClass<*>>,
channelProcessor: AnnotationProcessor<Channel, KClass<*>>
channelProcessor: AnnotationProcessor<Channel, Any>
) = packageFromContext(context)?.let {
AnnotationProvider(
applicationPackage = it,
Expand Down

0 comments on commit c45fba1

Please sign in to comment.